cmSetCommand.cxx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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 "cmSetCommand.h"
  4. #include <algorithm>
  5. #include <cm/optional>
  6. #include <cm/string_view>
  7. #include <cmext/string_view>
  8. #include "cmArgumentParser.h"
  9. #include "cmArgumentParserTypes.h"
  10. #include "cmExecutionStatus.h"
  11. #include "cmList.h"
  12. #include "cmMakefile.h"
  13. #include "cmMessageType.h"
  14. #include "cmRange.h"
  15. #include "cmState.h"
  16. #include "cmStateTypes.h"
  17. #include "cmStringAlgorithms.h"
  18. #include "cmSystemTools.h"
  19. #include "cmValue.h"
  20. // cmSetCommand
  21. bool cmSetCommand(std::vector<std::string> const& args,
  22. cmExecutionStatus& status)
  23. {
  24. if (args.empty()) {
  25. status.SetError("called with incorrect number of arguments");
  26. return false;
  27. }
  28. auto const& variable = args[0]; // VAR is always first
  29. // watch for ENV{} signature
  30. if (cmHasLiteralPrefix(variable, "ENV{") && variable.size() > 5) {
  31. // what is the variable name
  32. auto const& varName = variable.substr(4, variable.size() - 5);
  33. std::string putEnvArg = varName + "=";
  34. // what is the current value if any
  35. std::string currValue;
  36. bool const currValueSet = cmSystemTools::GetEnv(varName, currValue);
  37. // will it be set to something, then set it
  38. if (args.size() > 1 && !args[1].empty()) {
  39. // but only if it is different from current value
  40. if (!currValueSet || currValue != args[1]) {
  41. putEnvArg += args[1];
  42. cmSystemTools::PutEnv(putEnvArg);
  43. }
  44. // if there's extra arguments, warn user
  45. // that they are ignored by this command.
  46. if (args.size() > 2) {
  47. std::string m = "Only the first value argument is used when setting "
  48. "an environment variable. Argument '" +
  49. args[2] + "' and later are unused.";
  50. status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, m);
  51. }
  52. return true;
  53. }
  54. // if it will be cleared, then clear it if it isn't already clear
  55. if (currValueSet) {
  56. cmSystemTools::PutEnv(putEnvArg);
  57. }
  58. return true;
  59. }
  60. // watch for CACHE{} signature
  61. if (cmHasLiteralPrefix(variable, "CACHE{") && variable.size() > 7 &&
  62. cmHasLiteralSuffix(variable, "}")) {
  63. // what is the variable name
  64. auto const& varName = variable.substr(6, variable.size() - 7);
  65. // VALUE handling
  66. auto valueArg = std::find(args.cbegin() + 1, args.cend(), "VALUE");
  67. if (valueArg == args.cend()) {
  68. status.SetError("Required argument 'VALUE' is missing.");
  69. return false;
  70. }
  71. auto value = cmMakeRange(valueArg + 1, args.cend());
  72. // Handle options
  73. struct Arguments : public ArgumentParser::ParseResult
  74. {
  75. ArgumentParser::Continue validateTypeValue(cm::string_view type)
  76. {
  77. if (!cmState::StringToCacheEntryType(std::string{ type },
  78. this->Type)) {
  79. this->AddKeywordError("TYPE"_s,
  80. cmStrCat("Invalid value: ", type, '.'));
  81. }
  82. return ArgumentParser::Continue::No;
  83. }
  84. cmStateEnums::CacheEntryType Type = cmStateEnums::UNINITIALIZED;
  85. cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> Help;
  86. bool Force = false;
  87. };
  88. static auto const optionsParser =
  89. cmArgumentParser<Arguments>{}
  90. .Bind("TYPE"_s, &Arguments::validateTypeValue)
  91. .Bind("HELP"_s, &Arguments::Help)
  92. .Bind("FORCE"_s, &Arguments::Force);
  93. std::vector<std::string> unrecognizedArguments;
  94. auto parsedArgs = optionsParser.Parse(
  95. cmMakeRange(args.cbegin() + 1, valueArg), &unrecognizedArguments);
  96. if (!unrecognizedArguments.empty()) {
  97. status.SetError(cmStrCat("Called with unsupported argument(s): ",
  98. cmJoin(unrecognizedArguments, ", "_s), '.'));
  99. return false;
  100. }
  101. if (parsedArgs.MaybeReportError(status.GetMakefile())) {
  102. return false;
  103. }
  104. // see if this is already in the cache
  105. cmState* state = status.GetMakefile().GetState();
  106. cmValue existingValue = state->GetCacheEntryValue(varName);
  107. cmStateEnums::CacheEntryType existingType =
  108. state->GetCacheEntryType(varName);
  109. if (parsedArgs.Type == cmStateEnums::UNINITIALIZED) {
  110. parsedArgs.Type = existingType == cmStateEnums::UNINITIALIZED
  111. ? cmStateEnums::STRING
  112. : existingType;
  113. }
  114. std::string help = parsedArgs.Help
  115. ? cmJoin(*parsedArgs.Help, "")
  116. : *state->GetCacheEntryProperty(varName, "HELPSTRING");
  117. if (existingValue && existingType != cmStateEnums::UNINITIALIZED) {
  118. // if the set is trying to CACHE the value but the value
  119. // is already in the cache and the type is not internal
  120. // then leave now without setting any definitions in the cache
  121. // or the makefile
  122. if (parsedArgs.Type != cmStateEnums::INTERNAL && !parsedArgs.Force) {
  123. return true;
  124. }
  125. }
  126. status.GetMakefile().AddCacheDefinition(
  127. varName,
  128. valueArg == args.cend() ? *existingValue : cmList::to_string(value),
  129. help, parsedArgs.Type, parsedArgs.Force);
  130. return true;
  131. }
  132. // SET (VAR) // Removes the definition of VAR.
  133. if (args.size() == 1) {
  134. status.GetMakefile().RemoveDefinition(variable);
  135. return true;
  136. }
  137. // SET (VAR PARENT_SCOPE) // Removes the definition of VAR
  138. // in the parent scope.
  139. if (args.size() == 2 && args.back() == "PARENT_SCOPE") {
  140. status.GetMakefile().RaiseScope(variable, nullptr);
  141. return true;
  142. }
  143. // here are the remaining options
  144. // SET (VAR value )
  145. // SET (VAR value PARENT_SCOPE)
  146. // SET (VAR CACHE TYPE "doc String" [FORCE])
  147. // SET (VAR value CACHE TYPE "doc string" [FORCE])
  148. std::string value; // optional
  149. bool cache = false; // optional
  150. bool force = false; // optional
  151. bool parentScope = false;
  152. cmStateEnums::CacheEntryType type =
  153. cmStateEnums::STRING; // required if cache
  154. cmValue docstring; // required if cache
  155. unsigned int ignoreLastArgs = 0;
  156. // look for PARENT_SCOPE argument
  157. if (args.size() > 1 && args.back() == "PARENT_SCOPE") {
  158. parentScope = true;
  159. ignoreLastArgs++;
  160. } else {
  161. // look for FORCE argument
  162. if (args.size() > 4 && args.back() == "FORCE") {
  163. force = true;
  164. ignoreLastArgs++;
  165. }
  166. // check for cache signature
  167. if (args.size() > 3 &&
  168. args[args.size() - 3 - (force ? 1 : 0)] == "CACHE") {
  169. cache = true;
  170. ignoreLastArgs += 3;
  171. }
  172. }
  173. // collect any values into a single semi-colon separated value list
  174. value =
  175. cmList::to_string(cmMakeRange(args).advance(1).retreat(ignoreLastArgs));
  176. if (parentScope) {
  177. status.GetMakefile().RaiseScope(variable, value.c_str());
  178. return true;
  179. }
  180. // we should be nice and try to catch some simple screwups if the last or
  181. // next to last args are CACHE then they screwed up. If they used FORCE
  182. // without CACHE they screwed up
  183. if (args.back() == "CACHE") {
  184. status.SetError(
  185. "given invalid arguments for CACHE mode: missing type and docstring");
  186. return false;
  187. }
  188. if (args.size() > 1 && args[args.size() - 2] == "CACHE") {
  189. status.SetError(
  190. "given invalid arguments for CACHE mode: missing type or docstring");
  191. return false;
  192. }
  193. if (force && !cache) {
  194. status.SetError("given invalid arguments: FORCE specified without CACHE");
  195. return false;
  196. }
  197. if (cache) {
  198. std::string::size_type cacheStart = args.size() - 3 - (force ? 1 : 0);
  199. if (!cmState::StringToCacheEntryType(args[cacheStart + 1], type)) {
  200. std::string m = "implicitly converting '" + args[cacheStart + 1] +
  201. "' to 'STRING' type.";
  202. status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, m);
  203. // Setting this may not be required, since it's
  204. // initialized as a string. Keeping this here to
  205. // ensure that the type is actually converting to a string.
  206. type = cmStateEnums::STRING;
  207. }
  208. docstring = cmValue{ args[cacheStart + 2] };
  209. }
  210. // see if this is already in the cache
  211. cmState* state = status.GetMakefile().GetState();
  212. cmValue existingValue = state->GetCacheEntryValue(variable);
  213. if (existingValue &&
  214. (state->GetCacheEntryType(variable) != cmStateEnums::UNINITIALIZED)) {
  215. // if the set is trying to CACHE the value but the value
  216. // is already in the cache and the type is not internal
  217. // then leave now without setting any definitions in the cache
  218. // or the makefile
  219. if (cache && type != cmStateEnums::INTERNAL && !force) {
  220. return true;
  221. }
  222. }
  223. // if it is meant to be in the cache then define it in the cache
  224. if (cache) {
  225. status.GetMakefile().AddCacheDefinition(variable, cmValue{ value },
  226. docstring, type, force);
  227. } else {
  228. // add the definition
  229. status.GetMakefile().AddDefinition(variable, value);
  230. }
  231. return true;
  232. }