cmPackageInfoReader.cxx 26 KB

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