/* 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 #include #include #include #include #include #include #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 const& systemPaths) { return std::find(systemPaths.begin(), systemPaths.end(), path) != systemPaths.end(); } std::vector DefaultSystemIncludePaths() { return std::vector{ "/usr/include" }; } std::vector DefaultSystemLibraryPaths() { return std::vector{ "/usr/lib" }; } std::vector const& SystemCflags(cmPkgConfigEnv const& env, std::vector& def) { if (env.SysCflags) { return *env.SysCflags; } def = DefaultSystemIncludePaths(); return def; } std::vector const& SystemLibs(cmPkgConfigEnv const& env, std::vector& 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::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::ResolveStrict( cmPkgConfigParser parser, cmPkgConfigEnv const& env, std::string const& pcFileDir) { return Resolve(std::move(parser), env, Strictness::Strict, pcFileDir); } cm::optional 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 cmPkgConfigResolver::Fragments( std::string const& key) const { return this->Parser.GetFragments(key); } std::vector cmPkgConfigResolver::Dependencies( std::string const& key) const { return this->Parser.GetDependencies(key); } std::vector cmPkgConfigResolver::Conflicts() const { return this->Dependencies("Conflicts"); } std::vector cmPkgConfigResolver::Provides() const { return this->Dependencies("Provides"); } std::vector 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 const& flags, cmPkgConfigEnv const& env) { cmPkgConfigCflagsResult result; std::vector 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 const& flags, cmPkgConfigEnv const& env) { cmPkgConfigLibsResult result; std::vector 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(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); }