| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file LICENSE.rst or https://cmake.org/licensing for details. */
- #include "cmCMakePkgConfigCommand.h"
- #include <cstdio>
- #include <memory>
- #include <string>
- #include <unordered_map>
- #include <utility>
- #include <vector>
- #include <cm/filesystem>
- #include <cm/optional>
- #include <cm/string_view>
- #include <cmext/string_view>
- #include "cmsys/FStream.hxx"
- #include "cmArgumentParser.h"
- #include "cmArgumentParserTypes.h"
- #include "cmExecutionStatus.h"
- #include "cmList.h"
- #include "cmMakefile.h"
- #include "cmMessageType.h"
- #include "cmPkgConfigParser.h"
- #include "cmPkgConfigResolver.h"
- #include "cmStateTypes.h"
- #include "cmStringAlgorithms.h"
- #include "cmSubcommandTable.h"
- #include "cmSystemTools.h"
- #include "cmTarget.h"
- #include "cmValue.h"
- #include <cmllpkgc/llpkgc.h>
- // IWYU wants this
- namespace {
- struct ExtractArguments;
- struct PopulateArguments;
- struct ImportArguments;
- }
- namespace {
- cm::optional<std::string> GetPkgConfigBin(cmMakefile& mf)
- {
- cm::optional<std::string> result;
- auto pkgcfg = mf.GetDefinition("CMAKE_PKG_CONFIG_BIN");
- if (pkgcfg.IsNOTFOUND()) {
- return result;
- }
- if (pkgcfg) {
- result = *pkgcfg;
- return result;
- }
- std::string path = cmSystemTools::FindProgram("pkgconf");
- if (path.empty()) {
- path = cmSystemTools::FindProgram("pkg-config");
- if (path.empty()) {
- mf.AddCacheDefinition("CMAKE_PKG_CONFIG_BIN", "pkg-config-NOTFOUND",
- "Location of pkg-config or pkgconf binary",
- cmStateEnums::FILEPATH);
- return result;
- }
- }
- mf.AddCacheDefinition("CMAKE_PKG_CONFIG_BIN", path,
- "Location of pkg-config or pkgconf binary",
- cmStateEnums::FILEPATH);
- result = std::move(path);
- return result;
- }
- std::vector<std::string> GetLocations(cmMakefile& mf, char const* cachevar,
- char const* envvar, char const* desc,
- char const* pcvar, bool need_pkgconf,
- std::vector<std::string> default_locs)
- {
- auto def = mf.GetDefinition(cachevar);
- if (def) {
- return cmList(def);
- }
- std::string paths;
- if (cmSystemTools::GetEnv(envvar, paths)) {
- cmPkgConfigResolver::ReplaceSep(paths);
- mf.AddCacheDefinition(cachevar, paths, desc, cmStateEnums::STRING);
- return cmList(paths);
- }
- auto pkgcfg = GetPkgConfigBin(mf);
- if (!pkgcfg || (need_pkgconf && (pkgcfg->find("pkgconf") == pkgcfg->npos))) {
- mf.AddCacheDefinition(cachevar, cmList::to_string(default_locs), desc,
- cmStateEnums::STRING);
- return default_locs;
- }
- std::string out;
- cmSystemTools::RunSingleCommand({ *pkgcfg, pcvar, "pkg-config" }, &out,
- nullptr, nullptr, nullptr,
- cmSystemTools::OUTPUT_NONE);
- cmPkgConfigResolver::ReplaceSep(out);
- out = cmTrimWhitespace(out);
- mf.AddCacheDefinition(cachevar, out, desc, cmStateEnums::STRING);
- return cmList(out);
- }
- std::vector<std::string> GetPcLibDirs(cmMakefile& mf)
- {
- std::vector<std::string> default_locs = {
- #ifndef _WIN32
- "/usr/lib/pkgconfig", "/usr/share/pkgconfig"
- #endif
- };
- return GetLocations(mf, "CMAKE_PKG_CONFIG_PC_LIB_DIRS", "PKG_CONFIG_LIBDIR",
- "Default search locations for package files",
- "--variable=pc_path", false, std::move(default_locs));
- }
- std::vector<std::string> GetSysLibDirs(cmMakefile& mf)
- {
- std::vector<std::string> default_locs = {
- #ifndef _WIN32
- "/lib", "/usr/lib"
- #endif
- };
- return GetLocations(
- mf, "CMAKE_PKG_CONFIG_SYS_LIB_DIRS", "PKG_CONFIG_SYSTEM_LIBRARY_PATH",
- "System library directories filtered by flag mangling",
- "--variable=pc_system_libdirs", true, std::move(default_locs));
- }
- std::vector<std::string> GetSysCflags(cmMakefile& mf)
- {
- std::vector<std::string> default_locs = {
- #ifndef _WIN32
- "/usr/include"
- #endif
- };
- return GetLocations(
- mf, "CMAKE_PKG_CONFIG_SYS_INCLUDE_DIRS", "PKG_CONFIG_SYSTEM_INCLUDE_PATH",
- "System include directories filtered by flag mangling",
- "--variable=pc_system_includedirs", true, std::move(default_locs));
- }
- std::vector<std::string> GetPkgConfSysLibs(cmMakefile& mf)
- {
- auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS");
- if (def) {
- return cmList(def);
- }
- std::string paths;
- if (!cmSystemTools::GetEnv("LIBRARY_PATH", paths)) {
- return {};
- }
- cmPkgConfigResolver::ReplaceSep(paths);
- mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS", paths,
- "Additional system library directories filtered by "
- "flag mangling in PKGCONF mode",
- cmStateEnums::STRING);
- return cmList(paths);
- }
- std::vector<std::string> GetPkgConfSysCflags(cmMakefile& mf)
- {
- auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PKGCONF_INCLUDES");
- if (def) {
- return cmList(def);
- }
- std::string paths;
- auto get_and_append = [&](char const* var) {
- if (paths.empty()) {
- cmSystemTools::GetEnv(var, paths);
- } else {
- std::string tmp;
- cmSystemTools::GetEnv(var, tmp);
- if (!tmp.empty()) {
- paths += ";" + tmp;
- }
- }
- };
- get_and_append("CPATH");
- get_and_append("C_INCLUDE_PATH");
- get_and_append("CPLUS_INCLUDE_PATH");
- get_and_append("OBJC_INCLUDE_PATH");
- #ifdef _WIN32
- get_and_append("INCLUDE");
- #endif
- cmPkgConfigResolver::ReplaceSep(paths);
- mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PKGCONF_INCLUDES", paths,
- "Additional system include directories filtered by "
- "flag mangling in PKGCONF mode",
- cmStateEnums::STRING);
- return cmList(paths);
- }
- std::vector<std::string> GetPcPath(cmMakefile& mf)
- {
- auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PC_PATH");
- if (def) {
- return cmList(def);
- }
- std::string pcpath;
- if (cmSystemTools::GetEnv("PKG_CONFIG_PATH", pcpath)) {
- auto result = cmSystemTools::SplitString(pcpath, cmPkgConfigResolver::Sep);
- mf.AddCacheDefinition(
- "CMAKE_PKG_CONFIG_PC_PATH", cmList::to_string(result),
- "Additional search locations for package files", cmStateEnums::STRING);
- return result;
- }
- mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PC_PATH", "",
- "Additional search locations for package files",
- cmStateEnums::STRING);
- return {};
- }
- cm::optional<std::string> GetPath(cmMakefile& mf, char const* cachevar,
- char const* envvar, char const* desc)
- {
- cm::optional<std::string> result;
- auto def = mf.GetDefinition(cachevar);
- if (def) {
- result = *def;
- return result;
- }
- std::string path;
- if (cmSystemTools::GetEnv(envvar, path)) {
- mf.AddCacheDefinition(cachevar, path, desc, cmStateEnums::FILEPATH);
- result = std::move(path);
- return result;
- }
- return result;
- }
- cm::optional<std::string> GetSysrootDir(cmMakefile& mf)
- {
- return GetPath(mf, "CMAKE_PKG_CONFIG_SYSROOT_DIR", "PKG_CONFIG_SYSROOT_DIR",
- "System root used for re-rooting package includes and "
- "library directories");
- }
- cm::optional<std::string> GetTopBuildDir(cmMakefile& mf)
- {
- return GetPath(mf, "CMAKE_PKG_CONFIG_TOP_BUILD_DIR",
- "PKG_CONFIG_TOP_BUILD_DIR",
- "Package file top_build_dir variable default value");
- }
- bool GetBool(cmMakefile& mf, char const* cachevar, char const* envvar,
- char const* desc)
- {
- auto def = mf.GetDefinition(cachevar);
- if (def) {
- return def.IsOn();
- }
- if (cmSystemTools::HasEnv(envvar)) {
- mf.AddCacheDefinition(cachevar, "ON", desc, cmStateEnums::BOOL);
- return true;
- }
- return false;
- }
- bool GetDisableUninstalled(cmMakefile& mf)
- {
- return GetBool(mf, "CMAKE_PKG_CONFIG_DISABLE_UNINSTALLED",
- "PKG_CONFIG_DISABLE_UNINSTALLED",
- "Disable search for `-uninstalled` (build tree) packages");
- }
- bool GetAllowSysLibs(cmMakefile& mf)
- {
- return GetBool(mf, "CMAKE_PKG_CONFIG_ALLOW_SYS_LIBS",
- "PKG_CONFIG_ALLOW_SYSTEM_LIBS",
- "Allow system library directories during flag mangling");
- }
- bool GetAllowSysInclude(cmMakefile& mf)
- {
- return GetBool(mf, "CMAKE_PKG_CONFIG_ALLOW_SYS_INCLUDES",
- "PKG_CONFIG_ALLOW_SYSTEM_CFLAGS",
- "Allow system include paths during flag manglging");
- }
- struct CommonArguments : ArgumentParser::ParseResult
- {
- bool Required = false;
- bool Exact = false;
- bool Quiet = false;
- enum StrictnessType
- {
- STRICTNESS_STRICT,
- STRICTNESS_PERMISSIVE,
- STRICTNESS_BEST_EFFORT,
- };
- StrictnessType Strictness = STRICTNESS_PERMISSIVE;
- std::string StrictnessError;
- ArgumentParser::Continue SetStrictness(cm::string_view strictness)
- {
- if (strictness == "STRICT"_s) {
- Strictness = STRICTNESS_STRICT;
- } else if (strictness == "PERMISSIVE"_s) {
- Strictness = STRICTNESS_PERMISSIVE;
- } else if (strictness == "BEST_EFFORT"_s) {
- Strictness = STRICTNESS_BEST_EFFORT;
- } else {
- StrictnessError =
- cmStrCat("Invalid 'STRICTNESS' '", strictness,
- "'; must be one of 'STRICT', 'PERMISSIVE', or 'BEST_EFFORT'");
- }
- return ArgumentParser::Continue::Yes;
- }
- enum EnvModeType
- {
- ENVMODE_FDO,
- ENVMODE_PKGCONF,
- ENVMODE_IGNORE,
- };
- EnvModeType EnvMode = ENVMODE_PKGCONF;
- std::string EnvModeError;
- ArgumentParser::Continue SetEnvMode(cm::string_view envMode)
- {
- if (envMode == "FDO"_s) {
- EnvMode = ENVMODE_FDO;
- } else if (envMode == "PKGCONF"_s) {
- EnvMode = ENVMODE_PKGCONF;
- } else if (envMode == "IGNORE"_s) {
- EnvMode = ENVMODE_IGNORE;
- } else {
- EnvModeError =
- cmStrCat("Invalid 'ENV_MODE' '", envMode,
- "'; must be one of 'FDO', 'PKGCONF', or 'IGNORE'");
- }
- return ArgumentParser::Continue::Yes;
- }
- cm::optional<std::string> Package;
- cm::optional<std::string> Version;
- cm::optional<std::string> SysrootDir;
- cm::optional<std::string> TopBuildDir;
- cm::optional<bool> DisableUninstalled;
- cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> PcPath;
- cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> PcLibdir;
- bool CheckArgs(cmExecutionStatus& status) const
- {
- if (!Package) {
- status.SetError("A package name or absolute path must be specified");
- return false;
- }
- if (!StrictnessError.empty()) {
- status.SetError(StrictnessError);
- return false;
- }
- if (!EnvModeError.empty()) {
- status.SetError(EnvModeError);
- return false;
- }
- return true;
- }
- };
- #define BIND_COMMON(argtype) \
- (cmArgumentParser<argtype>{}) \
- .Bind(1, &argtype::Package) \
- .Bind(2, &argtype::Version) \
- .Bind("REQUIRED"_s, &argtype::Required) \
- .Bind("EXACT"_s, &argtype::Exact) \
- .Bind("QUIET"_s, &argtype::Quiet) \
- .Bind("STRICTNESS"_s, &argtype::SetStrictness) \
- .Bind("ENV_MODE"_s, &argtype::SetEnvMode) \
- .Bind("PC_SYSROOT_DIR"_s, &argtype::SysrootDir) \
- .Bind("TOP_BUILD_DIR"_s, &argtype::TopBuildDir) \
- .Bind("DISABLE_UNINSTALLED"_s, &argtype::DisableUninstalled) \
- .Bind("PC_LIBDIR"_s, &argtype::PcLibdir) \
- .Bind("PC_PATH"_s, &argtype::PcPath)
- void CollectEnv(cmMakefile& mf, cmPkgConfigEnv& env,
- CommonArguments::EnvModeType mode)
- {
- if (mode == CommonArguments::EnvModeType::ENVMODE_IGNORE) {
- return;
- }
- if (!env.Path) {
- env.Path = GetPcPath(mf);
- }
- if (!env.LibDirs) {
- env.LibDirs = GetPcLibDirs(mf);
- }
- if (!env.DisableUninstalled) {
- env.DisableUninstalled = GetDisableUninstalled(mf);
- }
- if (!env.SysrootDir) {
- env.SysrootDir = GetSysrootDir(mf);
- }
- if (!env.TopBuildDir) {
- env.TopBuildDir = GetTopBuildDir(mf);
- }
- env.AllowSysCflags = GetAllowSysInclude(mf);
- env.SysCflags = GetSysCflags(mf);
- env.AllowSysLibs = GetAllowSysLibs(mf);
- env.SysLibs = GetSysLibDirs(mf);
- if (mode == CommonArguments::EnvModeType::ENVMODE_FDO) {
- return;
- }
- *env.SysCflags += GetPkgConfSysCflags(mf);
- *env.SysLibs += GetPkgConfSysLibs(mf);
- }
- struct ImportEnv
- {
- bool required;
- bool quiet;
- bool exact;
- bool err;
- CommonArguments::StrictnessType strictness;
- cmExecutionStatus& status;
- };
- void warn_or_error(std::string const& err, ImportEnv& imEnv)
- {
- if (imEnv.required) {
- imEnv.status.SetError(err);
- cmSystemTools::SetFatalErrorOccurred();
- } else if (!imEnv.quiet) {
- imEnv.status.GetMakefile().IssueMessage(MessageType::WARNING, err);
- }
- imEnv.err = true;
- }
- cm::optional<cmPkgConfigResult> ReadPackage(std::string const& package,
- ImportEnv& imEnv,
- cmPkgConfigEnv& pcEnv)
- {
- cm::optional<cmPkgConfigResult> result;
- cm::filesystem::path path{ package };
- if (path.extension() == ".pc") {
- if (!cmSystemTools::FileExists(path.string())) {
- return result;
- }
- } else {
- if (pcEnv.DisableUninstalled && !*pcEnv.DisableUninstalled) {
- auto uninstalled = path;
- uninstalled.concat("-uninstalled.pc");
- uninstalled =
- cmSystemTools::FindFile(uninstalled.string(), pcEnv.search, true);
- if (uninstalled.empty()) {
- path = cmSystemTools::FindFile(path.concat(".pc").string(),
- pcEnv.search, true);
- if (path.empty()) {
- return result;
- }
- } else {
- path = uninstalled;
- }
- } else {
- path = cmSystemTools::FindFile(path.concat(".pc").string(), pcEnv.search,
- true);
- if (path.empty()) {
- return result;
- }
- }
- }
- auto len = cmSystemTools::FileLength(path.string());
- // Windows requires this weird string -> c_str dance
- cmsys::ifstream ifs(path.string().c_str(), std::ios::binary);
- if (!ifs) {
- warn_or_error(cmStrCat("Could not open file '", path.string(), '\''),
- imEnv);
- return result;
- }
- std::unique_ptr<char[]> buf(new char[len]);
- ifs.read(buf.get(), len);
- // Shouldn't have hit eof on previous read, should hit eof now
- if (ifs.fail() || ifs.eof() || ifs.get() != EOF) {
- warn_or_error(cmStrCat("Error while reading file '", path.string(), '\''),
- imEnv);
- return result;
- }
- using StrictnessType = CommonArguments::StrictnessType;
- cmPkgConfigParser parser;
- auto err = parser.Finish(buf.get(), len);
- if (imEnv.strictness != StrictnessType::STRICTNESS_BEST_EFFORT &&
- err != PCE_OK) {
- warn_or_error(cmStrCat("Parsing failed for file '", path.string(), '\''),
- imEnv);
- return result;
- }
- if (imEnv.strictness == StrictnessType::STRICTNESS_STRICT) {
- result = cmPkgConfigResolver::ResolveStrict(parser.Data(), pcEnv);
- } else if (imEnv.strictness == StrictnessType::STRICTNESS_PERMISSIVE) {
- result = cmPkgConfigResolver::ResolvePermissive(parser.Data(), pcEnv);
- } else {
- result = cmPkgConfigResolver::ResolveBestEffort(parser.Data(), pcEnv);
- }
- if (!result) {
- warn_or_error(
- cmStrCat("Resolution failed for file '", path.string(), '\''), imEnv);
- }
- return result;
- }
- cm::optional<cmPkgConfigResult> ImportPackage(
- std::string const& package, cm::optional<std::string> version,
- ImportEnv& imEnv, cmPkgConfigEnv& pcEnv)
- {
- auto result = ReadPackage(package, imEnv, pcEnv);
- if (!result) {
- if (!imEnv.err) {
- warn_or_error(cmStrCat("Could not find pkg-config: '", package, '\''),
- imEnv);
- }
- return result;
- }
- if (imEnv.exact) {
- std::string ver;
- if (version) {
- ver = cmPkgConfigResolver::ParseVersion(*version).Version;
- }
- if (ver != result->Version()) {
- warn_or_error(
- cmStrCat("Package '", package, "' version '", result->Version(),
- "' does not meet exact version requirement '", ver, '\''),
- imEnv);
- return {};
- }
- } else if (version) {
- auto rv = cmPkgConfigResolver::ParseVersion(*version);
- if (!cmPkgConfigResolver::CheckVersion(rv, result->Version())) {
- warn_or_error(
- cmStrCat("Package '", package, "' version '", result->Version(),
- "' does not meet version requirement '", *version, '\''),
- imEnv);
- return {};
- }
- }
- result->env = &pcEnv;
- return result;
- }
- struct pkgStackEntry
- {
- cmPkgConfigVersionReq ver;
- std::string parent;
- };
- cm::optional<cmPkgConfigResult> ImportPackage(
- std::string const& package, std::vector<pkgStackEntry> const& reqs,
- ImportEnv& imEnv, cmPkgConfigEnv& pcEnv)
- {
- auto result = ReadPackage(package, imEnv, pcEnv);
- if (!result) {
- if (!imEnv.err) {
- std::string req_str = cmStrCat('\'', reqs.begin()->parent, '\'');
- for (auto it = reqs.begin() + 1; it != reqs.end(); ++it) {
- req_str = cmStrCat(req_str, ", '", it->parent, '\'');
- }
- warn_or_error(cmStrCat("Could not find pkg-config: '", package,
- "' required by: ", req_str),
- imEnv);
- }
- return result;
- }
- auto ver = result->Version();
- for (auto const& req : reqs) {
- if (!cmPkgConfigResolver::CheckVersion(req.ver, ver)) {
- warn_or_error(cmStrCat("Package '", package, "' version '", ver,
- "' does not meet version requirement '",
- req.ver.string(), "' of '", req.parent, '\''),
- imEnv);
- return {};
- }
- }
- result->env = &pcEnv;
- return result;
- }
- cm::optional<std::pair<cmPkgConfigEnv, ImportEnv>> HandleCommon(
- CommonArguments& args, cmExecutionStatus& status)
- {
- auto& mf = status.GetMakefile();
- if (!args.CheckArgs(status)) {
- return {};
- }
- cmPkgConfigEnv pcEnv;
- if (args.PcLibdir) {
- pcEnv.LibDirs = std::move(*args.PcLibdir);
- }
- if (args.PcPath) {
- pcEnv.Path = std::move(*args.PcPath);
- }
- pcEnv.DisableUninstalled = args.DisableUninstalled;
- if (args.SysrootDir) {
- pcEnv.SysrootDir = std::move(*args.SysrootDir);
- }
- if (args.TopBuildDir) {
- pcEnv.TopBuildDir = std::move(*args.TopBuildDir);
- }
- CollectEnv(mf, pcEnv, args.EnvMode);
- if (pcEnv.Path) {
- pcEnv.search = *pcEnv.Path;
- if (pcEnv.LibDirs) {
- pcEnv.search += *pcEnv.LibDirs;
- }
- } else if (pcEnv.LibDirs) {
- pcEnv.search = *pcEnv.LibDirs;
- }
- return std::pair<cmPkgConfigEnv, ImportEnv>{
- pcEnv,
- { args.Required, args.Quiet, args.Exact, false, args.Strictness, status }
- };
- }
- struct ExtractArguments : CommonArguments
- {
- cm::optional<bool> AllowSystemIncludes;
- cm::optional<bool> AllowSystemLibs;
- cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
- SystemIncludeDirs;
- cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
- SystemLibraryDirs;
- };
- auto const ExtractParser =
- BIND_COMMON(ExtractArguments)
- .Bind("ALLOW_SYSTEM_INCLUDES"_s, &ExtractArguments::AllowSystemIncludes)
- .Bind("ALLOW_SYSTEM_LIBS"_s, &ExtractArguments::AllowSystemLibs)
- .Bind("SYSTEM_INCLUDE_DIRS"_s, &ExtractArguments::SystemIncludeDirs)
- .Bind("SYSTEM_LIBRARY_DIRS"_s, &ExtractArguments::SystemLibraryDirs);
- bool HandleExtractCommand(std::vector<std::string> const& args,
- cmExecutionStatus& status)
- {
- std::vector<std::string> unparsed;
- auto parsedArgs = ExtractParser.Parse(args, &unparsed);
- auto maybeEnv = HandleCommon(parsedArgs, status);
- if (!maybeEnv) {
- return !parsedArgs.Required;
- }
- auto& pcEnv = maybeEnv->first;
- auto& imEnv = maybeEnv->second;
- auto maybePackage =
- ImportPackage(*parsedArgs.Package, parsedArgs.Version, imEnv, pcEnv);
- if (!maybePackage) {
- return !parsedArgs.Required;
- }
- auto& package = *maybePackage;
- if (parsedArgs.AllowSystemIncludes) {
- pcEnv.AllowSysCflags = *parsedArgs.AllowSystemIncludes;
- }
- if (parsedArgs.AllowSystemLibs) {
- pcEnv.AllowSysLibs = *parsedArgs.AllowSystemLibs;
- }
- if (parsedArgs.SystemIncludeDirs) {
- pcEnv.SysCflags = *parsedArgs.SystemIncludeDirs;
- }
- if (parsedArgs.SystemLibraryDirs) {
- pcEnv.SysLibs = *parsedArgs.SystemLibraryDirs;
- }
- auto& mf = status.GetMakefile();
- mf.AddDefinition("CMAKE_PKG_CONFIG_NAME", package.Name());
- mf.AddDefinition("CMAKE_PKG_CONFIG_DESCRIPTION", package.Description());
- mf.AddDefinition("CMAKE_PKG_CONFIG_VERSION", package.Version());
- auto make_list = [&](char const* def,
- std::vector<cmPkgConfigDependency> const& deps) {
- std::vector<cm::string_view> vec;
- vec.reserve(deps.size());
- for (auto const& dep : deps) {
- vec.emplace_back(dep.Name);
- }
- mf.AddDefinition(def, cmList::to_string(vec));
- };
- make_list("CMAKE_PKG_CONFIG_CONFLICTS", package.Conflicts());
- make_list("CMAKE_PKG_CONFIG_PROVIDES", package.Provides());
- make_list("CMAKE_PKG_CONFIG_REQUIRES", package.Requires());
- make_list("CMAKE_PKG_CONFIG_REQUIRES_PRIVATE", package.Requires(true));
- auto cflags = package.Cflags();
- mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS", cflags.Flagline);
- mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES",
- cmList::to_string(cflags.Includes));
- mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS",
- cmList::to_string(cflags.CompileOptions));
- cflags = package.Cflags(true);
- mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS_PRIVATE", cflags.Flagline);
- mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES_PRIVATE",
- cmList::to_string(cflags.Includes));
- mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS_PRIVATE",
- cmList::to_string(cflags.CompileOptions));
- auto libs = package.Libs();
- mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS", libs.Flagline);
- mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS",
- cmList::to_string(libs.LibDirs));
- mf.AddDefinition("CMAKE_PKG_CONFIG_LIBNAMES",
- cmList::to_string(libs.LibNames));
- mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS",
- cmList::to_string(libs.LinkOptions));
- libs = package.Libs(true);
- mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS_PRIVATE", libs.Flagline);
- mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS_PRIVATE",
- cmList::to_string(libs.LibDirs));
- mf.AddDefinition("CMAKE_PKG_CONFIG_LIBNAMES_PRIVATE",
- cmList::to_string(libs.LibNames));
- mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS_PRIVATE",
- cmList::to_string(libs.LinkOptions));
- return true;
- }
- using pkgStack = std::unordered_map<std::string, std::vector<pkgStackEntry>>;
- using pkgProviders = std::unordered_map<std::string, std::string>;
- cmTarget* CreateCMakeTarget(std::string const& name, std::string const& prefix,
- cmPkgConfigResult& pkg, pkgProviders& providers,
- cmMakefile& mf)
- {
- auto* tgt = mf.AddForeignTarget("pkgcfg", cmStrCat(prefix, name));
- tgt->AppendProperty("VERSION", pkg.Version());
- auto libs = pkg.Libs();
- for (auto const& flag : libs.LibNames) {
- tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", flag.substr(2));
- }
- for (auto const& flag : libs.LibDirs) {
- tgt->AppendProperty("INTERFACE_LINK_DIRECTORIES", flag.substr(2));
- }
- tgt->AppendProperty("INTERFACE_LINK_OPTIONS",
- cmList::to_string(libs.LinkOptions));
- auto cflags = pkg.Cflags();
- for (auto const& flag : cflags.Includes) {
- tgt->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES", flag.substr(2));
- }
- tgt->AppendProperty("INTERFACE_COMPILE_OPTIONS",
- cmList::to_string(cflags.CompileOptions));
- for (auto& dep : pkg.Requires()) {
- auto it = providers.find(dep.Name);
- if (it != providers.end()) {
- tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", it->second);
- continue;
- }
- tgt->AppendProperty("INTERFACE_LINK_LIBRARIES",
- cmStrCat("@foreign_pkgcfg::", prefix, dep.Name));
- }
- return tgt;
- }
- bool CheckPackageDependencies(
- std::string const& name, std::string const& prefix, cmPkgConfigResult& pkg,
- pkgStack& inStack,
- std::unordered_map<std::string, cmPkgConfigResult>& outStack,
- pkgProviders& providers, ImportEnv& imEnv)
- {
- for (auto& dep : pkg.Requires()) {
- auto prov_it = providers.find(dep.Name);
- if (prov_it != providers.end()) {
- continue;
- }
- auto* tgt = imEnv.status.GetMakefile().FindTargetToUse(
- cmStrCat("@foreign_pkgcfg::", prefix, dep.Name),
- cmStateEnums::TargetDomain::FOREIGN);
- if (tgt) {
- auto ver = tgt->GetProperty("VERSION");
- if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, *ver)) {
- warn_or_error(cmStrCat("Package '", dep.Name, "' version '", *ver,
- "' does not meet version requirement '",
- dep.VerReq.string(), "' of '", name, '\''),
- imEnv);
- return false;
- }
- continue;
- }
- auto it = outStack.find(dep.Name);
- if (it != outStack.end()) {
- auto ver = it->second.Version();
- if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, ver)) {
- warn_or_error(cmStrCat("Package '", dep.Name, "' version '", ver,
- "' does not meet version requirement '",
- dep.VerReq.string(), "' of '", name, '\''),
- imEnv);
- return false;
- }
- continue;
- }
- inStack[dep.Name].emplace_back(
- pkgStackEntry{ std::move(dep.VerReq), name });
- }
- return true;
- }
- struct PopulateArguments : CommonArguments
- {
- cm::optional<std::string> Prefix;
- cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Providers;
- };
- #define BIND_POPULATE(argtype) \
- BIND_COMMON(argtype) \
- .Bind("PREFIX"_s, &argtype::Prefix) \
- .Bind("BIND_PC_REQUIRES"_s, &argtype::Providers)
- auto const PopulateParser = BIND_POPULATE(PopulateArguments);
- std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
- cmExecutionStatus& status)
- {
- std::string prefix = args.Prefix ? cmStrCat(*args.Prefix, "_"_s) : "";
- auto& mf = status.GetMakefile();
- auto maybeEnv = HandleCommon(args, status);
- if (!maybeEnv) {
- return { !args.Required, false };
- }
- auto& pcEnv = maybeEnv->first;
- auto& imEnv = maybeEnv->second;
- pkgProviders providers;
- if (args.Providers) {
- for (auto const& provider_str : *args.Providers) {
- auto assignment = provider_str.find('=');
- if (assignment != std::string::npos) {
- providers.emplace(provider_str.substr(0, assignment),
- provider_str.substr(assignment + 1));
- } else {
- imEnv.status.SetError(cmStrCat(
- "No '=' found in BIND_PC_REQUIRES argument '", provider_str, '\''));
- cmSystemTools::SetFatalErrorOccurred();
- return { false, false };
- }
- }
- }
- pkgStack inStack;
- std::unordered_map<std::string, cmPkgConfigResult> outStack;
- auto maybePackage = ImportPackage(*args.Package, args.Version, imEnv, pcEnv);
- if (!maybePackage) {
- return { !args.Required, false };
- }
- imEnv.exact = false;
- if (!CheckPackageDependencies(*args.Package, prefix, *maybePackage, inStack,
- outStack, providers, imEnv)) {
- return { !args.Required, false };
- }
- outStack[*args.Package] = std::move(*maybePackage);
- while (!inStack.empty()) {
- auto name = inStack.begin()->first;
- auto reqs = inStack.begin()->second;
- maybePackage = ImportPackage(name, reqs, imEnv, pcEnv);
- if (!maybePackage) {
- return { !args.Required, false };
- }
- if (!CheckPackageDependencies(name, prefix, *maybePackage, inStack,
- outStack, providers, imEnv)) {
- return { !args.Required, false };
- }
- inStack.erase(name);
- outStack[std::move(name)] = std::move(*maybePackage);
- }
- for (auto& entry : outStack) {
- CreateCMakeTarget(entry.first, prefix, entry.second, providers, mf);
- }
- return { true, true };
- }
- bool HandlePopulateCommand(std::vector<std::string> const& args,
- cmExecutionStatus& status)
- {
- std::vector<std::string> unparsed;
- auto parsedArgs = PopulateParser.Parse(args, &unparsed);
- std::string prefix =
- parsedArgs.Prefix ? cmStrCat(*parsedArgs.Prefix, "_"_s) : "";
- auto foreign_name =
- cmStrCat("@foreign_pkgcfg::", prefix, *parsedArgs.Package);
- auto found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND");
- auto& mf = status.GetMakefile();
- if (mf.FindTargetToUse(foreign_name, cmStateEnums::TargetDomain::FOREIGN)) {
- mf.AddDefinition(found_var, "TRUE");
- return true;
- }
- auto result = PopulatePCTarget(parsedArgs, status);
- mf.AddDefinition(found_var, result.second ? "TRUE" : "FALSE");
- return result.first;
- }
- struct ImportArguments : PopulateArguments
- {
- cm::optional<std::string> Name;
- };
- auto const ImportParser =
- BIND_POPULATE(ImportArguments).Bind("NAME"_s, &ImportArguments::Name);
- bool HandleImportCommand(std::vector<std::string> const& args,
- cmExecutionStatus& status)
- {
- std::vector<std::string> unparsed;
- auto parsedArgs = ImportParser.Parse(args, &unparsed);
- std::string prefix =
- parsedArgs.Prefix ? cmStrCat(*parsedArgs.Prefix, "_"_s) : "";
- auto foreign_name =
- cmStrCat("@foreign_pkgcfg::", prefix, *parsedArgs.Package);
- auto local_name =
- cmStrCat("PkgConfig::", parsedArgs.Name.value_or(*parsedArgs.Package));
- auto found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND");
- auto& mf = status.GetMakefile();
- if (mf.FindTargetToUse(local_name)) {
- mf.AddDefinition(found_var, "TRUE");
- return true;
- }
- if (!mf.FindTargetToUse(foreign_name, cmStateEnums::TargetDomain::FOREIGN)) {
- auto result = PopulatePCTarget(parsedArgs, status);
- if (!result.second) {
- mf.AddDefinition(found_var, "FALSE");
- return result.first;
- }
- }
- mf.AddDefinition(found_var, "TRUE");
- auto* tgt = mf.AddImportedTarget(
- local_name, cmStateEnums::TargetType::INTERFACE_LIBRARY, false);
- tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", foreign_name);
- return true;
- }
- } // namespace
- bool cmCMakePkgConfigCommand(std::vector<std::string> const& args,
- cmExecutionStatus& status)
- {
- if (args.size() < 2) {
- status.SetError("must be called with at least two arguments.");
- return false;
- }
- static cmSubcommandTable const subcommand{
- { "EXTRACT"_s, HandleExtractCommand },
- { "POPULATE"_s, HandlePopulateCommand },
- { "IMPORT"_s, HandleImportCommand },
- };
- return subcommand(args[0], args, status);
- }
|