cmPackageInfoReader.cxx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmPackageInfoReader.h"
  4. #include <initializer_list>
  5. #include <limits>
  6. #include <unordered_map>
  7. #include <utility>
  8. #include <cmext/string_view>
  9. #include <cm3p/json/reader.h>
  10. #include <cm3p/json/value.h>
  11. #include <cm3p/json/version.h>
  12. #include "cmsys/FStream.hxx"
  13. #include "cmsys/RegularExpression.hxx"
  14. #include "cmExecutionStatus.h"
  15. #include "cmListFileCache.h"
  16. #include "cmMakefile.h"
  17. #include "cmMessageType.h"
  18. #include "cmStringAlgorithms.h"
  19. #include "cmSystemTools.h"
  20. #include "cmTarget.h"
  21. namespace {
  22. // Map of CPS language names to CMake language name. Case insensitivity is
  23. // achieved by converting the CPS value to lower case, so keys in this map must
  24. // be lower case.
  25. std::unordered_map<std::string, std::string> Languages = {
  26. // clang-format off
  27. { "c", "C" },
  28. { "c++", "CXX" },
  29. { "cpp", "CXX" },
  30. { "cxx", "CXX" },
  31. { "objc", "OBJC" },
  32. { "objc++", "OBJCXX" },
  33. { "objcpp", "OBJCXX" },
  34. { "objcxx", "OBJCXX" },
  35. { "swift", "swift" },
  36. { "hip", "HIP" },
  37. { "cuda", "CUDA" },
  38. { "ispc", "ISPC" },
  39. { "c#", "CSharp" },
  40. { "csharp", "CSharp" },
  41. { "fortran", "Fortran" },
  42. // clang-format on
  43. };
  44. enum LanguageGlobOption
  45. {
  46. DisallowGlob,
  47. AllowGlob,
  48. };
  49. cm::string_view MapLanguage(cm::string_view lang,
  50. LanguageGlobOption glob = AllowGlob)
  51. {
  52. if (glob == AllowGlob && lang == "*"_s) {
  53. return "*"_s;
  54. }
  55. auto const li = Languages.find(cmSystemTools::LowerCase(lang));
  56. if (li != Languages.end()) {
  57. return li->second;
  58. }
  59. return {};
  60. }
  61. std::string GetRealPath(std::string const& path)
  62. {
  63. return cmSystemTools::GetRealPath(path);
  64. }
  65. std::string GetRealDir(std::string const& path)
  66. {
  67. return cmSystemTools::GetFilenamePath(cmSystemTools::GetRealPath(path));
  68. }
  69. Json::Value ReadJson(std::string const& fileName)
  70. {
  71. // Open the specified file.
  72. cmsys::ifstream file(fileName.c_str(), std::ios::in | std::ios::binary);
  73. if (!file) {
  74. #if JSONCPP_VERSION_HEXA < 0x01070300
  75. return Json::Value::null;
  76. #else
  77. return Json::Value::nullSingleton();
  78. #endif
  79. }
  80. // Read file content and translate JSON.
  81. Json::Value data;
  82. Json::CharReaderBuilder builder;
  83. builder["collectComments"] = false;
  84. if (!Json::parseFromStream(builder, file, &data, nullptr)) {
  85. #if JSONCPP_VERSION_HEXA < 0x01070300
  86. return Json::Value::null;
  87. #else
  88. return Json::Value::nullSingleton();
  89. #endif
  90. }
  91. return data;
  92. }
  93. bool CheckSchemaVersion(Json::Value const& data)
  94. {
  95. std::string const& version = data["cps_version"].asString();
  96. // Check that a valid version is specified.
  97. if (version.empty()) {
  98. return false;
  99. }
  100. // Check that we understand this version.
  101. return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
  102. version, "0.13") &&
  103. cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, version, "1");
  104. // TODO Eventually this probably needs to return the version tuple, and
  105. // should share code with cmPackageInfoReader::ParseVersion.
  106. }
  107. bool ComparePathSuffix(std::string const& path, std::string const& suffix)
  108. {
  109. std::string const& tail = path.substr(path.size() - suffix.size());
  110. return cmSystemTools::ComparePath(tail, suffix);
  111. }
  112. std::string DeterminePrefix(std::string const& filepath,
  113. Json::Value const& data)
  114. {
  115. // First check if an absolute prefix was supplied.
  116. std::string prefix = data["prefix"].asString();
  117. if (!prefix.empty()) {
  118. // Ensure that the specified prefix is valid.
  119. if (cmsys::SystemTools::FileIsFullPath(prefix) &&
  120. cmsys::SystemTools::FileIsDirectory(prefix)) {
  121. cmSystemTools::ConvertToUnixSlashes(prefix);
  122. return prefix;
  123. }
  124. // The specified absolute prefix is not valid.
  125. return {};
  126. }
  127. // Get and validate prefix-relative path.
  128. std::string relPath = data["cps_path"].asString();
  129. cmSystemTools::ConvertToUnixSlashes(relPath);
  130. if (relPath.empty() || !cmHasLiteralPrefix(relPath, "@prefix@/")) {
  131. // The relative prefix is not valid.
  132. return {};
  133. }
  134. relPath = relPath.substr(8);
  135. // Get directory portion of the absolute path.
  136. std::string const& absPath = cmSystemTools::GetFilenamePath(filepath);
  137. if (ComparePathSuffix(absPath, relPath)) {
  138. return absPath.substr(0, absPath.size() - relPath.size());
  139. }
  140. for (auto* const f : { GetRealPath, GetRealDir }) {
  141. std::string const& tmpPath = (*f)(absPath);
  142. if (!cmSystemTools::ComparePath(tmpPath, absPath) &&
  143. ComparePathSuffix(tmpPath, relPath)) {
  144. return tmpPath.substr(0, tmpPath.size() - relPath.size());
  145. }
  146. }
  147. return {};
  148. }
  149. // Extract key name from value iterator as string_view.
  150. cm::string_view IterKey(Json::Value::const_iterator const& iter)
  151. {
  152. char const* end;
  153. char const* const start = iter.memberName(&end);
  154. return { start, static_cast<std::string::size_type>(end - start) };
  155. }
  156. // Get list-of-strings value from object.
  157. std::vector<std::string> ReadList(Json::Value const& data, char const* key)
  158. {
  159. std::vector<std::string> result;
  160. Json::Value const& arr = data[key];
  161. if (arr.isArray()) {
  162. for (Json::Value const& val : arr) {
  163. if (val.isString()) {
  164. result.push_back(val.asString());
  165. }
  166. }
  167. }
  168. return result;
  169. }
  170. std::string NormalizeTargetName(std::string const& name,
  171. std::string const& context)
  172. {
  173. if (cmHasLiteralPrefix(name, ":")) {
  174. return cmStrCat(context, name);
  175. }
  176. std::string::size_type const n = name.find_first_of(':');
  177. if (n != std::string::npos) {
  178. cm::string_view v{ name };
  179. return cmStrCat(v.substr(0, n), ':', v.substr(n));
  180. }
  181. return name;
  182. }
  183. void AppendProperty(cmMakefile* makefile, cmTarget* target,
  184. cm::string_view property, cm::string_view configuration,
  185. std::string const& value)
  186. {
  187. std::string fullprop;
  188. if (configuration.empty()) {
  189. fullprop = cmStrCat("INTERFACE_"_s, property);
  190. } else {
  191. fullprop = cmStrCat("INTERFACE_"_s, property, '_',
  192. cmSystemTools::UpperCase(configuration));
  193. }
  194. target->AppendProperty(fullprop, value, makefile->GetBacktrace());
  195. }
  196. void AddCompileFeature(cmMakefile* makefile, cmTarget* target,
  197. cm::string_view configuration, std::string const& value)
  198. {
  199. auto reLanguageLevel = []() -> cmsys::RegularExpression {
  200. static cmsys::RegularExpression re{ "^[Cc]([+][+])?([0-9][0-9])$" };
  201. return re;
  202. }();
  203. if (reLanguageLevel.find(value)) {
  204. std::string::size_type const n = reLanguageLevel.end() - 2;
  205. cm::string_view const featurePrefix = (n == 3 ? "cxx_std_"_s : "c_std_"_s);
  206. if (configuration.empty()) {
  207. AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {},
  208. cmStrCat(featurePrefix, value.substr(n)));
  209. } else {
  210. std::string const& feature =
  211. cmStrCat("$<$<CONFIG:"_s, configuration, ">:"_s, featurePrefix,
  212. value.substr(n), '>');
  213. AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {}, feature);
  214. }
  215. } else if (cmStrCaseEq(value, "gnu"_s)) {
  216. // Not implemented in CMake at this time
  217. } else if (cmStrCaseEq(value, "threads"_s)) {
  218. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
  219. "Threads::Threads");
  220. }
  221. }
  222. void AddLinkFeature(cmMakefile* makefile, cmTarget* target,
  223. cm::string_view configuration, std::string const& value)
  224. {
  225. if (cmStrCaseEq(value, "thread"_s)) {
  226. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
  227. "Threads::Threads");
  228. }
  229. }
  230. std::string BuildDefinition(std::string const& name, Json::Value const& value)
  231. {
  232. if (!value.isNull() && value.isConvertibleTo(Json::stringValue)) {
  233. return cmStrCat(name, '=', value.asString());
  234. }
  235. return name;
  236. }
  237. void AddDefinition(cmMakefile* makefile, cmTarget* target,
  238. cm::string_view configuration,
  239. std::string const& definition)
  240. {
  241. AppendProperty(makefile, target, "COMPILE_DEFINITIONS"_s, configuration,
  242. definition);
  243. }
  244. using DefinitionLanguageMap = std::map<cm::string_view, Json::Value>;
  245. using DefinitionsMap = std::map<std::string, DefinitionLanguageMap>;
  246. void AddDefinitions(cmMakefile* makefile, cmTarget* target,
  247. cm::string_view configuration,
  248. DefinitionsMap const& definitions)
  249. {
  250. for (auto const& di : definitions) {
  251. auto const& g = di.second.find("*"_s);
  252. if (g != di.second.end()) {
  253. std::string const& def = BuildDefinition(di.first, g->second);
  254. if (di.second.size() == 1) {
  255. // Only the non-language-specific definition exists.
  256. AddDefinition(makefile, target, configuration, def);
  257. continue;
  258. }
  259. // Create a genex to apply this definition to all languages except
  260. // those that override it.
  261. std::vector<cm::string_view> excludedLanguages;
  262. for (auto const& li : di.second) {
  263. if (li.first != "*"_s) {
  264. excludedLanguages.emplace_back(li.first);
  265. }
  266. }
  267. AddDefinition(makefile, target, configuration,
  268. cmStrCat("$<$<NOT:$<COMPILE_LANGUAGE:"_s,
  269. cmJoin(excludedLanguages, ","_s), ">>:"_s, def,
  270. '>'));
  271. }
  272. // Add language-specific definitions.
  273. for (auto const& li : di.second) {
  274. if (li.first != "*"_s) {
  275. AddDefinition(makefile, target, configuration,
  276. cmStrCat("$<$<COMPILE_LANGUAGE:"_s, li.first, ">:"_s,
  277. BuildDefinition(di.first, li.second), '>'));
  278. }
  279. }
  280. }
  281. }
  282. } // namespace
  283. std::unique_ptr<cmPackageInfoReader> cmPackageInfoReader::Read(
  284. std::string const& path, cmPackageInfoReader const* parent)
  285. {
  286. // Read file and perform some basic validation:
  287. // - the input is valid JSON
  288. // - the input is a JSON object
  289. // - the input has a "cps_version" that we (in theory) know how to parse
  290. Json::Value data = ReadJson(path);
  291. if (!data.isObject() || !CheckSchemaVersion(data)) {
  292. return nullptr;
  293. }
  294. // - the input has a "name" attribute that is a non-empty string
  295. Json::Value const& name = data["name"];
  296. if (!name.isString() || name.empty()) {
  297. return nullptr;
  298. }
  299. // - the input has a "components" attribute that is a JSON object
  300. if (!data["components"].isObject()) {
  301. return nullptr;
  302. }
  303. std::string prefix = (parent ? parent->Prefix : DeterminePrefix(path, data));
  304. if (prefix.empty()) {
  305. return nullptr;
  306. }
  307. // Seems sane enough to hand back to the caller.
  308. std::unique_ptr<cmPackageInfoReader> reader{ new cmPackageInfoReader };
  309. reader->Data = std::move(data);
  310. reader->Prefix = std::move(prefix);
  311. reader->Path = path;
  312. // Determine other information we need to know immediately, or (if this is
  313. // a supplemental reader) copy from the parent.
  314. if (parent) {
  315. reader->ComponentTargets = parent->ComponentTargets;
  316. reader->DefaultConfigurations = parent->DefaultConfigurations;
  317. } else {
  318. reader->DefaultConfigurations = ReadList(reader->Data, "configurations");
  319. }
  320. return reader;
  321. }
  322. std::string cmPackageInfoReader::GetName() const
  323. {
  324. return this->Data["name"].asString();
  325. }
  326. cm::optional<std::string> cmPackageInfoReader::GetVersion() const
  327. {
  328. Json::Value const& version = this->Data["version"];
  329. if (version.isString()) {
  330. return version.asString();
  331. }
  332. return cm::nullopt;
  333. }
  334. std::vector<unsigned> cmPackageInfoReader::ParseVersion() const
  335. {
  336. // Check that we have a version.
  337. cm::optional<std::string> const& version = this->GetVersion();
  338. if (!version) {
  339. return {};
  340. }
  341. std::vector<unsigned> result;
  342. cm::string_view remnant{ *version };
  343. // Check if we know how to parse the version.
  344. Json::Value const& schema = this->Data["version_schema"];
  345. if (schema.isNull() || cmStrCaseEq(schema.asString(), "simple"_s)) {
  346. // Keep going until we run out of parts.
  347. while (!remnant.empty()) {
  348. std::string::size_type n = remnant.find('.');
  349. cm::string_view part = remnant.substr(0, n);
  350. if (n == std::string::npos) {
  351. remnant = {};
  352. } else {
  353. remnant = remnant.substr(n + 1);
  354. }
  355. unsigned long const value = std::stoul(std::string{ part }, &n);
  356. if (n == 0 || value > std::numeric_limits<unsigned>::max()) {
  357. // The part was not a valid number or is too big.
  358. return {};
  359. }
  360. result.push_back(static_cast<unsigned>(value));
  361. }
  362. }
  363. return result;
  364. }
  365. std::string cmPackageInfoReader::ResolvePath(std::string path) const
  366. {
  367. cmSystemTools::ConvertToUnixSlashes(path);
  368. if (cmHasPrefix(path, "@prefix@"_s)) {
  369. return cmStrCat(this->Prefix, path.substr(8));
  370. }
  371. if (!cmSystemTools::FileIsFullPath(path)) {
  372. return cmStrCat(cmSystemTools::GetFilenamePath(this->Path), '/', path);
  373. }
  374. return path;
  375. }
  376. void cmPackageInfoReader::SetOptionalProperty(cmTarget* target,
  377. cm::string_view property,
  378. cm::string_view configuration,
  379. Json::Value const& value) const
  380. {
  381. if (!value.isNull()) {
  382. std::string fullprop;
  383. if (configuration.empty()) {
  384. fullprop = cmStrCat("IMPORTED_"_s, property);
  385. } else {
  386. fullprop = cmStrCat("IMPORTED_"_s, property, '_',
  387. cmSystemTools::UpperCase(configuration));
  388. }
  389. target->SetProperty(fullprop, this->ResolvePath(value.asString()));
  390. }
  391. }
  392. void cmPackageInfoReader::SetTargetProperties(
  393. cmMakefile* makefile, cmTarget* target, Json::Value const& data,
  394. std::string const& package, cm::string_view configuration) const
  395. {
  396. // Add compile and link features.
  397. for (std::string const& def : ReadList(data, "compile_features")) {
  398. AddCompileFeature(makefile, target, configuration, def);
  399. }
  400. for (std::string const& def : ReadList(data, "link_features")) {
  401. AddLinkFeature(makefile, target, configuration, def);
  402. }
  403. // Add compile definitions.
  404. Json::Value const& defs = data["definitions"];
  405. DefinitionsMap definitionsMap;
  406. for (auto ldi = defs.begin(), lde = defs.end(); ldi != lde; ++ldi) {
  407. cm::string_view const originalLang = IterKey(ldi);
  408. cm::string_view const lang = MapLanguage(originalLang);
  409. if (lang.empty()) {
  410. makefile->IssueMessage(MessageType::WARNING,
  411. cmStrCat("ignoring unknown language "_s,
  412. originalLang, " in definitions for "_s,
  413. target->GetName()));
  414. continue;
  415. }
  416. for (auto di = ldi->begin(), de = ldi->end(); di != de; ++di) {
  417. definitionsMap[di.name()].emplace(lang, *di);
  418. }
  419. }
  420. AddDefinitions(makefile, target, configuration, definitionsMap);
  421. // Add include directories.
  422. for (std::string inc : ReadList(data, "includes")) {
  423. AppendProperty(makefile, target, "INCLUDE_DIRECTORIES"_s, configuration,
  424. this->ResolvePath(std::move(inc)));
  425. }
  426. // Add link name/location(s).
  427. this->SetOptionalProperty(target, "LOCATION"_s, configuration,
  428. data["location"]);
  429. this->SetOptionalProperty(target, "IMPLIB"_s, configuration,
  430. data["link_location"]);
  431. this->SetOptionalProperty(target, "SONAME"_s, configuration,
  432. data["link_name"]);
  433. // Add link languages.
  434. for (std::string const& originalLang : ReadList(data, "link_languages")) {
  435. cm::string_view const lang = MapLanguage(originalLang, DisallowGlob);
  436. if (!lang.empty()) {
  437. AppendProperty(makefile, target, "LINK_LANGUAGES"_s, configuration,
  438. std::string{ lang });
  439. }
  440. }
  441. // Add transitive dependencies.
  442. for (std::string const& dep : ReadList(data, "requires")) {
  443. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
  444. NormalizeTargetName(dep, package));
  445. }
  446. for (std::string const& dep : ReadList(data, "link_requires")) {
  447. std::string const& lib =
  448. cmStrCat("$<LINK_ONLY:"_s, NormalizeTargetName(dep, package), '>');
  449. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib);
  450. }
  451. }
  452. cmTarget* cmPackageInfoReader::AddLibraryComponent(
  453. cmMakefile* makefile, cmStateEnums::TargetType type, std::string const& name,
  454. Json::Value const& data, std::string const& package) const
  455. {
  456. // Create the imported target.
  457. cmTarget* const target = makefile->AddImportedTarget(name, type, false);
  458. // Set target properties.
  459. this->SetTargetProperties(makefile, target, data, package, {});
  460. auto const& cfgData = data["configurations"];
  461. for (auto ci = cfgData.begin(), ce = cfgData.end(); ci != ce; ++ci) {
  462. this->SetTargetProperties(makefile, target, *ci, package, IterKey(ci));
  463. }
  464. // Set default configurations.
  465. if (!this->DefaultConfigurations.empty()) {
  466. target->SetProperty("IMPORTED_CONFIGURATIONS",
  467. cmJoin(this->DefaultConfigurations, ";"_s));
  468. }
  469. return target;
  470. }
  471. bool cmPackageInfoReader::ImportTargets(cmMakefile* makefile,
  472. cmExecutionStatus& status)
  473. {
  474. std::string const& package = this->GetName();
  475. // Read components.
  476. Json::Value const& components = this->Data["components"];
  477. for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
  478. cm::string_view const& name = IterKey(ci);
  479. std::string const& type =
  480. cmSystemTools::LowerCase((*ci)["type"].asString());
  481. // Get and validate full target name.
  482. std::string const& fullName = cmStrCat(package, "::"_s, name);
  483. {
  484. std::string msg;
  485. if (!makefile->EnforceUniqueName(fullName, msg)) {
  486. status.SetError(msg);
  487. return false;
  488. }
  489. }
  490. cmTarget* target = nullptr;
  491. if (type == "symbolic"_s) {
  492. // TODO
  493. } else if (type == "dylib"_s) {
  494. target = this->AddLibraryComponent(
  495. makefile, cmStateEnums::SHARED_LIBRARY, fullName, *ci, package);
  496. } else if (type == "module"_s) {
  497. target = this->AddLibraryComponent(
  498. makefile, cmStateEnums::MODULE_LIBRARY, fullName, *ci, package);
  499. } else if (type == "archive"_s) {
  500. target = this->AddLibraryComponent(
  501. makefile, cmStateEnums::STATIC_LIBRARY, fullName, *ci, package);
  502. } else if (type == "interface"_s) {
  503. target = this->AddLibraryComponent(
  504. makefile, cmStateEnums::INTERFACE_LIBRARY, fullName, *ci, package);
  505. } else {
  506. makefile->IssueMessage(MessageType::WARNING,
  507. cmStrCat("component "_s, fullName,
  508. " has unknown type "_s, type,
  509. " and was not imported"_s));
  510. }
  511. if (target) {
  512. this->ComponentTargets.emplace(std::string{ name }, target);
  513. }
  514. }
  515. // Read default components.
  516. std::vector<std::string> const& defaultComponents =
  517. ReadList(this->Data, "default_components");
  518. if (!defaultComponents.empty()) {
  519. std::string msg;
  520. if (!makefile->EnforceUniqueName(package, msg)) {
  521. status.SetError(msg);
  522. return false;
  523. }
  524. cmTarget* const target = makefile->AddImportedTarget(
  525. package, cmStateEnums::INTERFACE_LIBRARY, false);
  526. for (std::string const& name : defaultComponents) {
  527. std::string const& fullName = cmStrCat(package, "::"_s, name);
  528. AppendProperty(makefile, target, "LINK_LIBRARIES"_s, {}, fullName);
  529. }
  530. }
  531. return true;
  532. }
  533. bool cmPackageInfoReader::ImportTargetConfigurations(
  534. cmMakefile* makefile, cmExecutionStatus& status) const
  535. {
  536. std::string const& configuration = this->Data["configuration"].asString();
  537. if (configuration.empty()) {
  538. makefile->IssueMessage(MessageType::WARNING,
  539. cmStrCat("supplemental file "_s, this->Path,
  540. " does not specify a configuration"_s));
  541. return true;
  542. }
  543. std::string const& package = this->GetName();
  544. Json::Value const& components = this->Data["components"];
  545. for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
  546. // Get component name and look up target.
  547. cm::string_view const& name = IterKey(ci);
  548. auto const& ti = this->ComponentTargets.find(std::string{ name });
  549. if (ti == this->ComponentTargets.end()) {
  550. status.SetError(cmStrCat("component "_s, name, " was not found"_s));
  551. return false;
  552. }
  553. // Read supplemental data for component.
  554. this->SetTargetProperties(makefile, ti->second, *ci, package,
  555. configuration);
  556. }
  557. return true;
  558. }