|
|
@@ -6,6 +6,7 @@
|
|
|
#include <cstdio>
|
|
|
#include <memory>
|
|
|
#include <string>
|
|
|
+#include <unordered_map>
|
|
|
#include <utility>
|
|
|
#include <vector>
|
|
|
|
|
|
@@ -28,12 +29,14 @@
|
|
|
#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;
|
|
|
}
|
|
|
|
|
|
namespace {
|
|
|
@@ -441,89 +444,59 @@ void CollectEnv(cmMakefile& mf, cmPkgConfigEnv& env,
|
|
|
*env.SysLibs += GetPkgConfSysLibs(mf);
|
|
|
}
|
|
|
|
|
|
-cm::optional<cmPkgConfigResult> HandleCommon(CommonArguments& args,
|
|
|
- cmExecutionStatus& status)
|
|
|
+struct ImportEnv
|
|
|
{
|
|
|
+ bool required;
|
|
|
+ bool quiet;
|
|
|
+ bool exact;
|
|
|
+ bool err;
|
|
|
+ CommonArguments::StrictnessType strictness;
|
|
|
+ cmExecutionStatus& status;
|
|
|
+};
|
|
|
|
|
|
- auto& mf = status.GetMakefile();
|
|
|
-
|
|
|
- if (!args.CheckArgs(status)) {
|
|
|
- return {};
|
|
|
- }
|
|
|
-
|
|
|
- auto warn_or_error = [&](std::string const& err) {
|
|
|
- if (args.Required) {
|
|
|
- status.SetError(err);
|
|
|
- cmSystemTools::SetFatalErrorOccurred();
|
|
|
- } else if (!args.Quiet) {
|
|
|
- mf.IssueMessage(MessageType::WARNING, err);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- cm::filesystem::path path{ *args.Package };
|
|
|
-
|
|
|
- cmPkgConfigEnv env;
|
|
|
-
|
|
|
- if (args.PcLibdir) {
|
|
|
- env.LibDirs = std::move(*args.PcLibdir);
|
|
|
- }
|
|
|
-
|
|
|
- if (args.PcPath) {
|
|
|
- env.Path = std::move(*args.PcPath);
|
|
|
- }
|
|
|
-
|
|
|
- if (args.DisableUninstalled) {
|
|
|
- env.DisableUninstalled = args.DisableUninstalled;
|
|
|
- }
|
|
|
-
|
|
|
- if (args.SysrootDir) {
|
|
|
- env.SysrootDir = std::move(*args.SysrootDir);
|
|
|
- }
|
|
|
-
|
|
|
- if (args.TopBuildDir) {
|
|
|
- env.TopBuildDir = std::move(*args.TopBuildDir);
|
|
|
+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;
|
|
|
+}
|
|
|
|
|
|
- CollectEnv(mf, env, args.EnvMode);
|
|
|
+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())) {
|
|
|
- warn_or_error(cmStrCat("Could not find '", *args.Package, "'"));
|
|
|
- return {};
|
|
|
+ return result;
|
|
|
}
|
|
|
} else {
|
|
|
|
|
|
- std::vector<std::string> search;
|
|
|
- if (env.Path) {
|
|
|
- search = *env.Path;
|
|
|
- if (env.LibDirs) {
|
|
|
- search += *env.LibDirs;
|
|
|
- }
|
|
|
- } else if (env.LibDirs) {
|
|
|
- search = *env.LibDirs;
|
|
|
- }
|
|
|
-
|
|
|
- if (env.DisableUninstalled && !*env.DisableUninstalled) {
|
|
|
+ if (pcEnv.DisableUninstalled && !*pcEnv.DisableUninstalled) {
|
|
|
auto uninstalled = path;
|
|
|
uninstalled.concat("-uninstalled.pc");
|
|
|
uninstalled =
|
|
|
- cmSystemTools::FindFile(uninstalled.string(), search, true);
|
|
|
+ cmSystemTools::FindFile(uninstalled.string(), pcEnv.search, true);
|
|
|
if (uninstalled.empty()) {
|
|
|
- path =
|
|
|
- cmSystemTools::FindFile(path.concat(".pc").string(), search, true);
|
|
|
+ path = cmSystemTools::FindFile(path.concat(".pc").string(),
|
|
|
+ pcEnv.search, true);
|
|
|
if (path.empty()) {
|
|
|
- warn_or_error(cmStrCat("Could not find '", *args.Package, "'"));
|
|
|
- return {};
|
|
|
+ return result;
|
|
|
}
|
|
|
} else {
|
|
|
path = uninstalled;
|
|
|
}
|
|
|
} else {
|
|
|
- path =
|
|
|
- cmSystemTools::FindFile(path.concat(".pc").string(), search, true);
|
|
|
+ path = cmSystemTools::FindFile(path.concat(".pc").string(), pcEnv.search,
|
|
|
+ true);
|
|
|
if (path.empty()) {
|
|
|
- warn_or_error(cmStrCat("Could not find '", *args.Package, "'"));
|
|
|
- return {};
|
|
|
+ return result;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -534,8 +507,9 @@ cm::optional<cmPkgConfigResult> HandleCommon(CommonArguments& args,
|
|
|
cmsys::ifstream ifs(path.string().c_str(), std::ios::binary);
|
|
|
|
|
|
if (!ifs) {
|
|
|
- warn_or_error(cmStrCat("Could not open file '", path.string(), "'"));
|
|
|
- return {};
|
|
|
+ warn_or_error(cmStrCat("Could not open file '", path.string(), "'"),
|
|
|
+ imEnv);
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
std::unique_ptr<char[]> buf(new char[len]);
|
|
|
@@ -543,8 +517,9 @@ cm::optional<cmPkgConfigResult> HandleCommon(CommonArguments& args,
|
|
|
|
|
|
// 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(), "'"));
|
|
|
- return {};
|
|
|
+ warn_or_error(cmStrCat("Error while reading file '", path.string(), "'"),
|
|
|
+ imEnv);
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
using StrictnessType = CommonArguments::StrictnessType;
|
|
|
@@ -552,53 +527,161 @@ cm::optional<cmPkgConfigResult> HandleCommon(CommonArguments& args,
|
|
|
cmPkgConfigParser parser;
|
|
|
auto err = parser.Finish(buf.get(), len);
|
|
|
|
|
|
- if (args.Strictness != StrictnessType::STRICTNESS_BEST_EFFORT &&
|
|
|
+ if (imEnv.strictness != StrictnessType::STRICTNESS_BEST_EFFORT &&
|
|
|
err != PCE_OK) {
|
|
|
- warn_or_error(cmStrCat("Parsing failed for file '", path.string(), "'"));
|
|
|
- return {};
|
|
|
+ warn_or_error(cmStrCat("Parsing failed for file '", path.string(), "'"),
|
|
|
+ imEnv);
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
- cm::optional<cmPkgConfigResult> result;
|
|
|
- if (args.Strictness == StrictnessType::STRICTNESS_STRICT) {
|
|
|
- result = cmPkgConfigResolver::ResolveStrict(parser.Data(), std::move(env));
|
|
|
- } else if (args.Strictness == StrictnessType::STRICTNESS_PERMISSIVE) {
|
|
|
- result =
|
|
|
- cmPkgConfigResolver::ResolvePermissive(parser.Data(), std::move(env));
|
|
|
+ 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(), std::move(env));
|
|
|
+ result = cmPkgConfigResolver::ResolveBestEffort(parser.Data(), pcEnv);
|
|
|
}
|
|
|
|
|
|
if (!result) {
|
|
|
- warn_or_error(
|
|
|
- cmStrCat("Resolution failed for file '", path.string(), "'"));
|
|
|
- } else if (args.Exact) {
|
|
|
+ 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 (args.Version) {
|
|
|
- ver = cmPkgConfigResolver::ParseVersion(*args.Version).Version;
|
|
|
+ if (version) {
|
|
|
+ ver = cmPkgConfigResolver::ParseVersion(*version).Version;
|
|
|
}
|
|
|
|
|
|
if (ver != result->Version()) {
|
|
|
warn_or_error(
|
|
|
- cmStrCat("Package '", *args.Package, "' version '", result->Version(),
|
|
|
- "' does not meet exact version requirement '", ver, "'"));
|
|
|
+ cmStrCat("Package '", package, "' version '", result->Version(),
|
|
|
+ "' does not meet exact version requirement '", ver, "'"),
|
|
|
+ imEnv);
|
|
|
return {};
|
|
|
}
|
|
|
|
|
|
- } else if (args.Version) {
|
|
|
- auto rv = cmPkgConfigResolver::ParseVersion(*args.Version);
|
|
|
+ } else if (version) {
|
|
|
+ auto rv = cmPkgConfigResolver::ParseVersion(*version);
|
|
|
if (!cmPkgConfigResolver::CheckVersion(rv, result->Version())) {
|
|
|
warn_or_error(
|
|
|
- cmStrCat("Package '", *args.Package, "' version '", result->Version(),
|
|
|
- "' does not meet version requirement '", *args.Version, "'"));
|
|
|
+ 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;
|
|
|
@@ -623,35 +706,41 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
|
|
|
|
|
|
std::vector<std::string> unparsed;
|
|
|
auto parsedArgs = ExtractParser.Parse(args, &unparsed);
|
|
|
- auto maybeResolved = HandleCommon(parsedArgs, status);
|
|
|
+ auto maybeEnv = HandleCommon(parsedArgs, status);
|
|
|
|
|
|
- if (!maybeResolved) {
|
|
|
+ if (!maybeEnv) {
|
|
|
return !parsedArgs.Required;
|
|
|
}
|
|
|
+ auto& pcEnv = maybeEnv->first;
|
|
|
+ auto& imEnv = maybeEnv->second;
|
|
|
|
|
|
- auto& resolved = *maybeResolved;
|
|
|
- auto version = resolved.Version();
|
|
|
+ auto maybePackage =
|
|
|
+ ImportPackage(*parsedArgs.Package, parsedArgs.Version, imEnv, pcEnv);
|
|
|
+ if (!maybePackage) {
|
|
|
+ return !parsedArgs.Required;
|
|
|
+ }
|
|
|
+ auto& package = *maybePackage;
|
|
|
|
|
|
if (parsedArgs.AllowSystemIncludes) {
|
|
|
- resolved.env.AllowSysCflags = *parsedArgs.AllowSystemIncludes;
|
|
|
+ pcEnv.AllowSysCflags = *parsedArgs.AllowSystemIncludes;
|
|
|
}
|
|
|
|
|
|
if (parsedArgs.AllowSystemLibs) {
|
|
|
- resolved.env.AllowSysLibs = *parsedArgs.AllowSystemLibs;
|
|
|
+ pcEnv.AllowSysLibs = *parsedArgs.AllowSystemLibs;
|
|
|
}
|
|
|
|
|
|
if (parsedArgs.SystemIncludeDirs) {
|
|
|
- resolved.env.SysCflags = *parsedArgs.SystemIncludeDirs;
|
|
|
+ pcEnv.SysCflags = *parsedArgs.SystemIncludeDirs;
|
|
|
}
|
|
|
|
|
|
if (parsedArgs.SystemLibraryDirs) {
|
|
|
- resolved.env.SysLibs = *parsedArgs.SystemLibraryDirs;
|
|
|
+ pcEnv.SysLibs = *parsedArgs.SystemLibraryDirs;
|
|
|
}
|
|
|
|
|
|
auto& mf = status.GetMakefile();
|
|
|
- mf.AddDefinition("CMAKE_PKG_CONFIG_NAME", resolved.Name());
|
|
|
- mf.AddDefinition("CMAKE_PKG_CONFIG_DESCRIPTION", resolved.Description());
|
|
|
- mf.AddDefinition("CMAKE_PKG_CONFIG_VERSION", version);
|
|
|
+ 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) {
|
|
|
@@ -665,26 +754,26 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
|
|
|
mf.AddDefinition(def, cmList::to_string(vec));
|
|
|
};
|
|
|
|
|
|
- make_list("CMAKE_PKG_CONFIG_CONFLICTS", resolved.Conflicts());
|
|
|
- make_list("CMAKE_PKG_CONFIG_PROVIDES", resolved.Provides());
|
|
|
- make_list("CMAKE_PKG_CONFIG_REQUIRES", resolved.Requires());
|
|
|
- make_list("CMAKE_PKG_CONFIG_REQUIRES_PRIVATE", resolved.Requires(true));
|
|
|
+ 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 = resolved.Cflags();
|
|
|
+ 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 = resolved.Cflags(true);
|
|
|
+ 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 = resolved.Libs();
|
|
|
+ auto libs = package.Libs();
|
|
|
mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS", libs.Flagline);
|
|
|
mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS",
|
|
|
cmList::to_string(libs.LibDirs));
|
|
|
@@ -693,7 +782,7 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
|
|
|
mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS",
|
|
|
cmList::to_string(libs.LinkOptions));
|
|
|
|
|
|
- libs = resolved.Libs(true);
|
|
|
+ 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));
|
|
|
@@ -704,6 +793,220 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
|
|
|
|
|
|
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, cmPkgConfigResult& pkg,
|
|
|
+ pkgProviders& providers, cmMakefile& mf)
|
|
|
+{
|
|
|
+ auto* tgt = mf.AddForeignTarget("pkgcfg", 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::", dep.Name));
|
|
|
+ }
|
|
|
+ return tgt;
|
|
|
+}
|
|
|
+
|
|
|
+bool CheckPackageDependencies(
|
|
|
+ std::string const& name, 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::", 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<ArgumentParser::MaybeEmpty<std::vector<std::string>>> providers;
|
|
|
+};
|
|
|
+
|
|
|
+auto const PopulateParser =
|
|
|
+ BIND_COMMON(PopulateArguments)
|
|
|
+ .Bind("BIND_PC_REQUIRES"_s, &PopulateArguments::providers);
|
|
|
+
|
|
|
+std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
|
|
|
+ cmExecutionStatus& status)
|
|
|
+{
|
|
|
+
|
|
|
+ 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, *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, *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, 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);
|
|
|
+
|
|
|
+ auto foreign_name = cmStrCat("@foreign_pkgcfg::", *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;
|
|
|
+}
|
|
|
+
|
|
|
+bool HandleImportCommand(std::vector<std::string> const& args,
|
|
|
+ cmExecutionStatus& status)
|
|
|
+{
|
|
|
+ std::vector<std::string> unparsed;
|
|
|
+ auto parsedArgs = PopulateParser.Parse(args, &unparsed);
|
|
|
+ auto foreign_name = cmStrCat("@foreign_pkgcfg::", *parsedArgs.Package);
|
|
|
+ auto local_name = cmStrCat("PkgConfig::", *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,
|
|
|
@@ -716,6 +1019,8 @@ bool cmCMakePkgConfigCommand(std::vector<std::string> const& args,
|
|
|
|
|
|
static cmSubcommandTable const subcommand{
|
|
|
{ "EXTRACT"_s, HandleExtractCommand },
|
|
|
+ { "POPULATE"_s, HandlePopulateCommand },
|
|
|
+ { "IMPORT"_s, HandleImportCommand },
|
|
|
};
|
|
|
|
|
|
return subcommand(args[0], args, status);
|