| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file LICENSE.rst or https://cmake.org/licensing for details. */ |
| |
| #include "cmPkgConfigResolver.h" |
| |
| #include <algorithm> |
| #include <cstring> |
| #include <iterator> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <cm/optional> |
| |
| #include "cmsys/String.h" |
| |
| #include "cmOutputConverter.h" |
| #include "cmPkgConfigParser.h" |
| #include "cmStringAlgorithms.h" |
| |
| namespace { |
| |
| bool HasPrefix(std::string const& value, char const* prefix) |
| { |
| std::size_t const len = std::strlen(prefix); |
| return value.size() >= len && value.compare(0, len, prefix) == 0; |
| } |
| |
| void AppendFlag(std::string& flagline, std::string const& flag) |
| { |
| if (!flagline.empty()) { |
| flagline += ' '; |
| } |
| flagline += flag; |
| } |
| |
| bool IsSystemPath(std::string const& path, |
| std::vector<std::string> const& systemPaths) |
| { |
| return std::find(systemPaths.begin(), systemPaths.end(), path) != |
| systemPaths.end(); |
| } |
| |
| std::vector<std::string> DefaultSystemIncludePaths() |
| { |
| return std::vector<std::string>{ "/usr/include" }; |
| } |
| |
| std::vector<std::string> DefaultSystemLibraryPaths() |
| { |
| return std::vector<std::string>{ "/usr/lib" }; |
| } |
| |
| std::vector<std::string> const& SystemCflags(cmPkgConfigEnv const& env, |
| std::vector<std::string>& def) |
| { |
| if (env.SysCflags) { |
| return *env.SysCflags; |
| } |
| def = DefaultSystemIncludePaths(); |
| return def; |
| } |
| |
| std::vector<std::string> const& SystemLibs(cmPkgConfigEnv const& env, |
| std::vector<std::string>& def) |
| { |
| if (env.SysLibs) { |
| return *env.SysLibs; |
| } |
| def = DefaultSystemLibraryPaths(); |
| return def; |
| } |
| |
| } // namespace |
| |
| std::string cmPkgConfigResolver::VersionReqString( |
| cmPkgConfigDependencySpec const& spec) |
| { |
| switch (spec.Operation) { |
| case cmPkgConfigDependencyOperation::None: |
| return ""; |
| case cmPkgConfigDependencyOperation::LessThan: |
| return cmStrCat('<', spec.Version); |
| case cmPkgConfigDependencyOperation::LessThanEqual: |
| return cmStrCat("<=", spec.Version); |
| case cmPkgConfigDependencyOperation::Equal: |
| return cmStrCat('=', spec.Version); |
| case cmPkgConfigDependencyOperation::NotEqual: |
| return cmStrCat("!=", spec.Version); |
| case cmPkgConfigDependencyOperation::GreaterThanEqual: |
| return cmStrCat(">=", spec.Version); |
| case cmPkgConfigDependencyOperation::GreaterThan: |
| return cmStrCat('>', spec.Version); |
| } |
| return ""; |
| } |
| |
| cmPkgConfigResolver::cmPkgConfigResolver(cmPkgConfigParser parser, |
| cmPkgConfigEnv const& env, |
| std::string const& pcFileDir) |
| : Parser{ std::move(parser) } |
| , Env{ &env } |
| { |
| this->Parser.ParseComplete(); |
| this->ConfigureParser(pcFileDir); |
| } |
| |
| cm::optional<cmPkgConfigResolver> cmPkgConfigResolver::Resolve( |
| cmPkgConfigParser parser, cmPkgConfigEnv const& env, Strictness strictness, |
| std::string const& pcFileDir) |
| { |
| cmPkgConfigResolver resolver{ std::move(parser), env, pcFileDir }; |
| if (strictness != Strictness::BestEffort && !resolver.HasRequiredFields()) { |
| return {}; |
| } |
| if (strictness == Strictness::Strict && resolver.HasStrictConflicts()) { |
| return {}; |
| } |
| return resolver; |
| } |
| |
| cm::optional<cmPkgConfigResolver> cmPkgConfigResolver::ResolveStrict( |
| cmPkgConfigParser parser, cmPkgConfigEnv const& env, |
| std::string const& pcFileDir) |
| { |
| return Resolve(std::move(parser), env, Strictness::Strict, pcFileDir); |
| } |
| |
| cm::optional<cmPkgConfigResolver> cmPkgConfigResolver::ResolvePermissive( |
| cmPkgConfigParser parser, cmPkgConfigEnv const& env, |
| std::string const& pcFileDir) |
| { |
| return Resolve(std::move(parser), env, Strictness::Permissive, pcFileDir); |
| } |
| |
| cmPkgConfigResolver cmPkgConfigResolver::ResolveBestEffort( |
| cmPkgConfigParser parser, cmPkgConfigEnv const& env, |
| std::string const& pcFileDir) |
| { |
| return cmPkgConfigResolver{ std::move(parser), env, pcFileDir }; |
| } |
| |
| void cmPkgConfigResolver::ConfigureParser(std::string const& pcFileDir) |
| { |
| if (this->Env->SysrootDir) { |
| this->Parser.SetVariable( |
| "pc_sysrootdir", |
| cmOutputConverter::EscapeForShell(*this->Env->SysrootDir, |
| cmOutputConverter::Shell_Flag_IsUnix)); |
| } else { |
| this->Parser.SetVariable("pc_sysrootdir", "/"); |
| } |
| |
| if (this->Env->TopBuildDir) { |
| this->Parser.SetVariable( |
| "pc_top_builddir", |
| cmOutputConverter::EscapeForShell(*this->Env->TopBuildDir, |
| cmOutputConverter::Shell_Flag_IsUnix)); |
| } |
| |
| this->Parser.SetVariable("pcfiledir", |
| cmOutputConverter::EscapeForShell( |
| pcFileDir, cmOutputConverter::Shell_Flag_IsUnix)); |
| } |
| |
| bool cmPkgConfigResolver::HasRequiredFields() const |
| { |
| return this->Parser.HasProperty("Name") && |
| this->Parser.HasProperty("Description") && |
| this->Parser.HasProperty("Version"); |
| } |
| |
| bool cmPkgConfigResolver::HasStrictConflicts() const |
| { |
| return (this->Parser.HasProperty("Cflags") && |
| this->Parser.HasProperty("CFlags")) || |
| (this->Parser.HasProperty("Cflags.private") && |
| this->Parser.HasProperty("CFlags.private")); |
| } |
| |
| std::string cmPkgConfigResolver::Literal(std::string const& key) const |
| { |
| return this->Parser.GetLiteral(key); |
| } |
| |
| std::string cmPkgConfigResolver::Name() const |
| { |
| return this->Literal("Name"); |
| } |
| |
| std::string cmPkgConfigResolver::Description() const |
| { |
| return this->Literal("Description"); |
| } |
| |
| std::string cmPkgConfigResolver::Version() const |
| { |
| return this->Literal("Version"); |
| } |
| |
| std::vector<std::string> cmPkgConfigResolver::Fragments( |
| std::string const& key) const |
| { |
| return this->Parser.GetFragments(key); |
| } |
| |
| std::vector<cmPkgConfigDependencySpec> cmPkgConfigResolver::Dependencies( |
| std::string const& key) const |
| { |
| return this->Parser.GetDependencies(key); |
| } |
| |
| std::vector<cmPkgConfigDependencySpec> cmPkgConfigResolver::Conflicts() const |
| { |
| return this->Dependencies("Conflicts"); |
| } |
| |
| std::vector<cmPkgConfigDependencySpec> cmPkgConfigResolver::Provides() const |
| { |
| return this->Dependencies("Provides"); |
| } |
| |
| std::vector<cmPkgConfigDependencySpec> cmPkgConfigResolver::Requires( |
| bool priv) const |
| { |
| return this->Dependencies(priv ? "Requires.private" : "Requires"); |
| } |
| |
| cmPkgConfigCflagsResult cmPkgConfigResolver::Cflags(bool priv) const |
| { |
| auto flags = this->Fragments(priv ? "Cflags.private" : "Cflags"); |
| auto alt = this->Fragments(priv ? "CFlags.private" : "CFlags"); |
| flags.insert(flags.end(), alt.begin(), alt.end()); |
| return MangleCflags(flags, *this->Env); |
| } |
| |
| cmPkgConfigLibsResult cmPkgConfigResolver::Libs(bool priv) const |
| { |
| return MangleLibs(this->Fragments(priv ? "Libs.private" : "Libs"), |
| *this->Env); |
| } |
| |
| cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags( |
| std::vector<std::string> const& flags, cmPkgConfigEnv const& env) |
| { |
| cmPkgConfigCflagsResult result; |
| std::vector<std::string> defaultSystemPaths; |
| auto const& systemPaths = SystemCflags(env, defaultSystemPaths); |
| |
| for (auto const& flag : flags) { |
| std::string mangled = flag; |
| if (HasPrefix(flag, "-I") && env.SysrootDir) { |
| mangled = Reroot(flag, "-I", *env.SysrootDir); |
| } |
| |
| if (HasPrefix(mangled, "-I")) { |
| if (!env.AllowSysCflags && |
| IsSystemPath(mangled.substr(2), systemPaths)) { |
| continue; |
| } |
| AppendFlag(result.Flagline, mangled); |
| result.Includes.push_back(mangled); |
| } else { |
| AppendFlag(result.Flagline, mangled); |
| result.CompileOptions.push_back(mangled); |
| } |
| } |
| |
| return result; |
| } |
| |
| cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs( |
| std::vector<std::string> const& flags, cmPkgConfigEnv const& env) |
| { |
| cmPkgConfigLibsResult result; |
| std::vector<std::string> defaultSystemPaths; |
| auto const& systemPaths = SystemLibs(env, defaultSystemPaths); |
| |
| for (auto const& flag : flags) { |
| std::string mangled = flag; |
| if (HasPrefix(flag, "-L") && env.SysrootDir) { |
| mangled = Reroot(flag, "-L", *env.SysrootDir); |
| } |
| |
| if (HasPrefix(mangled, "-L")) { |
| if (!env.AllowSysLibs && IsSystemPath(mangled.substr(2), systemPaths)) { |
| continue; |
| } |
| AppendFlag(result.Flagline, mangled); |
| result.LibDirs.push_back(mangled); |
| } else if (HasPrefix(mangled, "-l")) { |
| AppendFlag(result.Flagline, mangled); |
| result.LibNames.push_back(mangled); |
| } else { |
| AppendFlag(result.Flagline, mangled); |
| result.LinkOptions.push_back(mangled); |
| } |
| } |
| |
| return result; |
| } |
| |
| std::string cmPkgConfigResolver::Reroot(std::string const& flag, |
| char const* prefix, |
| std::string const& sysroot) |
| { |
| std::string result = prefix; |
| result += sysroot; |
| result += flag.substr(std::strlen(prefix)); |
| return result; |
| } |
| |
| void cmPkgConfigResolver::ReplaceSep(std::string& list) |
| { |
| #ifndef _WIN32 |
| std::replace(list.begin(), list.end(), ':', ';'); |
| #else |
| static_cast<void>(list); |
| #endif |
| } |
| |
| bool cmPkgConfigResolver::CheckVersion( |
| cmPkgConfigDependencySpec const& desired, std::string const& provided) |
| { |
| if (desired.Operation == cmPkgConfigDependencyOperation::None) { |
| return true; |
| } |
| |
| auto check_with_op = [&](int comp) -> bool { |
| switch (desired.Operation) { |
| case cmPkgConfigDependencyOperation::Equal: |
| return comp == 0; |
| case cmPkgConfigDependencyOperation::NotEqual: |
| return comp != 0; |
| case cmPkgConfigDependencyOperation::GreaterThan: |
| return comp < 0; |
| case cmPkgConfigDependencyOperation::GreaterThanEqual: |
| return comp <= 0; |
| case cmPkgConfigDependencyOperation::LessThan: |
| return comp > 0; |
| case cmPkgConfigDependencyOperation::LessThanEqual: |
| return comp >= 0; |
| default: |
| return true; |
| } |
| }; |
| |
| if (desired.Version == provided) { |
| return check_with_op(0); |
| } |
| |
| auto a_cur = desired.Version.begin(); |
| auto a_end = desired.Version.end(); |
| |
| auto b_cur = provided.begin(); |
| auto b_end = provided.end(); |
| |
| while (a_cur != a_end && b_cur != b_end) { |
| while (a_cur != a_end && !cmsysString_isalnum(*a_cur) && *a_cur != '~') { |
| ++a_cur; |
| } |
| |
| while (b_cur != b_end && !cmsysString_isalnum(*b_cur) && *b_cur != '~') { |
| ++b_cur; |
| } |
| |
| if (a_cur == a_end || b_cur == b_end) { |
| break; |
| } |
| |
| if (*a_cur == '~' || *b_cur == '~') { |
| if (*a_cur != '~') { |
| return check_with_op(1); |
| } |
| |
| if (*b_cur != '~') { |
| return check_with_op(-1); |
| } |
| |
| ++a_cur; |
| ++b_cur; |
| continue; |
| } |
| |
| auto a_seg = a_cur; |
| auto b_seg = b_cur; |
| bool is_num; |
| |
| if (cmsysString_isdigit(*a_cur)) { |
| is_num = true; |
| while (a_cur != a_end && cmsysString_isdigit(*a_cur)) { |
| ++a_cur; |
| } |
| |
| while (b_cur != b_end && cmsysString_isdigit(*b_cur)) { |
| ++b_cur; |
| } |
| |
| } else { |
| is_num = false; |
| while (a_cur != a_end && cmsysString_isalpha(*a_cur)) { |
| ++a_cur; |
| } |
| |
| while (b_cur != b_end && cmsysString_isalpha(*b_cur)) { |
| ++b_cur; |
| } |
| } |
| |
| auto a_len = std::distance(a_seg, a_cur); |
| auto b_len = std::distance(b_seg, b_cur); |
| |
| if (!b_len) { |
| return check_with_op(is_num ? 1 : -1); |
| } |
| |
| if (is_num) { |
| while (a_seg != a_cur && *a_seg == '0') { |
| ++a_seg; |
| } |
| |
| while (b_seg != b_cur && *b_seg == '0') { |
| ++b_seg; |
| } |
| |
| a_len = std::distance(a_seg, a_cur); |
| b_len = std::distance(b_seg, b_cur); |
| |
| if (a_len != b_len) { |
| return check_with_op(a_len > b_len ? 1 : -1); |
| } |
| |
| auto cmp = std::memcmp(&*a_seg, &*b_seg, a_len); |
| if (cmp) { |
| return check_with_op(cmp); |
| } |
| } else { |
| auto cmp = std::memcmp(&*a_seg, &*b_seg, std::min(a_len, b_len)); |
| if (cmp) { |
| return check_with_op(cmp); |
| } |
| |
| if (a_len != b_len) { |
| return check_with_op(a_len > b_len ? 1 : -1); |
| } |
| } |
| } |
| |
| if (a_cur == a_end) { |
| if (b_cur == b_end) { |
| return check_with_op(0); |
| } |
| return check_with_op(-1); |
| } |
| |
| return check_with_op(1); |
| } |