cmPackageInfoReader.cxx 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  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 "cmPackageInfoReader.h"
  4. #include <algorithm>
  5. #include <initializer_list>
  6. #include <limits>
  7. #include <unordered_map>
  8. #include <utility>
  9. #include <cmext/algorithm>
  10. #include <cmext/string_view>
  11. #include <cm3p/json/reader.h>
  12. #include <cm3p/json/value.h>
  13. #include <cm3p/json/version.h>
  14. #include "cmsys/FStream.hxx"
  15. #include "cmsys/RegularExpression.hxx"
  16. #include "cmExecutionStatus.h"
  17. #include "cmList.h"
  18. #include "cmListFileCache.h"
  19. #include "cmMakefile.h"
  20. #include "cmMessageType.h"
  21. #include "cmStringAlgorithms.h"
  22. #include "cmSystemTools.h"
  23. #include "cmTarget.h"
  24. #include "cmValue.h"
  25. namespace {
  26. // Map of CPS language names to CMake language name. Case insensitivity is
  27. // achieved by converting the CPS value to lower case, so keys in this map must
  28. // be lower case.
  29. std::unordered_map<std::string, std::string> Languages = {
  30. // clang-format off
  31. { "c", "C" },
  32. { "c++", "CXX" },
  33. { "cpp", "CXX" },
  34. { "cxx", "CXX" },
  35. { "objc", "OBJC" },
  36. { "objc++", "OBJCXX" },
  37. { "objcpp", "OBJCXX" },
  38. { "objcxx", "OBJCXX" },
  39. { "swift", "swift" },
  40. { "hip", "HIP" },
  41. { "cuda", "CUDA" },
  42. { "ispc", "ISPC" },
  43. { "c#", "CSharp" },
  44. { "csharp", "CSharp" },
  45. { "fortran", "Fortran" },
  46. // clang-format on
  47. };
  48. enum LanguageGlobOption
  49. {
  50. DisallowGlob,
  51. AllowGlob,
  52. };
  53. cm::string_view MapLanguage(cm::string_view lang,
  54. LanguageGlobOption glob = AllowGlob)
  55. {
  56. if (glob == AllowGlob && lang == "*"_s) {
  57. return "*"_s;
  58. }
  59. auto const li = Languages.find(cmSystemTools::LowerCase(lang));
  60. if (li != Languages.end()) {
  61. return li->second;
  62. }
  63. return {};
  64. }
  65. std::string GetRealPath(std::string const& path)
  66. {
  67. return cmSystemTools::GetRealPath(path);
  68. }
  69. std::string GetRealDir(std::string const& path)
  70. {
  71. return cmSystemTools::GetFilenamePath(cmSystemTools::GetRealPath(path));
  72. }
  73. Json::Value ReadJson(std::string const& fileName)
  74. {
  75. // Open the specified file.
  76. cmsys::ifstream file(fileName.c_str(), std::ios::in | std::ios::binary);
  77. if (!file) {
  78. #if JSONCPP_VERSION_HEXA < 0x01070300
  79. return Json::Value::null;
  80. #else
  81. return Json::Value::nullSingleton();
  82. #endif
  83. }
  84. // Read file content and translate JSON.
  85. Json::Value data;
  86. Json::CharReaderBuilder builder;
  87. builder["collectComments"] = false;
  88. if (!Json::parseFromStream(builder, file, &data, nullptr)) {
  89. #if JSONCPP_VERSION_HEXA < 0x01070300
  90. return Json::Value::null;
  91. #else
  92. return Json::Value::nullSingleton();
  93. #endif
  94. }
  95. return data;
  96. }
  97. std::string ToString(Json::Value const& value)
  98. {
  99. if (value.isString()) {
  100. return value.asString();
  101. }
  102. return {};
  103. }
  104. bool CheckSchemaVersion(Json::Value const& data)
  105. {
  106. std::string const& version = ToString(data["cps_version"]);
  107. // Check that a valid version is specified.
  108. if (version.empty()) {
  109. return false;
  110. }
  111. // Check that we understand this version.
  112. return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
  113. version, "0.13") &&
  114. cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, version, "1");
  115. // TODO Eventually this probably needs to return the version tuple, and
  116. // should share code with cmPackageInfoReader::ParseVersion.
  117. }
  118. bool ComparePathSuffix(std::string const& path, std::string const& suffix)
  119. {
  120. std::string const& tail = path.substr(path.size() - suffix.size());
  121. return cmSystemTools::ComparePath(tail, suffix);
  122. }
  123. std::string DeterminePrefix(std::string const& filepath,
  124. Json::Value const& data)
  125. {
  126. // First check if an absolute prefix was supplied.
  127. std::string prefix = ToString(data["prefix"]);
  128. if (!prefix.empty()) {
  129. // Ensure that the specified prefix is valid.
  130. if (cmsys::SystemTools::FileIsFullPath(prefix) &&
  131. cmsys::SystemTools::FileIsDirectory(prefix)) {
  132. cmSystemTools::ConvertToUnixSlashes(prefix);
  133. return prefix;
  134. }
  135. // The specified absolute prefix is not valid.
  136. return {};
  137. }
  138. // Get and validate prefix-relative path.
  139. std::string const& absPath = cmSystemTools::GetFilenamePath(filepath);
  140. std::string relPath = ToString(data["cps_path"]);
  141. cmSystemTools::ConvertToUnixSlashes(relPath);
  142. if (relPath.empty() || !cmHasLiteralPrefix(relPath, "@prefix@")) {
  143. // The relative prefix is not valid.
  144. return {};
  145. }
  146. if (relPath.size() == 8) {
  147. // The relative path is exactly "@prefix@".
  148. return absPath;
  149. }
  150. if (relPath[8] != '/') {
  151. // The relative prefix is not valid.
  152. return {};
  153. }
  154. relPath = relPath.substr(8);
  155. // Get directory portion of the absolute path.
  156. if (ComparePathSuffix(absPath, relPath)) {
  157. return absPath.substr(0, absPath.size() - relPath.size());
  158. }
  159. for (auto* const f : { GetRealPath, GetRealDir }) {
  160. std::string const& tmpPath = (*f)(absPath);
  161. if (!cmSystemTools::ComparePath(tmpPath, absPath) &&
  162. ComparePathSuffix(tmpPath, relPath)) {
  163. return tmpPath.substr(0, tmpPath.size() - relPath.size());
  164. }
  165. }
  166. return {};
  167. }
  168. // Extract key name from value iterator as string_view.
  169. cm::string_view IterKey(Json::Value::const_iterator iter)
  170. {
  171. char const* end;
  172. char const* const start = iter.memberName(&end);
  173. return { start, static_cast<std::string::size_type>(end - start) };
  174. }
  175. // Get list-of-strings value from object.
  176. std::vector<std::string> ReadList(Json::Value const& arr)
  177. {
  178. std::vector<std::string> result;
  179. if (arr.isArray()) {
  180. for (Json::Value const& val : arr) {
  181. if (val.isString()) {
  182. result.push_back(val.asString());
  183. }
  184. }
  185. }
  186. return result;
  187. }
  188. std::vector<std::string> ReadList(Json::Value const& data, char const* key)
  189. {
  190. return ReadList(data[key]);
  191. }
  192. std::string NormalizeTargetName(std::string const& name,
  193. std::string const& context)
  194. {
  195. if (cmHasPrefix(name, ':')) {
  196. return cmStrCat(context, ':', name);
  197. }
  198. std::string::size_type const n = name.find_first_of(':');
  199. if (n != std::string::npos) {
  200. cm::string_view v{ name };
  201. return cmStrCat(v.substr(0, n), ':', v.substr(n));
  202. }
  203. return name;
  204. }
  205. void AppendProperty(cmMakefile* makefile, cmTarget* target,
  206. cm::string_view property, cm::string_view configuration,
  207. std::string const& value)
  208. {
  209. std::string const fullprop = cmStrCat("INTERFACE_", property);
  210. if (!configuration.empty()) {
  211. std::string const genexValue =
  212. cmStrCat("$<$<CONFIG:", configuration, ">:", value, '>');
  213. target->AppendProperty(fullprop, genexValue, makefile->GetBacktrace());
  214. } else {
  215. target->AppendProperty(fullprop, value, makefile->GetBacktrace());
  216. }
  217. }
  218. template <typename Transform>
  219. void AppendLanguageProperties(cmMakefile* makefile, cmTarget* target,
  220. cm::string_view property,
  221. cm::string_view configuration,
  222. Json::Value const& data, char const* key,
  223. Transform transform)
  224. {
  225. Json::Value const& value = data[key];
  226. if (value.isArray()) {
  227. for (std::string v : ReadList(value)) {
  228. AppendProperty(makefile, target, property, configuration,
  229. transform(std::move(v)));
  230. }
  231. } else if (value.isObject()) {
  232. for (auto vi = value.begin(), ve = value.end(); vi != ve; ++vi) {
  233. cm::string_view const originalLang = IterKey(vi);
  234. cm::string_view const lang = MapLanguage(originalLang);
  235. if (lang.empty()) {
  236. makefile->IssueMessage(MessageType::WARNING,
  237. cmStrCat(R"(ignoring unknown language ")"_s,
  238. originalLang, R"(" in )"_s, key,
  239. " for "_s, target->GetName()));
  240. continue;
  241. }
  242. if (lang == "*"_s) {
  243. for (std::string v : ReadList(*vi)) {
  244. AppendProperty(makefile, target, property, configuration,
  245. transform(std::move(v)));
  246. }
  247. } else {
  248. for (std::string v : ReadList(*vi)) {
  249. v = cmStrCat("$<$<COMPILE_LANGUAGE:"_s, lang, ">:"_s,
  250. transform(std::move(v)), '>');
  251. AppendProperty(makefile, target, property, configuration, v);
  252. }
  253. }
  254. }
  255. }
  256. }
  257. void AddCompileFeature(cmMakefile* makefile, cmTarget* target,
  258. cm::string_view configuration, std::string const& value)
  259. {
  260. auto reLanguageLevel = []() -> cmsys::RegularExpression {
  261. static cmsys::RegularExpression re{ "^[Cc]([+][+])?([0-9][0-9])$" };
  262. return re;
  263. }();
  264. if (reLanguageLevel.find(value)) {
  265. std::string::size_type const n = reLanguageLevel.end() - 2;
  266. cm::string_view const featurePrefix = (n == 3 ? "cxx_std_"_s : "c_std_"_s);
  267. if (configuration.empty()) {
  268. AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {},
  269. cmStrCat(featurePrefix, value.substr(n)));
  270. } else {
  271. std::string const& feature =
  272. cmStrCat("$<$<CONFIG:"_s, configuration, ">:"_s, featurePrefix,
  273. value.substr(n), '>');
  274. AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {}, feature);
  275. }
  276. } else if (cmStrCaseEq(value, "gnu"_s)) {
  277. // Not implemented in CMake at this time
  278. } else if (cmStrCaseEq(value, "threads"_s)) {
  279. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
  280. "Threads::Threads");
  281. }
  282. }
  283. void AddLinkFeature(cmMakefile* makefile, cmTarget* target,
  284. cm::string_view configuration, std::string const& value)
  285. {
  286. if (cmStrCaseEq(value, "thread"_s)) {
  287. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
  288. "Threads::Threads");
  289. }
  290. }
  291. std::string BuildDefinition(std::string const& name, Json::Value const& value)
  292. {
  293. if (!value.isNull() && value.isConvertibleTo(Json::stringValue)) {
  294. return cmStrCat(name, '=', value.asString());
  295. }
  296. return name;
  297. }
  298. void AddDefinition(cmMakefile* makefile, cmTarget* target,
  299. cm::string_view configuration,
  300. std::string const& definition)
  301. {
  302. AppendProperty(makefile, target, "COMPILE_DEFINITIONS"_s, configuration,
  303. definition);
  304. }
  305. using DefinitionLanguageMap = std::map<cm::string_view, Json::Value>;
  306. using DefinitionsMap = std::map<std::string, DefinitionLanguageMap>;
  307. void AddDefinitions(cmMakefile* makefile, cmTarget* target,
  308. cm::string_view configuration,
  309. DefinitionsMap const& definitions)
  310. {
  311. for (auto const& di : definitions) {
  312. auto const& g = di.second.find("*"_s);
  313. if (g != di.second.end()) {
  314. std::string const& def = BuildDefinition(di.first, g->second);
  315. if (di.second.size() == 1) {
  316. // Only the non-language-specific definition exists.
  317. AddDefinition(makefile, target, configuration, def);
  318. continue;
  319. }
  320. // Create a genex to apply this definition to all languages except
  321. // those that override it.
  322. std::vector<cm::string_view> excludedLanguages;
  323. for (auto const& li : di.second) {
  324. if (li.first != "*"_s) {
  325. excludedLanguages.emplace_back(li.first);
  326. }
  327. }
  328. AddDefinition(makefile, target, configuration,
  329. cmStrCat("$<$<NOT:$<COMPILE_LANGUAGE:"_s,
  330. cmJoin(excludedLanguages, ","_s), ">>:"_s, def,
  331. '>'));
  332. }
  333. // Add language-specific definitions.
  334. for (auto const& li : di.second) {
  335. if (li.first != "*"_s) {
  336. AddDefinition(makefile, target, configuration,
  337. cmStrCat("$<$<COMPILE_LANGUAGE:"_s, li.first, ">:"_s,
  338. BuildDefinition(di.first, li.second), '>'));
  339. }
  340. }
  341. }
  342. }
  343. cm::optional<cmPackageInfoReader::Pep440Version> ParseSimpleVersion(
  344. std::string const& version)
  345. {
  346. if (version.empty()) {
  347. return cm::nullopt;
  348. }
  349. cmPackageInfoReader::Pep440Version result;
  350. result.Simple = true;
  351. cm::string_view remnant{ version };
  352. for (;;) {
  353. // Find the next part separator.
  354. std::string::size_type const n = remnant.find_first_of(".+-"_s);
  355. if (n == 0) {
  356. // The part is an empty string.
  357. return cm::nullopt;
  358. }
  359. // Extract the part as a number.
  360. cm::string_view const part = remnant.substr(0, n);
  361. std::string::size_type const l = part.size();
  362. std::string::size_type p;
  363. unsigned long const value = std::stoul(std::string{ part }, &p);
  364. if (p != l || value > std::numeric_limits<unsigned>::max()) {
  365. // The part was not a valid number or is too big.
  366. return cm::nullopt;
  367. }
  368. result.ReleaseComponents.push_back(static_cast<unsigned>(value));
  369. // Have we consumed the entire input?
  370. if (n == std::string::npos) {
  371. return { std::move(result) };
  372. }
  373. // Lop off the current part.
  374. char const sep = remnant[n];
  375. remnant = remnant.substr(n + 1);
  376. if (sep == '+' || sep == '-') {
  377. // If we hit the local label, we're done.
  378. result.LocalLabel = remnant;
  379. return { std::move(result) };
  380. }
  381. // We just consumed a '.'; check that there's more.
  382. if (remnant.empty()) {
  383. // A trailing part separator is not allowed.
  384. return cm::nullopt;
  385. }
  386. // Continue with the remaining input.
  387. }
  388. // Unreachable.
  389. }
  390. } // namespace
  391. std::unique_ptr<cmPackageInfoReader> cmPackageInfoReader::Read(
  392. cmMakefile* makefile, std::string const& path,
  393. cmPackageInfoReader const* parent)
  394. {
  395. // Read file and perform some basic validation:
  396. // - the input is valid JSON
  397. // - the input is a JSON object
  398. // - the input has a "cps_version" that we (in theory) know how to parse
  399. Json::Value data = ReadJson(path);
  400. if (!data.isObject() || (!parent && !CheckSchemaVersion(data))) {
  401. return nullptr;
  402. }
  403. // - the input has a "name" attribute that is a non-empty string
  404. Json::Value const& name = data["name"];
  405. if (!name.isString() || name.empty()) {
  406. return nullptr;
  407. }
  408. // - the input has a "components" attribute that is a JSON object
  409. if (!data["components"].isObject()) {
  410. return nullptr;
  411. }
  412. std::string prefix = (parent ? parent->Prefix : DeterminePrefix(path, data));
  413. if (prefix.empty()) {
  414. return nullptr;
  415. }
  416. // Seems sane enough to hand back to the caller.
  417. std::unique_ptr<cmPackageInfoReader> reader{ new cmPackageInfoReader };
  418. reader->Data = std::move(data);
  419. reader->Prefix = std::move(prefix);
  420. reader->Path = path;
  421. // Determine other information we need to know immediately, or (if this is
  422. // a supplemental reader) copy from the parent.
  423. if (parent) {
  424. reader->ComponentTargets = parent->ComponentTargets;
  425. reader->DefaultConfigurations = parent->DefaultConfigurations;
  426. } else {
  427. for (std::string const& config :
  428. ReadList(reader->Data, "configurations")) {
  429. reader->DefaultConfigurations.emplace_back(
  430. cmSystemTools::UpperCase(config));
  431. }
  432. }
  433. // Check for a default license.
  434. Json::Value const& defaultLicense = reader->Data["default_license"];
  435. if (!defaultLicense.isNull()) {
  436. if (defaultLicense.isString()) {
  437. reader->DefaultLicense = defaultLicense.asString();
  438. } else {
  439. makefile->IssueMessage(
  440. MessageType::WARNING,
  441. "Package attribute \"default_license\" is not a string.");
  442. }
  443. } else if (parent) {
  444. reader->DefaultLicense = parent->DefaultLicense;
  445. } else {
  446. // If there is no 'default_license', check for 'license'. Note that we
  447. // intentionally allow `default_license` on an appendix to override the
  448. // parent, but we do not consider `license` on an appendix. This is
  449. // consistent with not allowing LICENSE and APPENDIX to be used together.
  450. Json::Value const& packageLicense = reader->Data["license"];
  451. if (!packageLicense.isNull()) {
  452. if (packageLicense.isString()) {
  453. reader->DefaultLicense = packageLicense.asString();
  454. } else {
  455. makefile->IssueMessage(
  456. MessageType::WARNING,
  457. "Package attribute \"license\" is not a string.");
  458. }
  459. }
  460. }
  461. return reader;
  462. }
  463. std::string cmPackageInfoReader::GetName() const
  464. {
  465. return ToString(this->Data["name"]);
  466. }
  467. cm::optional<std::string> cmPackageInfoReader::GetVersion() const
  468. {
  469. Json::Value const& version = this->Data["version"];
  470. if (version.isString()) {
  471. return version.asString();
  472. }
  473. return cm::nullopt;
  474. }
  475. cm::optional<std::string> cmPackageInfoReader::GetCompatVersion() const
  476. {
  477. Json::Value const& version = this->Data["compat_version"];
  478. if (version.isString()) {
  479. return version.asString();
  480. }
  481. return cm::nullopt;
  482. }
  483. cm::optional<cmPackageInfoReader::Pep440Version>
  484. cmPackageInfoReader::ParseVersion(
  485. cm::optional<std::string> const& version) const
  486. {
  487. // Check that we have a version.
  488. if (!version) {
  489. return cm::nullopt;
  490. }
  491. // Check if we know how to parse the version.
  492. Json::Value const& schema = this->Data["version_schema"];
  493. if (schema.isNull() || cmStrCaseEq(ToString(schema), "simple"_s)) {
  494. return ParseSimpleVersion(*version);
  495. }
  496. return cm::nullopt;
  497. }
  498. std::vector<cmPackageRequirement> cmPackageInfoReader::GetRequirements() const
  499. {
  500. std::vector<cmPackageRequirement> requirements;
  501. auto const& requirementObjects = this->Data["requires"];
  502. for (auto ri = requirementObjects.begin(), re = requirementObjects.end();
  503. ri != re; ++ri) {
  504. cmPackageRequirement r{ ri.name(), ToString((*ri)["version"]),
  505. ReadList(*ri, "components"),
  506. ReadList(*ri, "hints") };
  507. requirements.emplace_back(std::move(r));
  508. }
  509. return requirements;
  510. }
  511. std::vector<std::string> cmPackageInfoReader::GetComponentNames() const
  512. {
  513. std::vector<std::string> componentNames;
  514. Json::Value const& components = this->Data["components"];
  515. for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
  516. componentNames.emplace_back(ci.name());
  517. }
  518. return componentNames;
  519. }
  520. std::string cmPackageInfoReader::ResolvePath(std::string path) const
  521. {
  522. cmSystemTools::ConvertToUnixSlashes(path);
  523. if (cmHasPrefix(path, "@prefix@"_s)) {
  524. return cmStrCat(this->Prefix, path.substr(8));
  525. }
  526. if (!cmSystemTools::FileIsFullPath(path)) {
  527. return cmStrCat(cmSystemTools::GetFilenamePath(this->Path), '/', path);
  528. }
  529. return path;
  530. }
  531. void cmPackageInfoReader::AddTargetConfiguration(
  532. cmTarget* target, cm::string_view configuration) const
  533. {
  534. static std::string const icProp = "IMPORTED_CONFIGURATIONS";
  535. std::string const& configUpper = cmSystemTools::UpperCase(configuration);
  536. // Get existing list of imported configurations.
  537. cmList configs;
  538. if (cmValue v = target->GetProperty(icProp)) {
  539. configs.assign(cmSystemTools::UpperCase(*v));
  540. } else {
  541. // If the existing list is empty, just add the new one and return.
  542. target->SetProperty(icProp, configUpper);
  543. return;
  544. }
  545. if (cm::contains(configs, configUpper)) {
  546. // If the configuration is already listed, we don't need to do anything.
  547. return;
  548. }
  549. // Add the new configuration.
  550. configs.append(configUpper);
  551. // Rebuild the configuration list by extracting any configuration in the
  552. // default configurations and reinserting it at the beginning of the list
  553. // according to the order of the default configurations.
  554. std::vector<std::string> newConfigs;
  555. for (std::string const& c : this->DefaultConfigurations) {
  556. auto ci = std::find(configs.begin(), configs.end(), c);
  557. if (ci != configs.end()) {
  558. newConfigs.emplace_back(std::move(*ci));
  559. configs.erase(ci);
  560. }
  561. }
  562. for (std::string& c : configs) {
  563. newConfigs.emplace_back(std::move(c));
  564. }
  565. target->SetProperty("IMPORTED_CONFIGURATIONS", cmJoin(newConfigs, ";"_s));
  566. }
  567. void cmPackageInfoReader::SetImportProperty(cmMakefile* makefile,
  568. cmTarget* target,
  569. cm::string_view property,
  570. cm::string_view configuration,
  571. Json::Value const& object,
  572. std::string const& attribute) const
  573. {
  574. Json::Value const& value = object[attribute];
  575. if (!value.isNull()) {
  576. std::string fullprop;
  577. if (configuration.empty()) {
  578. fullprop = cmStrCat("IMPORTED_"_s, property);
  579. } else {
  580. fullprop = cmStrCat("IMPORTED_"_s, property, '_',
  581. cmSystemTools::UpperCase(configuration));
  582. }
  583. if (value.isString()) {
  584. target->SetProperty(fullprop, this->ResolvePath(value.asString()));
  585. } else {
  586. makefile->IssueMessage(MessageType::WARNING,
  587. cmStrCat("Failed to set property \""_s, property,
  588. "\" on target \""_s, target->GetName(),
  589. "\": attribute \"", attribute,
  590. "\" is not a string."_s));
  591. }
  592. }
  593. }
  594. void cmPackageInfoReader::SetMetaProperty(
  595. cmMakefile* makefile, cmTarget* target, std::string const& property,
  596. Json::Value const& object, std::string const& attribute,
  597. std::string const& defaultValue) const
  598. {
  599. Json::Value const& value = object[attribute];
  600. if (!value.isNull()) {
  601. if (value.isString()) {
  602. target->SetProperty(property, value.asString());
  603. } else {
  604. makefile->IssueMessage(MessageType::WARNING,
  605. cmStrCat("Failed to set property \""_s, property,
  606. "\" on target \""_s, target->GetName(),
  607. "\": attribute \"", attribute,
  608. "\" is not a string."_s));
  609. }
  610. } else if (!defaultValue.empty()) {
  611. target->SetProperty(property, defaultValue);
  612. }
  613. }
  614. void cmPackageInfoReader::SetTargetProperties(
  615. cmMakefile* makefile, cmTarget* target, Json::Value const& data,
  616. std::string const& package, cm::string_view configuration) const
  617. {
  618. // Add configuration (if applicable).
  619. if (!configuration.empty()) {
  620. this->AddTargetConfiguration(target, configuration);
  621. }
  622. // Add compile and link features.
  623. for (std::string const& def : ReadList(data, "compile_features")) {
  624. AddCompileFeature(makefile, target, configuration, def);
  625. }
  626. for (std::string const& def : ReadList(data, "link_features")) {
  627. AddLinkFeature(makefile, target, configuration, def);
  628. }
  629. // Add compile definitions.
  630. Json::Value const& defs = data["definitions"];
  631. DefinitionsMap definitionsMap;
  632. for (auto ldi = defs.begin(), lde = defs.end(); ldi != lde; ++ldi) {
  633. cm::string_view const originalLang = IterKey(ldi);
  634. cm::string_view const lang = MapLanguage(originalLang);
  635. if (lang.empty()) {
  636. makefile->IssueMessage(
  637. MessageType::WARNING,
  638. cmStrCat(R"(ignoring unknown language ")"_s, originalLang,
  639. R"(" in definitions for )"_s, target->GetName()));
  640. continue;
  641. }
  642. for (auto di = ldi->begin(), de = ldi->end(); di != de; ++di) {
  643. definitionsMap[di.name()].emplace(lang, *di);
  644. }
  645. }
  646. AddDefinitions(makefile, target, configuration, definitionsMap);
  647. // Add include directories.
  648. AppendLanguageProperties(makefile, target, "INCLUDE_DIRECTORIES"_s,
  649. configuration, data, "includes",
  650. [this](std::string p) -> std::string {
  651. return this->ResolvePath(std::move(p));
  652. });
  653. // Add link name/location(s).
  654. this->SetImportProperty(makefile, target, "LOCATION"_s, // br
  655. configuration, data, "location");
  656. this->SetImportProperty(makefile, target, "IMPLIB"_s, // br
  657. configuration, data, "link_location");
  658. this->SetImportProperty(makefile, target, "SONAME"_s, // br
  659. configuration, data, "link_name");
  660. // Add link languages.
  661. for (std::string const& originalLang : ReadList(data, "link_languages")) {
  662. cm::string_view const lang = MapLanguage(originalLang, DisallowGlob);
  663. if (!lang.empty()) {
  664. AppendProperty(makefile, target, "LINK_LANGUAGES"_s, configuration,
  665. std::string{ lang });
  666. }
  667. }
  668. // Add transitive dependencies.
  669. for (std::string const& dep : ReadList(data, "requires")) {
  670. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
  671. NormalizeTargetName(dep, package));
  672. }
  673. for (std::string const& dep : ReadList(data, "link_requires")) {
  674. std::string const& lib =
  675. cmStrCat("$<LINK_ONLY:"_s, NormalizeTargetName(dep, package), '>');
  676. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib);
  677. }
  678. // Add other information.
  679. if (configuration.empty()) {
  680. this->SetMetaProperty(makefile, target, "SPDX_LICENSE", data, "license",
  681. this->DefaultLicense);
  682. }
  683. }
  684. cmTarget* cmPackageInfoReader::AddLibraryComponent(
  685. cmMakefile* makefile, cmStateEnums::TargetType type, std::string const& name,
  686. Json::Value const& data, std::string const& package) const
  687. {
  688. // Create the imported target.
  689. cmTarget* const target = makefile->AddImportedTarget(name, type, false);
  690. target->SetOrigin(cmTarget::Origin::Cps);
  691. // Set target properties.
  692. this->SetTargetProperties(makefile, target, data, package, {});
  693. auto const& cfgData = data["configurations"];
  694. for (auto ci = cfgData.begin(), ce = cfgData.end(); ci != ce; ++ci) {
  695. this->SetTargetProperties(makefile, target, *ci, package, IterKey(ci));
  696. }
  697. return target;
  698. }
  699. bool cmPackageInfoReader::ImportTargets(cmMakefile* makefile,
  700. cmExecutionStatus& status)
  701. {
  702. std::string const& package = this->GetName();
  703. // Read components.
  704. Json::Value const& components = this->Data["components"];
  705. for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
  706. cm::string_view const name = IterKey(ci);
  707. std::string const& type =
  708. cmSystemTools::LowerCase(ToString((*ci)["type"]));
  709. // Get and validate full target name.
  710. std::string const& fullName = cmStrCat(package, "::"_s, name);
  711. {
  712. std::string msg;
  713. if (!makefile->EnforceUniqueName(fullName, msg)) {
  714. status.SetError(msg);
  715. return false;
  716. }
  717. }
  718. cmTarget* target = nullptr;
  719. if (type == "symbolic"_s) {
  720. target = this->AddLibraryComponent(
  721. makefile, cmStateEnums::INTERFACE_LIBRARY, fullName, *ci, package);
  722. target->SetSymbolic(true);
  723. } else if (type == "dylib"_s) {
  724. target = this->AddLibraryComponent(
  725. makefile, cmStateEnums::SHARED_LIBRARY, fullName, *ci, package);
  726. } else if (type == "module"_s) {
  727. target = this->AddLibraryComponent(
  728. makefile, cmStateEnums::MODULE_LIBRARY, fullName, *ci, package);
  729. } else if (type == "archive"_s) {
  730. target = this->AddLibraryComponent(
  731. makefile, cmStateEnums::STATIC_LIBRARY, fullName, *ci, package);
  732. } else if (type == "interface"_s) {
  733. target = this->AddLibraryComponent(
  734. makefile, cmStateEnums::INTERFACE_LIBRARY, fullName, *ci, package);
  735. } else {
  736. makefile->IssueMessage(MessageType::WARNING,
  737. cmStrCat(R"(component ")"_s, fullName,
  738. R"(" has unknown type ")"_s, type,
  739. R"(" and was not imported)"_s));
  740. }
  741. if (target) {
  742. this->ComponentTargets.emplace(std::string{ name }, target);
  743. }
  744. }
  745. // Read default components.
  746. std::vector<std::string> const& defaultComponents =
  747. ReadList(this->Data, "default_components");
  748. if (!defaultComponents.empty()) {
  749. std::string msg;
  750. if (!makefile->EnforceUniqueName(package, msg)) {
  751. status.SetError(msg);
  752. return false;
  753. }
  754. cmTarget* const target = makefile->AddImportedTarget(
  755. package, cmStateEnums::INTERFACE_LIBRARY, false);
  756. for (std::string const& name : defaultComponents) {
  757. std::string const& fullName = cmStrCat(package, "::"_s, name);
  758. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, {}, fullName);
  759. }
  760. }
  761. return true;
  762. }
  763. bool cmPackageInfoReader::ImportTargetConfigurations(
  764. cmMakefile* makefile, cmExecutionStatus& status) const
  765. {
  766. std::string const& configuration = ToString(this->Data["configuration"]);
  767. if (configuration.empty()) {
  768. makefile->IssueMessage(MessageType::WARNING,
  769. cmStrCat("supplemental file "_s, this->Path,
  770. " does not specify a configuration"_s));
  771. return true;
  772. }
  773. std::string const& package = this->GetName();
  774. Json::Value const& components = this->Data["components"];
  775. for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
  776. // Get component name and look up target.
  777. cm::string_view const name = IterKey(ci);
  778. auto const& ti = this->ComponentTargets.find(std::string{ name });
  779. if (ti == this->ComponentTargets.end()) {
  780. status.SetError(cmStrCat("component "_s, name, " was not found"_s));
  781. return false;
  782. }
  783. // Read supplemental data for component.
  784. this->SetTargetProperties(makefile, ti->second, *ci, package,
  785. configuration);
  786. }
  787. return true;
  788. }