cmGetPropertyCommand.cxx 19 KB

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