cmCMakePkgConfigCommand.cxx 31 KB

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