cmGetPropertyCommand.cxx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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 "cmGetPropertyCommand.h"
  4. #include <cstddef>
  5. #include <functional>
  6. #include <cm/string_view>
  7. #include <cmext/string_view>
  8. #include "cmExecutionStatus.h"
  9. #include "cmGlobalGenerator.h"
  10. #include "cmInstalledFile.h"
  11. #include "cmMakefile.h"
  12. #include "cmMessageType.h"
  13. #include "cmPolicies.h"
  14. #include "cmProperty.h"
  15. #include "cmPropertyDefinition.h"
  16. #include "cmSetPropertyCommand.h"
  17. #include "cmSourceFile.h"
  18. #include "cmState.h"
  19. #include "cmStringAlgorithms.h"
  20. #include "cmSystemTools.h"
  21. #include "cmTarget.h"
  22. #include "cmTest.h"
  23. #include "cmValue.h"
  24. #include "cmake.h"
  25. namespace {
  26. enum OutType
  27. {
  28. OutValue,
  29. OutDefined,
  30. OutBriefDoc,
  31. OutFullDoc,
  32. OutSet
  33. };
  34. // Implementation of each property type.
  35. bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
  36. OutType infoType, const std::string& variable,
  37. const std::string& propertyName);
  38. bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
  39. OutType infoType, const std::string& variable,
  40. const std::string& propertyName);
  41. bool HandleTargetMode(cmExecutionStatus& status, const std::string& name,
  42. OutType infoType, const std::string& variable,
  43. const std::string& propertyName);
  44. bool HandleSourceMode(cmExecutionStatus& status, const std::string& name,
  45. OutType infoType, const std::string& variable,
  46. const std::string& propertyName,
  47. cmMakefile& directory_makefile,
  48. bool source_file_paths_should_be_absolute);
  49. bool HandleTestMode(cmExecutionStatus& status, const std::string& name,
  50. OutType infoType, const std::string& variable,
  51. const std::string& propertyName,
  52. cmMakefile& directory_makefile);
  53. bool HandleVariableMode(cmExecutionStatus& status, const std::string& name,
  54. OutType infoType, const std::string& variable,
  55. const std::string& propertyName);
  56. bool HandleCacheMode(cmExecutionStatus& status, const std::string& name,
  57. OutType infoType, const std::string& variable,
  58. const std::string& propertyName);
  59. bool HandleInstallMode(cmExecutionStatus& status, const std::string& name,
  60. OutType infoType, const std::string& variable,
  61. const std::string& propertyName);
  62. }
  63. bool cmGetPropertyCommand(std::vector<std::string> const& args,
  64. cmExecutionStatus& status)
  65. {
  66. OutType infoType = OutValue;
  67. if (args.size() < 3) {
  68. status.SetError("called with incorrect number of arguments");
  69. return false;
  70. }
  71. // The cmake variable in which to store the result.
  72. std::string const& variable = args[0];
  73. std::string name;
  74. std::string propertyName;
  75. std::vector<std::string> source_file_directories;
  76. std::vector<std::string> source_file_target_directories;
  77. bool source_file_directory_option_enabled = false;
  78. bool source_file_target_option_enabled = false;
  79. std::string test_directory;
  80. bool test_directory_option_enabled = false;
  81. // Get the scope from which to get the property.
  82. cmProperty::ScopeType scope;
  83. if (args[1] == "GLOBAL") {
  84. scope = cmProperty::GLOBAL;
  85. } else if (args[1] == "DIRECTORY") {
  86. scope = cmProperty::DIRECTORY;
  87. } else if (args[1] == "TARGET") {
  88. scope = cmProperty::TARGET;
  89. } else if (args[1] == "SOURCE") {
  90. scope = cmProperty::SOURCE_FILE;
  91. } else if (args[1] == "TEST") {
  92. scope = cmProperty::TEST;
  93. } else if (args[1] == "VARIABLE") {
  94. scope = cmProperty::VARIABLE;
  95. } else if (args[1] == "CACHE") {
  96. scope = cmProperty::CACHE;
  97. } else if (args[1] == "INSTALL") {
  98. scope = cmProperty::INSTALL;
  99. } else {
  100. status.SetError(cmStrCat(
  101. "given invalid scope ", args[1],
  102. ". "
  103. "Valid scopes are "
  104. "GLOBAL, DIRECTORY, TARGET, SOURCE, TEST, VARIABLE, CACHE, INSTALL."));
  105. return false;
  106. }
  107. // Parse remaining arguments.
  108. enum Doing
  109. {
  110. DoingNone,
  111. DoingName,
  112. DoingProperty,
  113. DoingType,
  114. DoingSourceDirectory,
  115. DoingSourceTargetDirectory,
  116. DoingTestDirectory,
  117. };
  118. Doing doing = DoingName;
  119. for (unsigned int i = 2; i < args.size(); ++i) {
  120. if (args[i] == "PROPERTY") {
  121. doing = DoingProperty;
  122. } else if (args[i] == "BRIEF_DOCS") {
  123. doing = DoingNone;
  124. infoType = OutBriefDoc;
  125. } else if (args[i] == "FULL_DOCS") {
  126. doing = DoingNone;
  127. infoType = OutFullDoc;
  128. } else if (args[i] == "SET") {
  129. doing = DoingNone;
  130. infoType = OutSet;
  131. } else if (args[i] == "DEFINED") {
  132. doing = DoingNone;
  133. infoType = OutDefined;
  134. } else if (doing == DoingName) {
  135. doing = DoingNone;
  136. name = args[i];
  137. } else if (doing == DoingNone && scope == cmProperty::SOURCE_FILE &&
  138. args[i] == "DIRECTORY") {
  139. doing = DoingSourceDirectory;
  140. source_file_directory_option_enabled = true;
  141. } else if (doing == DoingNone && scope == cmProperty::SOURCE_FILE &&
  142. args[i] == "TARGET_DIRECTORY") {
  143. doing = DoingSourceTargetDirectory;
  144. source_file_target_option_enabled = true;
  145. } else if (doing == DoingNone && scope == cmProperty::TEST &&
  146. args[i] == "DIRECTORY") {
  147. doing = DoingTestDirectory;
  148. test_directory_option_enabled = true;
  149. } else if (doing == DoingSourceDirectory) {
  150. source_file_directories.push_back(args[i]);
  151. doing = DoingNone;
  152. } else if (doing == DoingSourceTargetDirectory) {
  153. source_file_target_directories.push_back(args[i]);
  154. doing = DoingNone;
  155. } else if (doing == DoingTestDirectory) {
  156. test_directory = args[i];
  157. doing = DoingNone;
  158. } else if (doing == DoingProperty) {
  159. doing = DoingNone;
  160. propertyName = args[i];
  161. } else {
  162. status.SetError(cmStrCat("given invalid argument \"", args[i], "\"."));
  163. return false;
  164. }
  165. }
  166. // Make sure a property name was found.
  167. if (propertyName.empty()) {
  168. status.SetError("not given a PROPERTY <name> argument.");
  169. return false;
  170. }
  171. std::vector<cmMakefile*> source_file_directory_makefiles;
  172. bool source_file_scopes_handled =
  173. SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
  174. status, source_file_directory_option_enabled,
  175. source_file_target_option_enabled, source_file_directories,
  176. source_file_target_directories, source_file_directory_makefiles);
  177. cmMakefile* test_directory_makefile;
  178. bool test_scopes_handled =
  179. SetPropertyCommand::HandleAndValidateTestDirectoryScopes(
  180. status, test_directory_option_enabled, test_directory,
  181. test_directory_makefile);
  182. if (!(source_file_scopes_handled && test_scopes_handled)) {
  183. return false;
  184. }
  185. // Compute requested output.
  186. if (infoType == OutBriefDoc) {
  187. // Lookup brief documentation.
  188. std::string output;
  189. if (cmPropertyDefinition const* def =
  190. status.GetMakefile().GetState()->GetPropertyDefinition(propertyName,
  191. scope)) {
  192. output = def->GetShortDescription();
  193. }
  194. if (output.empty()) {
  195. output = "NOTFOUND";
  196. }
  197. status.GetMakefile().AddDefinition(variable, output);
  198. } else if (infoType == OutFullDoc) {
  199. // Lookup full documentation.
  200. std::string output;
  201. if (cmPropertyDefinition const* def =
  202. status.GetMakefile().GetState()->GetPropertyDefinition(propertyName,
  203. scope)) {
  204. output = def->GetFullDescription();
  205. }
  206. if (output.empty()) {
  207. output = "NOTFOUND";
  208. }
  209. status.GetMakefile().AddDefinition(variable, output);
  210. } else if (infoType == OutDefined) {
  211. // Lookup if the property is defined
  212. if (status.GetMakefile().GetState()->GetPropertyDefinition(propertyName,
  213. scope)) {
  214. status.GetMakefile().AddDefinition(variable, "1");
  215. } else {
  216. status.GetMakefile().AddDefinition(variable, "0");
  217. }
  218. } else {
  219. // Dispatch property getting.
  220. cmMakefile& directory_scope_mf = *(source_file_directory_makefiles[0]);
  221. bool source_file_paths_should_be_absolute =
  222. source_file_directory_option_enabled ||
  223. source_file_target_option_enabled;
  224. switch (scope) {
  225. case cmProperty::GLOBAL:
  226. return HandleGlobalMode(status, name, infoType, variable,
  227. propertyName);
  228. case cmProperty::DIRECTORY:
  229. return HandleDirectoryMode(status, name, infoType, variable,
  230. propertyName);
  231. case cmProperty::TARGET:
  232. return HandleTargetMode(status, name, infoType, variable,
  233. propertyName);
  234. case cmProperty::SOURCE_FILE:
  235. return HandleSourceMode(status, name, infoType, variable, propertyName,
  236. directory_scope_mf,
  237. source_file_paths_should_be_absolute);
  238. case cmProperty::TEST:
  239. return HandleTestMode(status, name, infoType, variable, propertyName,
  240. *test_directory_makefile);
  241. case cmProperty::VARIABLE:
  242. return HandleVariableMode(status, name, infoType, variable,
  243. propertyName);
  244. case cmProperty::CACHE:
  245. return HandleCacheMode(status, name, infoType, variable, propertyName);
  246. case cmProperty::INSTALL:
  247. return HandleInstallMode(status, name, infoType, variable,
  248. propertyName);
  249. case cmProperty::CACHED_VARIABLE:
  250. break; // should never happen
  251. }
  252. }
  253. return true;
  254. }
  255. namespace GetPropertyCommand {
  256. bool GetSourceFilePropertyGENERATED(
  257. const std::string& name, cmMakefile& mf,
  258. const std::function<bool(bool)>& storeResult)
  259. {
  260. // Globally set as generated?
  261. // Note: If the given "name" only contains a filename or a relative path
  262. // the file's location is ambiguous. In general, one would expect
  263. // it in the source-directory, because that is where source files
  264. // are located normally. However, generated files are normally
  265. // generated in the build-directory. Therefore, we first check for
  266. // a generated file in the build-directory before we check for a
  267. // generated file in the source-directory.
  268. {
  269. auto file =
  270. cmSystemTools::CollapseFullPath(name, mf.GetCurrentBinaryDirectory());
  271. if (mf.GetGlobalGenerator()->IsGeneratedFile(file)) {
  272. return storeResult(true);
  273. }
  274. }
  275. {
  276. auto file =
  277. cmSystemTools::CollapseFullPath(name, mf.GetCurrentSourceDirectory());
  278. if (mf.GetGlobalGenerator()->IsGeneratedFile(file)) {
  279. return storeResult(true);
  280. }
  281. }
  282. // Skip checking the traditional/local property.
  283. return storeResult(false);
  284. }
  285. }
  286. namespace {
  287. // Implementation of result storage.
  288. template <typename ValueType>
  289. bool StoreResult(OutType infoType, cmMakefile& makefile,
  290. const std::string& variable, ValueType value)
  291. {
  292. if (infoType == OutSet) {
  293. makefile.AddDefinition(variable, value ? "1" : "0");
  294. } else // if(infoType == OutValue)
  295. {
  296. if (value) {
  297. makefile.AddDefinition(variable, value);
  298. } else {
  299. makefile.RemoveDefinition(variable);
  300. }
  301. }
  302. return true;
  303. }
  304. template <>
  305. bool StoreResult(OutType infoType, cmMakefile& makefile,
  306. const std::string& variable, std::nullptr_t value)
  307. {
  308. return StoreResult(infoType, makefile, variable, cmValue(value));
  309. }
  310. bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
  311. OutType infoType, const std::string& variable,
  312. const std::string& propertyName)
  313. {
  314. if (!name.empty()) {
  315. status.SetError("given name for GLOBAL scope.");
  316. return false;
  317. }
  318. // Get the property.
  319. cmake* cm = status.GetMakefile().GetCMakeInstance();
  320. return StoreResult(infoType, status.GetMakefile(), variable,
  321. cm->GetState()->GetGlobalProperty(propertyName));
  322. }
  323. bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
  324. OutType infoType, const std::string& variable,
  325. const std::string& propertyName)
  326. {
  327. // Default to the current directory.
  328. cmMakefile* mf = &status.GetMakefile();
  329. // Lookup the directory if given.
  330. if (!name.empty()) {
  331. // Construct the directory name. Interpret relative paths with
  332. // respect to the current directory.
  333. std::string dir = cmSystemTools::CollapseFullPath(
  334. name, status.GetMakefile().GetCurrentSourceDirectory());
  335. // Lookup the generator.
  336. mf = status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir);
  337. if (!mf) {
  338. // Could not find the directory.
  339. status.SetError(
  340. "DIRECTORY scope provided but requested directory was not found. "
  341. "This could be because the directory argument was invalid or, "
  342. "it is valid but has not been processed yet.");
  343. return false;
  344. }
  345. }
  346. if (propertyName == "DEFINITIONS") {
  347. switch (mf->GetPolicyStatus(cmPolicies::CMP0059)) {
  348. case cmPolicies::WARN:
  349. mf->IssueMessage(MessageType::AUTHOR_WARNING,
  350. cmPolicies::GetPolicyWarning(cmPolicies::CMP0059));
  351. CM_FALLTHROUGH;
  352. case cmPolicies::OLD:
  353. return StoreResult(infoType, status.GetMakefile(), variable,
  354. mf->GetDefineFlagsCMP0059());
  355. case cmPolicies::NEW:
  356. break;
  357. }
  358. }
  359. // Get the property.
  360. return StoreResult(infoType, status.GetMakefile(), variable,
  361. mf->GetProperty(propertyName));
  362. }
  363. bool HandleTargetMode(cmExecutionStatus& status, const std::string& name,
  364. OutType infoType, const std::string& variable,
  365. const std::string& propertyName)
  366. {
  367. if (name.empty()) {
  368. status.SetError("not given name for TARGET scope.");
  369. return false;
  370. }
  371. if (cmTarget* target = status.GetMakefile().FindTargetToUse(name)) {
  372. if (propertyName == "ALIASED_TARGET" || propertyName == "ALIAS_GLOBAL") {
  373. if (status.GetMakefile().IsAlias(name)) {
  374. if (propertyName == "ALIASED_TARGET") {
  375. return StoreResult(infoType, status.GetMakefile(), variable,
  376. target->GetName().c_str());
  377. }
  378. if (propertyName == "ALIAS_GLOBAL") {
  379. return StoreResult(
  380. infoType, status.GetMakefile(), variable,
  381. status.GetMakefile().GetGlobalGenerator()->IsAlias(name)
  382. ? "TRUE"
  383. : "FALSE");
  384. }
  385. }
  386. return StoreResult(infoType, status.GetMakefile(), variable, nullptr);
  387. }
  388. cmValue prop =
  389. target->GetComputedProperty(propertyName, status.GetMakefile());
  390. if (!prop) {
  391. prop = target->GetProperty(propertyName);
  392. }
  393. return StoreResult(infoType, status.GetMakefile(), variable, prop);
  394. }
  395. status.SetError(cmStrCat("could not find TARGET ", name,
  396. ". Perhaps it has not yet been created."));
  397. return false;
  398. }
  399. bool HandleSourceMode(cmExecutionStatus& status, const std::string& name,
  400. OutType infoType, const std::string& variable,
  401. const std::string& propertyName,
  402. cmMakefile& directory_makefile,
  403. const bool source_file_paths_should_be_absolute)
  404. {
  405. if (name.empty()) {
  406. status.SetError("not given name for SOURCE scope.");
  407. return false;
  408. }
  409. // Special handling for GENERATED property.
  410. // Note: Only, if CMP0163 is set to NEW!
  411. if (propertyName == "GENERATED"_s) {
  412. auto& mf = status.GetMakefile();
  413. auto cmp0163 = directory_makefile.GetPolicyStatus(cmPolicies::CMP0163);
  414. bool const cmp0163new =
  415. cmp0163 != cmPolicies::OLD && cmp0163 != cmPolicies::WARN;
  416. if (cmp0163new) {
  417. return GetPropertyCommand::GetSourceFilePropertyGENERATED(
  418. name, mf, [infoType, &variable, &mf](bool isGenerated) -> bool {
  419. // Set the value on the original Makefile scope, not the scope of the
  420. // requested directory.
  421. return StoreResult(infoType, mf, variable,
  422. (isGenerated) ? cmValue("1") : cmValue("0"));
  423. });
  424. }
  425. }
  426. // Get the source file.
  427. const std::string source_file_absolute_path =
  428. SetPropertyCommand::MakeSourceFilePathAbsoluteIfNeeded(
  429. status, name, source_file_paths_should_be_absolute);
  430. if (cmSourceFile* sf =
  431. directory_makefile.GetOrCreateSource(source_file_absolute_path)) {
  432. // Set the value on the original Makefile scope, not the scope of the
  433. // requested directory.
  434. return StoreResult(infoType, status.GetMakefile(), variable,
  435. sf->GetPropertyForUser(propertyName));
  436. }
  437. status.SetError(
  438. cmStrCat("given SOURCE name that could not be found or created: ",
  439. source_file_absolute_path));
  440. return false;
  441. }
  442. bool HandleTestMode(cmExecutionStatus& status, const std::string& name,
  443. OutType infoType, const std::string& variable,
  444. const std::string& propertyName, cmMakefile& test_makefile)
  445. {
  446. if (name.empty()) {
  447. status.SetError("not given name for TEST scope.");
  448. return false;
  449. }
  450. // Loop over all tests looking for matching names.
  451. if (cmTest* test = test_makefile.GetTest(name)) {
  452. return StoreResult(infoType, status.GetMakefile(), variable,
  453. test->GetProperty(propertyName));
  454. }
  455. // If not found it is an error.
  456. status.SetError(cmStrCat("given TEST name that does not exist: ", name));
  457. return false;
  458. }
  459. bool HandleVariableMode(cmExecutionStatus& status, const std::string& name,
  460. OutType infoType, const std::string& variable,
  461. const std::string& propertyName)
  462. {
  463. if (!name.empty()) {
  464. status.SetError("given name for VARIABLE scope.");
  465. return false;
  466. }
  467. return StoreResult(infoType, status.GetMakefile(), variable,
  468. status.GetMakefile().GetDefinition(propertyName));
  469. }
  470. bool HandleCacheMode(cmExecutionStatus& status, const std::string& name,
  471. OutType infoType, const std::string& variable,
  472. const std::string& propertyName)
  473. {
  474. if (name.empty()) {
  475. status.SetError("not given name for CACHE scope.");
  476. return false;
  477. }
  478. cmValue value = nullptr;
  479. if (status.GetMakefile().GetState()->GetCacheEntryValue(name)) {
  480. value = status.GetMakefile().GetState()->GetCacheEntryProperty(
  481. name, propertyName);
  482. }
  483. StoreResult(infoType, status.GetMakefile(), variable, value);
  484. return true;
  485. }
  486. bool HandleInstallMode(cmExecutionStatus& status, const std::string& name,
  487. OutType infoType, const std::string& variable,
  488. const std::string& propertyName)
  489. {
  490. if (name.empty()) {
  491. status.SetError("not given name for INSTALL scope.");
  492. return false;
  493. }
  494. // Get the installed file.
  495. cmake* cm = status.GetMakefile().GetCMakeInstance();
  496. if (cmInstalledFile* file =
  497. cm->GetOrCreateInstalledFile(&status.GetMakefile(), name)) {
  498. std::string value;
  499. bool isSet = file->GetProperty(propertyName, value);
  500. return StoreResult(infoType, status.GetMakefile(), variable,
  501. isSet ? value.c_str() : nullptr);
  502. }
  503. status.SetError(
  504. cmStrCat("given INSTALL name that could not be found or created: ", name));
  505. return false;
  506. }
  507. }