cmCMakePkgConfigCommand.cxx 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCMakePkgConfigCommand.h"
  4. #include <cstdio>
  5. #include <memory>
  6. #include <string>
  7. #include <unordered_map>
  8. #include <utility>
  9. #include <vector>
  10. #include <cm/filesystem>
  11. #include <cm/optional>
  12. #include <cm/string_view>
  13. #include <cmext/string_view>
  14. #include "cmsys/FStream.hxx"
  15. #include "cmArgumentParser.h"
  16. #include "cmArgumentParserTypes.h"
  17. #include "cmExecutionStatus.h"
  18. #include "cmList.h"
  19. #include "cmMakefile.h"
  20. #include "cmMessageType.h"
  21. #include "cmPkgConfigParser.h"
  22. #include "cmPkgConfigResolver.h"
  23. #include "cmStateTypes.h"
  24. #include "cmStringAlgorithms.h"
  25. #include "cmSubcommandTable.h"
  26. #include "cmSystemTools.h"
  27. #include "cmTarget.h"
  28. #include "cmValue.h"
  29. #include <cmllpkgc/llpkgc.h>
  30. // IWYU wants this
  31. namespace {
  32. struct ExtractArguments;
  33. struct PopulateArguments;
  34. }
  35. namespace {
  36. cm::optional<std::string> GetPkgConfigBin(cmMakefile& mf)
  37. {
  38. cm::optional<std::string> result;
  39. auto pkgcfg = mf.GetDefinition("CMAKE_PKG_CONFIG_BIN");
  40. if (pkgcfg.IsNOTFOUND()) {
  41. return result;
  42. }
  43. if (pkgcfg) {
  44. result = *pkgcfg;
  45. return result;
  46. }
  47. std::string path = cmSystemTools::FindProgram("pkgconf");
  48. if (path.empty()) {
  49. path = cmSystemTools::FindProgram("pkg-config");
  50. if (path.empty()) {
  51. mf.AddCacheDefinition("CMAKE_PKG_CONFIG_BIN", "pkg-config-NOTFOUND",
  52. "Location of pkg-config or pkgconf binary",
  53. cmStateEnums::FILEPATH);
  54. return result;
  55. }
  56. }
  57. mf.AddCacheDefinition("CMAKE_PKG_CONFIG_BIN", path,
  58. "Location of pkg-config or pkgconf binary",
  59. cmStateEnums::FILEPATH);
  60. result = std::move(path);
  61. return result;
  62. }
  63. std::vector<std::string> GetLocations(cmMakefile& mf, char const* cachevar,
  64. char const* envvar, char const* desc,
  65. char const* pcvar, bool need_pkgconf,
  66. std::vector<std::string> default_locs)
  67. {
  68. auto def = mf.GetDefinition(cachevar);
  69. if (def) {
  70. return cmList(def);
  71. }
  72. std::string paths;
  73. if (cmSystemTools::GetEnv(envvar, paths)) {
  74. cmPkgConfigResolver::ReplaceSep(paths);
  75. mf.AddCacheDefinition(cachevar, paths, desc, cmStateEnums::STRING);
  76. return cmList(paths);
  77. }
  78. auto pkgcfg = GetPkgConfigBin(mf);
  79. if (!pkgcfg || (need_pkgconf && (pkgcfg->find("pkgconf") == pkgcfg->npos))) {
  80. mf.AddCacheDefinition(cachevar, cmList::to_string(default_locs), desc,
  81. cmStateEnums::STRING);
  82. return default_locs;
  83. }
  84. std::string out;
  85. cmSystemTools::RunSingleCommand({ *pkgcfg, pcvar, "pkg-config" }, &out,
  86. nullptr, nullptr, nullptr,
  87. cmSystemTools::OUTPUT_NONE);
  88. cmPkgConfigResolver::ReplaceSep(out);
  89. out = cmTrimWhitespace(out);
  90. mf.AddCacheDefinition(cachevar, out, desc, cmStateEnums::STRING);
  91. return cmList(out);
  92. }
  93. std::vector<std::string> GetPcLibDirs(cmMakefile& mf)
  94. {
  95. std::vector<std::string> default_locs = {
  96. #ifndef _WIN32
  97. "/usr/lib/pkgconfig", "/usr/share/pkgconfig"
  98. #endif
  99. };
  100. return GetLocations(mf, "CMAKE_PKG_CONFIG_PC_LIB_DIRS", "PKG_CONFIG_LIBDIR",
  101. "Default search locations for package files",
  102. "--variable=pc_path", false, std::move(default_locs));
  103. }
  104. std::vector<std::string> GetSysLibDirs(cmMakefile& mf)
  105. {
  106. std::vector<std::string> default_locs = {
  107. #ifndef _WIN32
  108. "/lib", "/usr/lib"
  109. #endif
  110. };
  111. return GetLocations(
  112. mf, "CMAKE_PKG_CONFIG_SYS_LIB_DIRS", "PKG_CONFIG_SYSTEM_LIBRARY_PATH",
  113. "System library directories filtered by flag mangling",
  114. "--variable=pc_system_libdirs", true, std::move(default_locs));
  115. }
  116. std::vector<std::string> GetSysCflags(cmMakefile& mf)
  117. {
  118. std::vector<std::string> default_locs = {
  119. #ifndef _WIN32
  120. "/usr/include"
  121. #endif
  122. };
  123. return GetLocations(
  124. mf, "CMAKE_PKG_CONFIG_SYS_INCLUDE_DIRS", "PKG_CONFIG_SYSTEM_INCLUDE_PATH",
  125. "System include directories filtered by flag mangling",
  126. "--variable=pc_system_includedirs", true, std::move(default_locs));
  127. }
  128. std::vector<std::string> GetPkgConfSysLibs(cmMakefile& mf)
  129. {
  130. auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS");
  131. if (def) {
  132. return cmList(def);
  133. }
  134. std::string paths;
  135. if (!cmSystemTools::GetEnv("LIBRARY_PATH", paths)) {
  136. return {};
  137. }
  138. cmPkgConfigResolver::ReplaceSep(paths);
  139. mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS", paths,
  140. "Additional system library directories filtered by "
  141. "flag mangling in PKGCONF mode",
  142. cmStateEnums::STRING);
  143. return cmList(paths);
  144. }
  145. std::vector<std::string> GetPkgConfSysCflags(cmMakefile& mf)
  146. {
  147. auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PKGCONF_INCLUDES");
  148. if (def) {
  149. return cmList(def);
  150. }
  151. std::string paths;
  152. auto get_and_append = [&](char const* var) {
  153. if (paths.empty()) {
  154. cmSystemTools::GetEnv(var, paths);
  155. } else {
  156. std::string tmp;
  157. cmSystemTools::GetEnv(var, tmp);
  158. if (!tmp.empty()) {
  159. paths += ";" + tmp;
  160. }
  161. }
  162. };
  163. get_and_append("CPATH");
  164. get_and_append("C_INCLUDE_PATH");
  165. get_and_append("CPLUS_INCLUDE_PATH");
  166. get_and_append("OBJC_INCLUDE_PATH");
  167. #ifdef _WIN32
  168. get_and_append("INCLUDE");
  169. #endif
  170. cmPkgConfigResolver::ReplaceSep(paths);
  171. mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PKGCONF_INCLUDES", paths,
  172. "Additional system include directories filtered by "
  173. "flag mangling in PKGCONF mode",
  174. cmStateEnums::STRING);
  175. return cmList(paths);
  176. }
  177. std::vector<std::string> GetPcPath(cmMakefile& mf)
  178. {
  179. auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PC_PATH");
  180. if (def) {
  181. return cmList(def);
  182. }
  183. std::string pcpath;
  184. if (cmSystemTools::GetEnv("PKG_CONFIG_PATH", pcpath)) {
  185. auto result = cmSystemTools::SplitString(pcpath, cmPkgConfigResolver::Sep);
  186. mf.AddCacheDefinition(
  187. "CMAKE_PKG_CONFIG_PC_PATH", cmList::to_string(result),
  188. "Additional search locations for package files", cmStateEnums::STRING);
  189. return result;
  190. }
  191. mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PC_PATH", "",
  192. "Additional search locations for package files",
  193. cmStateEnums::STRING);
  194. return {};
  195. }
  196. cm::optional<std::string> GetPath(cmMakefile& mf, char const* cachevar,
  197. char const* envvar, char const* desc)
  198. {
  199. cm::optional<std::string> result;
  200. auto def = mf.GetDefinition(cachevar);
  201. if (def) {
  202. result = *def;
  203. return result;
  204. }
  205. std::string path;
  206. if (cmSystemTools::GetEnv(envvar, path)) {
  207. mf.AddCacheDefinition(cachevar, path, desc, cmStateEnums::FILEPATH);
  208. result = std::move(path);
  209. return result;
  210. }
  211. return result;
  212. }
  213. cm::optional<std::string> GetSysrootDir(cmMakefile& mf)
  214. {
  215. return GetPath(mf, "CMAKE_PKG_CONFIG_SYSROOT_DIR", "PKG_CONFIG_SYSROOT_DIR",
  216. "System root used for re-rooting package includes and "
  217. "library directories");
  218. }
  219. cm::optional<std::string> GetTopBuildDir(cmMakefile& mf)
  220. {
  221. return GetPath(mf, "CMAKE_PKG_CONFIG_TOP_BUILD_DIR",
  222. "PKG_CONFIG_TOP_BUILD_DIR",
  223. "Package file top_build_dir variable default value");
  224. }
  225. bool GetBool(cmMakefile& mf, char const* cachevar, char const* envvar,
  226. char const* desc)
  227. {
  228. auto def = mf.GetDefinition(cachevar);
  229. if (def) {
  230. return def.IsOn();
  231. }
  232. if (cmSystemTools::HasEnv(envvar)) {
  233. mf.AddCacheDefinition(cachevar, "ON", desc, cmStateEnums::BOOL);
  234. return true;
  235. }
  236. return false;
  237. }
  238. bool GetDisableUninstalled(cmMakefile& mf)
  239. {
  240. return GetBool(mf, "CMAKE_PKG_CONFIG_DISABLE_UNINSTALLED",
  241. "PKG_CONFIG_DISABLE_UNINSTALLED",
  242. "Disable search for `-uninstalled` (build tree) packages");
  243. }
  244. bool GetAllowSysLibs(cmMakefile& mf)
  245. {
  246. return GetBool(mf, "CMAKE_PKG_CONFIG_ALLOW_SYS_LIBS",
  247. "PKG_CONFIG_ALLOW_SYSTEM_LIBS",
  248. "Allow system library directories during flag mangling");
  249. }
  250. bool GetAllowSysInclude(cmMakefile& mf)
  251. {
  252. return GetBool(mf, "CMAKE_PKG_CONFIG_ALLOW_SYS_INCLUDES",
  253. "PKG_CONFIG_ALLOW_SYSTEM_CFLAGS",
  254. "Allow system include paths during flag manglging");
  255. }
  256. struct CommonArguments : ArgumentParser::ParseResult
  257. {
  258. bool Required = false;
  259. bool Exact = false;
  260. bool Quiet = false;
  261. enum StrictnessType
  262. {
  263. STRICTNESS_STRICT,
  264. STRICTNESS_PERMISSIVE,
  265. STRICTNESS_BEST_EFFORT,
  266. };
  267. StrictnessType Strictness = STRICTNESS_PERMISSIVE;
  268. std::string StrictnessError;
  269. ArgumentParser::Continue SetStrictness(cm::string_view strictness)
  270. {
  271. if (strictness == "STRICT"_s) {
  272. Strictness = STRICTNESS_STRICT;
  273. } else if (strictness == "PERMISSIVE"_s) {
  274. Strictness = STRICTNESS_PERMISSIVE;
  275. } else if (strictness == "BEST_EFFORT"_s) {
  276. Strictness = STRICTNESS_BEST_EFFORT;
  277. } else {
  278. StrictnessError =
  279. cmStrCat("Invalid 'STRICTNESS' '", strictness,
  280. "'; must be one of 'STRICT', 'PERMISSIVE', or 'BEST_EFFORT'");
  281. }
  282. return ArgumentParser::Continue::Yes;
  283. }
  284. enum EnvModeType
  285. {
  286. ENVMODE_FDO,
  287. ENVMODE_PKGCONF,
  288. ENVMODE_IGNORE,
  289. };
  290. EnvModeType EnvMode = ENVMODE_PKGCONF;
  291. std::string EnvModeError;
  292. ArgumentParser::Continue SetEnvMode(cm::string_view envMode)
  293. {
  294. if (envMode == "FDO"_s) {
  295. EnvMode = ENVMODE_FDO;
  296. } else if (envMode == "PKGCONF"_s) {
  297. EnvMode = ENVMODE_PKGCONF;
  298. } else if (envMode == "IGNORE"_s) {
  299. EnvMode = ENVMODE_IGNORE;
  300. } else {
  301. EnvModeError =
  302. cmStrCat("Invalid 'ENV_MODE' '", envMode,
  303. "'; must be one of 'FDO', 'PKGCONF', or 'IGNORE'");
  304. }
  305. return ArgumentParser::Continue::Yes;
  306. }
  307. cm::optional<std::string> Package;
  308. cm::optional<std::string> Version;
  309. cm::optional<std::string> SysrootDir;
  310. cm::optional<std::string> TopBuildDir;
  311. cm::optional<bool> DisableUninstalled;
  312. cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> PcPath;
  313. cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> PcLibdir;
  314. bool CheckArgs(cmExecutionStatus& status) const
  315. {
  316. if (!Package) {
  317. status.SetError("A package name or absolute path must be specified");
  318. return false;
  319. }
  320. if (!StrictnessError.empty()) {
  321. status.SetError(StrictnessError);
  322. return false;
  323. }
  324. if (!EnvModeError.empty()) {
  325. status.SetError(EnvModeError);
  326. return false;
  327. }
  328. return true;
  329. }
  330. };
  331. #define BIND_COMMON(argtype) \
  332. (cmArgumentParser<argtype>{}) \
  333. .Bind(1, &argtype::Package) \
  334. .Bind(2, &argtype::Version) \
  335. .Bind("REQUIRED"_s, &argtype::Required) \
  336. .Bind("EXACT"_s, &argtype::Exact) \
  337. .Bind("QUIET"_s, &argtype::Quiet) \
  338. .Bind("STRICTNESS"_s, &argtype::SetStrictness) \
  339. .Bind("ENV_MODE"_s, &argtype::SetEnvMode) \
  340. .Bind("PC_SYSROOT_DIR"_s, &argtype::SysrootDir) \
  341. .Bind("TOP_BUILD_DIR"_s, &argtype::TopBuildDir) \
  342. .Bind("DISABLE_UNINSTALLED"_s, &argtype::DisableUninstalled) \
  343. .Bind("PC_LIBDIR"_s, &argtype::PcLibdir) \
  344. .Bind("PC_PATH"_s, &argtype::PcPath)
  345. void CollectEnv(cmMakefile& mf, cmPkgConfigEnv& env,
  346. CommonArguments::EnvModeType mode)
  347. {
  348. if (mode == CommonArguments::EnvModeType::ENVMODE_IGNORE) {
  349. return;
  350. }
  351. if (!env.Path) {
  352. env.Path = GetPcPath(mf);
  353. }
  354. if (!env.LibDirs) {
  355. env.LibDirs = GetPcLibDirs(mf);
  356. }
  357. if (!env.DisableUninstalled) {
  358. env.DisableUninstalled = GetDisableUninstalled(mf);
  359. }
  360. if (!env.SysrootDir) {
  361. env.SysrootDir = GetSysrootDir(mf);
  362. }
  363. if (!env.TopBuildDir) {
  364. env.TopBuildDir = GetTopBuildDir(mf);
  365. }
  366. env.AllowSysCflags = GetAllowSysInclude(mf);
  367. env.SysCflags = GetSysCflags(mf);
  368. env.AllowSysLibs = GetAllowSysLibs(mf);
  369. env.SysLibs = GetSysLibDirs(mf);
  370. if (mode == CommonArguments::EnvModeType::ENVMODE_FDO) {
  371. return;
  372. }
  373. *env.SysCflags += GetPkgConfSysCflags(mf);
  374. *env.SysLibs += GetPkgConfSysLibs(mf);
  375. }
  376. struct ImportEnv
  377. {
  378. bool required;
  379. bool quiet;
  380. bool exact;
  381. bool err;
  382. CommonArguments::StrictnessType strictness;
  383. cmExecutionStatus& status;
  384. };
  385. void warn_or_error(std::string const& err, ImportEnv& imEnv)
  386. {
  387. if (imEnv.required) {
  388. imEnv.status.SetError(err);
  389. cmSystemTools::SetFatalErrorOccurred();
  390. } else if (!imEnv.quiet) {
  391. imEnv.status.GetMakefile().IssueMessage(MessageType::WARNING, err);
  392. }
  393. imEnv.err = true;
  394. }
  395. cm::optional<cmPkgConfigResult> ReadPackage(std::string const& package,
  396. ImportEnv& imEnv,
  397. cmPkgConfigEnv& pcEnv)
  398. {
  399. cm::optional<cmPkgConfigResult> result;
  400. cm::filesystem::path path{ package };
  401. if (path.extension() == ".pc") {
  402. if (!cmSystemTools::FileExists(path.string())) {
  403. return result;
  404. }
  405. } else {
  406. if (pcEnv.DisableUninstalled && !*pcEnv.DisableUninstalled) {
  407. auto uninstalled = path;
  408. uninstalled.concat("-uninstalled.pc");
  409. uninstalled =
  410. cmSystemTools::FindFile(uninstalled.string(), pcEnv.search, true);
  411. if (uninstalled.empty()) {
  412. path = cmSystemTools::FindFile(path.concat(".pc").string(),
  413. pcEnv.search, true);
  414. if (path.empty()) {
  415. return result;
  416. }
  417. } else {
  418. path = uninstalled;
  419. }
  420. } else {
  421. path = cmSystemTools::FindFile(path.concat(".pc").string(), pcEnv.search,
  422. true);
  423. if (path.empty()) {
  424. return result;
  425. }
  426. }
  427. }
  428. auto len = cmSystemTools::FileLength(path.string());
  429. // Windows requires this weird string -> c_str dance
  430. cmsys::ifstream ifs(path.string().c_str(), std::ios::binary);
  431. if (!ifs) {
  432. warn_or_error(cmStrCat("Could not open file '", path.string(), "'"),
  433. imEnv);
  434. return result;
  435. }
  436. std::unique_ptr<char[]> buf(new char[len]);
  437. ifs.read(buf.get(), len);
  438. // Shouldn't have hit eof on previous read, should hit eof now
  439. if (ifs.fail() || ifs.eof() || ifs.get() != EOF) {
  440. warn_or_error(cmStrCat("Error while reading file '", path.string(), "'"),
  441. imEnv);
  442. return result;
  443. }
  444. using StrictnessType = CommonArguments::StrictnessType;
  445. cmPkgConfigParser parser;
  446. auto err = parser.Finish(buf.get(), len);
  447. if (imEnv.strictness != StrictnessType::STRICTNESS_BEST_EFFORT &&
  448. err != PCE_OK) {
  449. warn_or_error(cmStrCat("Parsing failed for file '", path.string(), "'"),
  450. imEnv);
  451. return result;
  452. }
  453. if (imEnv.strictness == StrictnessType::STRICTNESS_STRICT) {
  454. result = cmPkgConfigResolver::ResolveStrict(parser.Data(), pcEnv);
  455. } else if (imEnv.strictness == StrictnessType::STRICTNESS_PERMISSIVE) {
  456. result = cmPkgConfigResolver::ResolvePermissive(parser.Data(), pcEnv);
  457. } else {
  458. result = cmPkgConfigResolver::ResolveBestEffort(parser.Data(), pcEnv);
  459. }
  460. if (!result) {
  461. warn_or_error(cmStrCat("Resolution failed for file '", path.string(), "'"),
  462. imEnv);
  463. }
  464. return result;
  465. }
  466. cm::optional<cmPkgConfigResult> ImportPackage(
  467. std::string const& package, cm::optional<std::string> version,
  468. ImportEnv& imEnv, cmPkgConfigEnv& pcEnv)
  469. {
  470. auto result = ReadPackage(package, imEnv, pcEnv);
  471. if (!result) {
  472. if (!imEnv.err) {
  473. warn_or_error(cmStrCat("Could not find pkg-config: '", package, "'"),
  474. imEnv);
  475. }
  476. return result;
  477. }
  478. if (imEnv.exact) {
  479. std::string ver;
  480. if (version) {
  481. ver = cmPkgConfigResolver::ParseVersion(*version).Version;
  482. }
  483. if (ver != result->Version()) {
  484. warn_or_error(
  485. cmStrCat("Package '", package, "' version '", result->Version(),
  486. "' does not meet exact version requirement '", ver, "'"),
  487. imEnv);
  488. return {};
  489. }
  490. } else if (version) {
  491. auto rv = cmPkgConfigResolver::ParseVersion(*version);
  492. if (!cmPkgConfigResolver::CheckVersion(rv, result->Version())) {
  493. warn_or_error(
  494. cmStrCat("Package '", package, "' version '", result->Version(),
  495. "' does not meet version requirement '", *version, "'"),
  496. imEnv);
  497. return {};
  498. }
  499. }
  500. result->env = &pcEnv;
  501. return result;
  502. }
  503. struct pkgStackEntry
  504. {
  505. cmPkgConfigVersionReq ver;
  506. std::string parent;
  507. };
  508. cm::optional<cmPkgConfigResult> ImportPackage(
  509. std::string const& package, std::vector<pkgStackEntry> const& reqs,
  510. ImportEnv& imEnv, cmPkgConfigEnv& pcEnv)
  511. {
  512. auto result = ReadPackage(package, imEnv, pcEnv);
  513. if (!result) {
  514. if (!imEnv.err) {
  515. std::string req_str = cmStrCat("'", reqs.begin()->parent, "'");
  516. for (auto it = reqs.begin() + 1; it != reqs.end(); ++it) {
  517. req_str = cmStrCat(req_str, ", '", it->parent, "'");
  518. }
  519. warn_or_error(cmStrCat("Could not find pkg-config: '", package,
  520. "' required by: ", req_str),
  521. imEnv);
  522. }
  523. return result;
  524. }
  525. auto ver = result->Version();
  526. for (auto const& req : reqs) {
  527. if (!cmPkgConfigResolver::CheckVersion(req.ver, ver)) {
  528. warn_or_error(cmStrCat("Package '", package, "' version '", ver,
  529. "' does not meet version requirement '",
  530. req.ver.string(), "' ", "of '", req.parent, "'"),
  531. imEnv);
  532. return {};
  533. }
  534. }
  535. result->env = &pcEnv;
  536. return result;
  537. }
  538. cm::optional<std::pair<cmPkgConfigEnv, ImportEnv>> HandleCommon(
  539. CommonArguments& args, cmExecutionStatus& status)
  540. {
  541. auto& mf = status.GetMakefile();
  542. if (!args.CheckArgs(status)) {
  543. return {};
  544. }
  545. cmPkgConfigEnv pcEnv;
  546. if (args.PcLibdir) {
  547. pcEnv.LibDirs = std::move(*args.PcLibdir);
  548. }
  549. if (args.PcPath) {
  550. pcEnv.Path = std::move(*args.PcPath);
  551. }
  552. pcEnv.DisableUninstalled = args.DisableUninstalled;
  553. if (args.SysrootDir) {
  554. pcEnv.SysrootDir = std::move(*args.SysrootDir);
  555. }
  556. if (args.TopBuildDir) {
  557. pcEnv.TopBuildDir = std::move(*args.TopBuildDir);
  558. }
  559. CollectEnv(mf, pcEnv, args.EnvMode);
  560. if (pcEnv.Path) {
  561. pcEnv.search = *pcEnv.Path;
  562. if (pcEnv.LibDirs) {
  563. pcEnv.search += *pcEnv.LibDirs;
  564. }
  565. } else if (pcEnv.LibDirs) {
  566. pcEnv.search = *pcEnv.LibDirs;
  567. }
  568. return std::pair<cmPkgConfigEnv, ImportEnv>{
  569. pcEnv,
  570. { args.Required, args.Quiet, args.Exact, false, args.Strictness, status }
  571. };
  572. }
  573. struct ExtractArguments : CommonArguments
  574. {
  575. cm::optional<bool> AllowSystemIncludes;
  576. cm::optional<bool> AllowSystemLibs;
  577. cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
  578. SystemIncludeDirs;
  579. cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
  580. SystemLibraryDirs;
  581. };
  582. auto const ExtractParser =
  583. BIND_COMMON(ExtractArguments)
  584. .Bind("ALLOW_SYSTEM_INCLUDES"_s, &ExtractArguments::AllowSystemIncludes)
  585. .Bind("ALLOW_SYSTEM_LIBS"_s, &ExtractArguments::AllowSystemLibs)
  586. .Bind("SYSTEM_INCLUDE_DIRS"_s, &ExtractArguments::SystemIncludeDirs)
  587. .Bind("SYSTEM_LIBRARY_DIRS"_s, &ExtractArguments::SystemLibraryDirs);
  588. bool HandleExtractCommand(std::vector<std::string> const& args,
  589. cmExecutionStatus& status)
  590. {
  591. std::vector<std::string> unparsed;
  592. auto parsedArgs = ExtractParser.Parse(args, &unparsed);
  593. auto maybeEnv = HandleCommon(parsedArgs, status);
  594. if (!maybeEnv) {
  595. return !parsedArgs.Required;
  596. }
  597. auto& pcEnv = maybeEnv->first;
  598. auto& imEnv = maybeEnv->second;
  599. auto maybePackage =
  600. ImportPackage(*parsedArgs.Package, parsedArgs.Version, imEnv, pcEnv);
  601. if (!maybePackage) {
  602. return !parsedArgs.Required;
  603. }
  604. auto& package = *maybePackage;
  605. if (parsedArgs.AllowSystemIncludes) {
  606. pcEnv.AllowSysCflags = *parsedArgs.AllowSystemIncludes;
  607. }
  608. if (parsedArgs.AllowSystemLibs) {
  609. pcEnv.AllowSysLibs = *parsedArgs.AllowSystemLibs;
  610. }
  611. if (parsedArgs.SystemIncludeDirs) {
  612. pcEnv.SysCflags = *parsedArgs.SystemIncludeDirs;
  613. }
  614. if (parsedArgs.SystemLibraryDirs) {
  615. pcEnv.SysLibs = *parsedArgs.SystemLibraryDirs;
  616. }
  617. auto& mf = status.GetMakefile();
  618. mf.AddDefinition("CMAKE_PKG_CONFIG_NAME", package.Name());
  619. mf.AddDefinition("CMAKE_PKG_CONFIG_DESCRIPTION", package.Description());
  620. mf.AddDefinition("CMAKE_PKG_CONFIG_VERSION", package.Version());
  621. auto make_list = [&](char const* def,
  622. std::vector<cmPkgConfigDependency> const& deps) {
  623. std::vector<cm::string_view> vec;
  624. vec.reserve(deps.size());
  625. for (auto const& dep : deps) {
  626. vec.emplace_back(dep.Name);
  627. }
  628. mf.AddDefinition(def, cmList::to_string(vec));
  629. };
  630. make_list("CMAKE_PKG_CONFIG_CONFLICTS", package.Conflicts());
  631. make_list("CMAKE_PKG_CONFIG_PROVIDES", package.Provides());
  632. make_list("CMAKE_PKG_CONFIG_REQUIRES", package.Requires());
  633. make_list("CMAKE_PKG_CONFIG_REQUIRES_PRIVATE", package.Requires(true));
  634. auto cflags = package.Cflags();
  635. mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS", cflags.Flagline);
  636. mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES",
  637. cmList::to_string(cflags.Includes));
  638. mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS",
  639. cmList::to_string(cflags.CompileOptions));
  640. cflags = package.Cflags(true);
  641. mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS_PRIVATE", cflags.Flagline);
  642. mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES_PRIVATE",
  643. cmList::to_string(cflags.Includes));
  644. mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS_PRIVATE",
  645. cmList::to_string(cflags.CompileOptions));
  646. auto libs = package.Libs();
  647. mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS", libs.Flagline);
  648. mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS",
  649. cmList::to_string(libs.LibDirs));
  650. mf.AddDefinition("CMAKE_PKG_CONFIG_LIBNAMES",
  651. cmList::to_string(libs.LibNames));
  652. mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS",
  653. cmList::to_string(libs.LinkOptions));
  654. libs = package.Libs(true);
  655. mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS_PRIVATE", libs.Flagline);
  656. mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS_PRIVATE",
  657. cmList::to_string(libs.LibDirs));
  658. mf.AddDefinition("CMAKE_PKG_CONFIG_LIBNAMES_PRIVATE",
  659. cmList::to_string(libs.LibNames));
  660. mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS_PRIVATE",
  661. cmList::to_string(libs.LinkOptions));
  662. return true;
  663. }
  664. using pkgStack = std::unordered_map<std::string, std::vector<pkgStackEntry>>;
  665. using pkgProviders = std::unordered_map<std::string, std::string>;
  666. cmTarget* CreateCMakeTarget(std::string const& name, cmPkgConfigResult& pkg,
  667. pkgProviders& providers, cmMakefile& mf)
  668. {
  669. auto* tgt = mf.AddForeignTarget("pkgcfg", name);
  670. tgt->AppendProperty("VERSION", pkg.Version());
  671. auto libs = pkg.Libs();
  672. for (auto const& flag : libs.LibNames) {
  673. tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", flag.substr(2));
  674. }
  675. for (auto const& flag : libs.LibDirs) {
  676. tgt->AppendProperty("INTERFACE_LINK_DIRECTORIES", flag.substr(2));
  677. }
  678. tgt->AppendProperty("INTERFACE_LINK_OPTIONS",
  679. cmList::to_string(libs.LinkOptions));
  680. auto cflags = pkg.Cflags();
  681. for (auto const& flag : cflags.Includes) {
  682. tgt->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES", flag.substr(2));
  683. }
  684. tgt->AppendProperty("INTERFACE_COMPILE_OPTIONS",
  685. cmList::to_string(cflags.CompileOptions));
  686. for (auto& dep : pkg.Requires()) {
  687. auto it = providers.find(dep.Name);
  688. if (it != providers.end()) {
  689. tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", it->second);
  690. continue;
  691. }
  692. tgt->AppendProperty("INTERFACE_LINK_LIBRARIES",
  693. cmStrCat("@foreign_pkgcfg::", dep.Name));
  694. }
  695. return tgt;
  696. }
  697. bool CheckPackageDependencies(
  698. std::string const& name, cmPkgConfigResult& pkg, pkgStack& inStack,
  699. std::unordered_map<std::string, cmPkgConfigResult>& outStack,
  700. pkgProviders& providers, ImportEnv& imEnv)
  701. {
  702. for (auto& dep : pkg.Requires()) {
  703. auto prov_it = providers.find(dep.Name);
  704. if (prov_it != providers.end()) {
  705. continue;
  706. }
  707. auto* tgt = imEnv.status.GetMakefile().FindTargetToUse(
  708. cmStrCat("@foreign_pkgcfg::", dep.Name),
  709. cmStateEnums::TargetDomain::FOREIGN);
  710. if (tgt) {
  711. auto ver = tgt->GetProperty("VERSION");
  712. if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, *ver)) {
  713. warn_or_error(cmStrCat("Package '", dep.Name, "' version '", *ver,
  714. "' does not meet version requirement '",
  715. dep.VerReq.string(), "' ", "of '", name, "'"),
  716. imEnv);
  717. return false;
  718. }
  719. continue;
  720. }
  721. auto it = outStack.find(dep.Name);
  722. if (it != outStack.end()) {
  723. auto ver = it->second.Version();
  724. if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, ver)) {
  725. warn_or_error(cmStrCat("Package '", dep.Name, "' version '", ver,
  726. "' does not meet version requirement '",
  727. dep.VerReq.string(), "' ", "of '", name, "'"),
  728. imEnv);
  729. return false;
  730. }
  731. continue;
  732. }
  733. inStack[dep.Name].emplace_back(
  734. pkgStackEntry{ std::move(dep.VerReq), name });
  735. }
  736. return true;
  737. }
  738. struct PopulateArguments : CommonArguments
  739. {
  740. cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> providers;
  741. };
  742. auto const PopulateParser =
  743. BIND_COMMON(PopulateArguments)
  744. .Bind("BIND_PC_REQUIRES"_s, &PopulateArguments::providers);
  745. std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
  746. cmExecutionStatus& status)
  747. {
  748. auto& mf = status.GetMakefile();
  749. auto maybeEnv = HandleCommon(args, status);
  750. if (!maybeEnv) {
  751. return { !args.Required, false };
  752. }
  753. auto& pcEnv = maybeEnv->first;
  754. auto& imEnv = maybeEnv->second;
  755. pkgProviders providers;
  756. if (args.providers) {
  757. for (auto const& provider_str : *args.providers) {
  758. auto assignment = provider_str.find('=');
  759. if (assignment != std::string::npos) {
  760. providers.emplace(provider_str.substr(0, assignment),
  761. provider_str.substr(assignment + 1));
  762. } else {
  763. imEnv.status.SetError(cmStrCat(
  764. "No '=' found in BIND_PC_REQUIRES argument '", provider_str, "'"));
  765. cmSystemTools::SetFatalErrorOccurred();
  766. return { false, false };
  767. }
  768. }
  769. }
  770. pkgStack inStack;
  771. std::unordered_map<std::string, cmPkgConfigResult> outStack;
  772. auto maybePackage = ImportPackage(*args.Package, args.Version, imEnv, pcEnv);
  773. if (!maybePackage) {
  774. return { !args.Required, false };
  775. }
  776. imEnv.exact = false;
  777. if (!CheckPackageDependencies(*args.Package, *maybePackage, inStack,
  778. outStack, providers, imEnv)) {
  779. return { !args.Required, false };
  780. }
  781. outStack[*args.Package] = std::move(*maybePackage);
  782. while (!inStack.empty()) {
  783. auto name = inStack.begin()->first;
  784. auto reqs = inStack.begin()->second;
  785. maybePackage = ImportPackage(name, reqs, imEnv, pcEnv);
  786. if (!maybePackage) {
  787. return { !args.Required, false };
  788. }
  789. if (!CheckPackageDependencies(name, *maybePackage, inStack, outStack,
  790. providers, imEnv)) {
  791. return { !args.Required, false };
  792. }
  793. inStack.erase(name);
  794. outStack[std::move(name)] = std::move(*maybePackage);
  795. }
  796. for (auto& entry : outStack) {
  797. CreateCMakeTarget(entry.first, entry.second, providers, mf);
  798. }
  799. return { true, true };
  800. }
  801. bool HandlePopulateCommand(std::vector<std::string> const& args,
  802. cmExecutionStatus& status)
  803. {
  804. std::vector<std::string> unparsed;
  805. auto parsedArgs = PopulateParser.Parse(args, &unparsed);
  806. auto foreign_name = cmStrCat("@foreign_pkgcfg::", *parsedArgs.Package);
  807. auto found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND");
  808. auto& mf = status.GetMakefile();
  809. if (mf.FindTargetToUse(foreign_name, cmStateEnums::TargetDomain::FOREIGN)) {
  810. mf.AddDefinition(found_var, "TRUE");
  811. return true;
  812. }
  813. auto result = PopulatePCTarget(parsedArgs, status);
  814. mf.AddDefinition(found_var, result.second ? "TRUE" : "FALSE");
  815. return result.first;
  816. }
  817. bool HandleImportCommand(std::vector<std::string> const& args,
  818. cmExecutionStatus& status)
  819. {
  820. std::vector<std::string> unparsed;
  821. auto parsedArgs = PopulateParser.Parse(args, &unparsed);
  822. auto foreign_name = cmStrCat("@foreign_pkgcfg::", *parsedArgs.Package);
  823. auto local_name = cmStrCat("PkgConfig::", *parsedArgs.Package);
  824. auto found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND");
  825. auto& mf = status.GetMakefile();
  826. if (mf.FindTargetToUse(local_name)) {
  827. mf.AddDefinition(found_var, "TRUE");
  828. return true;
  829. }
  830. if (!mf.FindTargetToUse(foreign_name, cmStateEnums::TargetDomain::FOREIGN)) {
  831. auto result = PopulatePCTarget(parsedArgs, status);
  832. if (!result.second) {
  833. mf.AddDefinition(found_var, "FALSE");
  834. return result.first;
  835. }
  836. }
  837. mf.AddDefinition(found_var, "TRUE");
  838. auto* tgt = mf.AddImportedTarget(
  839. local_name, cmStateEnums::TargetType::INTERFACE_LIBRARY, false);
  840. tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", foreign_name);
  841. return true;
  842. }
  843. } // namespace
  844. bool cmCMakePkgConfigCommand(std::vector<std::string> const& args,
  845. cmExecutionStatus& status)
  846. {
  847. if (args.size() < 2) {
  848. status.SetError("must be called with at least two arguments.");
  849. return false;
  850. }
  851. static cmSubcommandTable const subcommand{
  852. { "EXTRACT"_s, HandleExtractCommand },
  853. { "POPULATE"_s, HandlePopulateCommand },
  854. { "IMPORT"_s, HandleImportCommand },
  855. };
  856. return subcommand(args[0], args, status);
  857. }