cmSetPropertyCommand.cxx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  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 "cmSetPropertyCommand.h"
  4. #include <set>
  5. #include <sstream>
  6. #include "cmExecutionStatus.h"
  7. #include "cmGlobalGenerator.h"
  8. #include "cmInstalledFile.h"
  9. #include "cmMakefile.h"
  10. #include "cmProperty.h"
  11. #include "cmRange.h"
  12. #include "cmSourceFile.h"
  13. #include "cmState.h"
  14. #include "cmStringAlgorithms.h"
  15. #include "cmSystemTools.h"
  16. #include "cmTarget.h"
  17. #include "cmTest.h"
  18. #include "cmake.h"
  19. namespace {
  20. bool HandleGlobalMode(cmExecutionStatus& status,
  21. const std::set<std::string>& names,
  22. const std::string& propertyName,
  23. const std::string& propertyValue, bool appendAsString,
  24. bool appendMode, bool remove);
  25. bool HandleDirectoryMode(cmExecutionStatus& status,
  26. const std::set<std::string>& names,
  27. const std::string& propertyName,
  28. const std::string& propertyValue, bool appendAsString,
  29. bool appendMode, bool remove);
  30. bool HandleTargetMode(cmExecutionStatus& status,
  31. const std::set<std::string>& names,
  32. const std::string& propertyName,
  33. const std::string& propertyValue, bool appendAsString,
  34. bool appendMode, bool remove);
  35. bool HandleTarget(cmTarget* target, cmMakefile& makefile,
  36. const std::string& propertyName,
  37. const std::string& propertyValue, bool appendAsString,
  38. bool appendMode, bool remove);
  39. bool HandleSourceMode(cmExecutionStatus& status,
  40. const std::set<std::string>& names,
  41. const std::string& propertyName,
  42. const std::string& propertyValue, bool appendAsString,
  43. bool appendMode, bool remove);
  44. bool HandleSource(cmSourceFile* sf, const std::string& propertyName,
  45. const std::string& propertyValue, bool appendAsString,
  46. bool appendMode, bool remove);
  47. bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
  48. const std::string& propertyName,
  49. const std::string& propertyValue, bool appendAsString,
  50. bool appendMode, bool remove);
  51. bool HandleTest(cmTest* test, const std::string& propertyName,
  52. const std::string& propertyValue, bool appendAsString,
  53. bool appendMode, bool remove);
  54. bool HandleCacheMode(cmExecutionStatus& status,
  55. const std::set<std::string>& names,
  56. const std::string& propertyName,
  57. const std::string& propertyValue, bool appendAsString,
  58. bool appendMode, bool remove);
  59. bool HandleCacheEntry(std::string const& cacheKey, const cmMakefile& makefile,
  60. const std::string& propertyName,
  61. const std::string& propertyValue, bool appendAsString,
  62. bool appendMode, bool remove);
  63. bool HandleInstallMode(cmExecutionStatus& status,
  64. const std::set<std::string>& names,
  65. const std::string& propertyName,
  66. const std::string& propertyValue, bool appendAsString,
  67. bool appendMode, bool remove);
  68. bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile,
  69. const std::string& propertyName,
  70. const std::string& propertyValue, bool appendAsString,
  71. bool appendMode, bool remove);
  72. }
  73. bool cmSetPropertyCommand(std::vector<std::string> const& args,
  74. cmExecutionStatus& status)
  75. {
  76. if (args.size() < 2) {
  77. status.SetError("called with incorrect number of arguments");
  78. return false;
  79. }
  80. // Get the scope on which to set the property.
  81. std::string const& scopeName = args.front();
  82. cmProperty::ScopeType scope;
  83. if (scopeName == "GLOBAL") {
  84. scope = cmProperty::GLOBAL;
  85. } else if (scopeName == "DIRECTORY") {
  86. scope = cmProperty::DIRECTORY;
  87. } else if (scopeName == "TARGET") {
  88. scope = cmProperty::TARGET;
  89. } else if (scopeName == "SOURCE") {
  90. scope = cmProperty::SOURCE_FILE;
  91. } else if (scopeName == "TEST") {
  92. scope = cmProperty::TEST;
  93. } else if (scopeName == "CACHE") {
  94. scope = cmProperty::CACHE;
  95. } else if (scopeName == "INSTALL") {
  96. scope = cmProperty::INSTALL;
  97. } else {
  98. std::ostringstream e;
  99. e << "given invalid scope " << scopeName << ". "
  100. << "Valid scopes are GLOBAL, DIRECTORY, "
  101. "TARGET, SOURCE, TEST, CACHE, INSTALL.";
  102. status.SetError(e.str());
  103. return false;
  104. }
  105. bool appendAsString = false;
  106. bool appendMode = false;
  107. bool remove = true;
  108. std::set<std::string> names;
  109. std::string propertyName;
  110. std::string propertyValue;
  111. // Parse the rest of the arguments up to the values.
  112. enum Doing
  113. {
  114. DoingNone,
  115. DoingNames,
  116. DoingProperty,
  117. DoingValues
  118. };
  119. Doing doing = DoingNames;
  120. const char* sep = "";
  121. for (std::string const& arg : cmMakeRange(args).advance(1)) {
  122. if (arg == "PROPERTY") {
  123. doing = DoingProperty;
  124. } else if (arg == "APPEND") {
  125. doing = DoingNone;
  126. appendMode = true;
  127. remove = false;
  128. appendAsString = false;
  129. } else if (arg == "APPEND_STRING") {
  130. doing = DoingNone;
  131. appendMode = true;
  132. remove = false;
  133. appendAsString = true;
  134. } else if (doing == DoingNames) {
  135. names.insert(arg);
  136. } else if (doing == DoingProperty) {
  137. propertyName = arg;
  138. doing = DoingValues;
  139. } else if (doing == DoingValues) {
  140. propertyValue += sep;
  141. sep = ";";
  142. propertyValue += arg;
  143. remove = false;
  144. } else {
  145. std::ostringstream e;
  146. e << "given invalid argument \"" << arg << "\".";
  147. status.SetError(e.str());
  148. return false;
  149. }
  150. }
  151. // Make sure a property name was found.
  152. if (propertyName.empty()) {
  153. status.SetError("not given a PROPERTY <name> argument.");
  154. return false;
  155. }
  156. // Dispatch property setting.
  157. switch (scope) {
  158. case cmProperty::GLOBAL:
  159. return HandleGlobalMode(status, names, propertyName, propertyValue,
  160. appendAsString, appendMode, remove);
  161. case cmProperty::DIRECTORY:
  162. return HandleDirectoryMode(status, names, propertyName, propertyValue,
  163. appendAsString, appendMode, remove);
  164. case cmProperty::TARGET:
  165. return HandleTargetMode(status, names, propertyName, propertyValue,
  166. appendAsString, appendMode, remove);
  167. case cmProperty::SOURCE_FILE:
  168. return HandleSourceMode(status, names, propertyName, propertyValue,
  169. appendAsString, appendMode, remove);
  170. case cmProperty::TEST:
  171. return HandleTestMode(status, names, propertyName, propertyValue,
  172. appendAsString, appendMode, remove);
  173. case cmProperty::CACHE:
  174. return HandleCacheMode(status, names, propertyName, propertyValue,
  175. appendAsString, appendMode, remove);
  176. case cmProperty::INSTALL:
  177. return HandleInstallMode(status, names, propertyName, propertyValue,
  178. appendAsString, appendMode, remove);
  179. case cmProperty::VARIABLE:
  180. case cmProperty::CACHED_VARIABLE:
  181. break; // should never happen
  182. }
  183. return true;
  184. }
  185. namespace {
  186. bool HandleGlobalMode(cmExecutionStatus& status,
  187. const std::set<std::string>& names,
  188. const std::string& propertyName,
  189. const std::string& propertyValue,
  190. const bool appendAsString, const bool appendMode,
  191. const bool remove)
  192. {
  193. if (!names.empty()) {
  194. status.SetError("given names for GLOBAL scope.");
  195. return false;
  196. }
  197. // Set or append the property.
  198. cmake* cm = status.GetMakefile().GetCMakeInstance();
  199. const char* value = propertyValue.c_str();
  200. if (remove) {
  201. value = nullptr;
  202. }
  203. if (appendMode) {
  204. cm->AppendProperty(propertyName, value ? value : "", appendAsString);
  205. } else {
  206. cm->SetProperty(propertyName, value);
  207. }
  208. return true;
  209. }
  210. bool HandleDirectoryMode(cmExecutionStatus& status,
  211. const std::set<std::string>& names,
  212. const std::string& propertyName,
  213. const std::string& propertyValue,
  214. const bool appendAsString, const bool appendMode,
  215. const bool remove)
  216. {
  217. if (names.size() > 1) {
  218. status.SetError("allows at most one name for DIRECTORY scope.");
  219. return false;
  220. }
  221. // Default to the current directory.
  222. cmMakefile* mf = &status.GetMakefile();
  223. // Lookup the directory if given.
  224. if (!names.empty()) {
  225. // Construct the directory name. Interpret relative paths with
  226. // respect to the current directory.
  227. std::string dir = *names.begin();
  228. if (!cmSystemTools::FileIsFullPath(dir)) {
  229. dir = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/',
  230. *names.begin());
  231. }
  232. // The local generators are associated with collapsed paths.
  233. dir = cmSystemTools::CollapseFullPath(dir);
  234. mf = status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir);
  235. if (!mf) {
  236. // Could not find the directory.
  237. status.SetError(
  238. "DIRECTORY scope provided but requested directory was not found. "
  239. "This could be because the directory argument was invalid or, "
  240. "it is valid but has not been processed yet.");
  241. return false;
  242. }
  243. }
  244. // Set or append the property.
  245. const char* value = propertyValue.c_str();
  246. if (remove) {
  247. value = nullptr;
  248. }
  249. if (appendMode) {
  250. mf->AppendProperty(propertyName, value ? value : "", appendAsString);
  251. } else {
  252. mf->SetProperty(propertyName, value);
  253. }
  254. return true;
  255. }
  256. bool HandleTargetMode(cmExecutionStatus& status,
  257. const std::set<std::string>& names,
  258. const std::string& propertyName,
  259. const std::string& propertyValue,
  260. const bool appendAsString, const bool appendMode,
  261. const bool remove)
  262. {
  263. for (std::string const& name : names) {
  264. if (status.GetMakefile().IsAlias(name)) {
  265. status.SetError("can not be used on an ALIAS target.");
  266. return false;
  267. }
  268. if (cmTarget* target = status.GetMakefile().FindTargetToUse(name)) {
  269. // Handle the current target.
  270. if (!HandleTarget(target, status.GetMakefile(), propertyName,
  271. propertyValue, appendAsString, appendMode, remove)) {
  272. return false;
  273. }
  274. } else {
  275. std::ostringstream e;
  276. e << "could not find TARGET " << name
  277. << ". Perhaps it has not yet been created.";
  278. status.SetError(e.str());
  279. return false;
  280. }
  281. }
  282. return true;
  283. }
  284. bool HandleTarget(cmTarget* target, cmMakefile& makefile,
  285. const std::string& propertyName,
  286. const std::string& propertyValue, const bool appendAsString,
  287. const bool appendMode, const bool remove)
  288. {
  289. // Set or append the property.
  290. const char* value = propertyValue.c_str();
  291. if (remove) {
  292. value = nullptr;
  293. }
  294. if (appendMode) {
  295. target->AppendProperty(propertyName, value, appendAsString);
  296. } else {
  297. target->SetProperty(propertyName, value);
  298. }
  299. // Check the resulting value.
  300. target->CheckProperty(propertyName, &makefile);
  301. return true;
  302. }
  303. bool HandleSourceMode(cmExecutionStatus& status,
  304. const std::set<std::string>& names,
  305. const std::string& propertyName,
  306. const std::string& propertyValue,
  307. const bool appendAsString, const bool appendMode,
  308. const bool remove)
  309. {
  310. for (std::string const& name : names) {
  311. // Get the source file.
  312. if (cmSourceFile* sf = status.GetMakefile().GetOrCreateSource(name)) {
  313. if (!HandleSource(sf, propertyName, propertyValue, appendAsString,
  314. appendMode, remove)) {
  315. return false;
  316. }
  317. } else {
  318. std::ostringstream e;
  319. e << "given SOURCE name that could not be found or created: " << name;
  320. status.SetError(e.str());
  321. return false;
  322. }
  323. }
  324. return true;
  325. }
  326. bool HandleSource(cmSourceFile* sf, const std::string& propertyName,
  327. const std::string& propertyValue, const bool appendAsString,
  328. const bool appendMode, const bool remove)
  329. {
  330. // Set or append the property.
  331. const char* value = propertyValue.c_str();
  332. if (remove) {
  333. value = nullptr;
  334. }
  335. if (appendMode) {
  336. sf->AppendProperty(propertyName, value, appendAsString);
  337. } else {
  338. sf->SetProperty(propertyName, value);
  339. }
  340. return true;
  341. }
  342. bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
  343. const std::string& propertyName,
  344. const std::string& propertyValue,
  345. const bool appendAsString, const bool appendMode,
  346. const bool remove)
  347. {
  348. // Look for tests with all names given.
  349. std::set<std::string>::iterator next;
  350. for (auto ni = names.begin(); ni != names.end(); ni = next) {
  351. next = ni;
  352. ++next;
  353. if (cmTest* test = status.GetMakefile().GetTest(*ni)) {
  354. if (HandleTest(test, propertyName, propertyValue, appendAsString,
  355. appendMode, remove)) {
  356. names.erase(ni);
  357. } else {
  358. return false;
  359. }
  360. }
  361. }
  362. // Names that are still left were not found.
  363. if (!names.empty()) {
  364. std::ostringstream e;
  365. e << "given TEST names that do not exist:\n";
  366. for (std::string const& name : names) {
  367. e << " " << name << "\n";
  368. }
  369. status.SetError(e.str());
  370. return false;
  371. }
  372. return true;
  373. }
  374. bool HandleTest(cmTest* test, const std::string& propertyName,
  375. const std::string& propertyValue, const bool appendAsString,
  376. const bool appendMode, const bool remove)
  377. {
  378. // Set or append the property.
  379. const char* value = propertyValue.c_str();
  380. if (remove) {
  381. value = nullptr;
  382. }
  383. if (appendMode) {
  384. test->AppendProperty(propertyName, value, appendAsString);
  385. } else {
  386. test->SetProperty(propertyName, value);
  387. }
  388. return true;
  389. }
  390. bool HandleCacheMode(cmExecutionStatus& status,
  391. const std::set<std::string>& names,
  392. const std::string& propertyName,
  393. const std::string& propertyValue,
  394. const bool appendAsString, const bool appendMode,
  395. const bool remove)
  396. {
  397. if (propertyName == "ADVANCED") {
  398. if (!remove && !cmIsOn(propertyValue) && !cmIsOff(propertyValue)) {
  399. std::ostringstream e;
  400. e << "given non-boolean value \"" << propertyValue
  401. << R"(" for CACHE property "ADVANCED". )";
  402. status.SetError(e.str());
  403. return false;
  404. }
  405. } else if (propertyName == "TYPE") {
  406. if (!cmState::IsCacheEntryType(propertyValue)) {
  407. std::ostringstream e;
  408. e << "given invalid CACHE entry TYPE \"" << propertyValue << "\"";
  409. status.SetError(e.str());
  410. return false;
  411. }
  412. } else if (propertyName != "HELPSTRING" && propertyName != "STRINGS" &&
  413. propertyName != "VALUE") {
  414. std::ostringstream e;
  415. e << "given invalid CACHE property " << propertyName << ". "
  416. << "Settable CACHE properties are: "
  417. << "ADVANCED, HELPSTRING, STRINGS, TYPE, and VALUE.";
  418. status.SetError(e.str());
  419. return false;
  420. }
  421. for (std::string const& name : names) {
  422. // Get the source file.
  423. cmake* cm = status.GetMakefile().GetCMakeInstance();
  424. const char* existingValue = cm->GetState()->GetCacheEntryValue(name);
  425. if (existingValue) {
  426. if (!HandleCacheEntry(name, status.GetMakefile(), propertyName,
  427. propertyValue, appendAsString, appendMode,
  428. remove)) {
  429. return false;
  430. }
  431. } else {
  432. std::ostringstream e;
  433. e << "could not find CACHE variable " << name
  434. << ". Perhaps it has not yet been created.";
  435. status.SetError(e.str());
  436. return false;
  437. }
  438. }
  439. return true;
  440. }
  441. bool HandleCacheEntry(std::string const& cacheKey, const cmMakefile& makefile,
  442. const std::string& propertyName,
  443. const std::string& propertyValue,
  444. const bool appendAsString, const bool appendMode,
  445. const bool remove)
  446. {
  447. // Set or append the property.
  448. const char* value = propertyValue.c_str();
  449. cmState* state = makefile.GetState();
  450. if (remove) {
  451. state->RemoveCacheEntryProperty(cacheKey, propertyName);
  452. }
  453. if (appendMode) {
  454. state->AppendCacheEntryProperty(cacheKey, propertyName, value,
  455. appendAsString);
  456. } else {
  457. state->SetCacheEntryProperty(cacheKey, propertyName, value);
  458. }
  459. return true;
  460. }
  461. bool HandleInstallMode(cmExecutionStatus& status,
  462. const std::set<std::string>& names,
  463. const std::string& propertyName,
  464. const std::string& propertyValue,
  465. const bool appendAsString, const bool appendMode,
  466. const bool remove)
  467. {
  468. cmake* cm = status.GetMakefile().GetCMakeInstance();
  469. for (std::string const& name : names) {
  470. if (cmInstalledFile* file =
  471. cm->GetOrCreateInstalledFile(&status.GetMakefile(), name)) {
  472. if (!HandleInstall(file, status.GetMakefile(), propertyName,
  473. propertyValue, appendAsString, appendMode, remove)) {
  474. return false;
  475. }
  476. } else {
  477. std::ostringstream e;
  478. e << "given INSTALL name that could not be found or created: " << name;
  479. status.SetError(e.str());
  480. return false;
  481. }
  482. }
  483. return true;
  484. }
  485. bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile,
  486. const std::string& propertyName,
  487. const std::string& propertyValue, const bool appendAsString,
  488. const bool appendMode, const bool remove)
  489. {
  490. // Set or append the property.
  491. const char* value = propertyValue.c_str();
  492. if (remove) {
  493. file->RemoveProperty(propertyName);
  494. } else if (appendMode) {
  495. file->AppendProperty(&makefile, propertyName, value, appendAsString);
  496. } else {
  497. file->SetProperty(&makefile, propertyName, value);
  498. }
  499. return true;
  500. }
  501. }