cmParseArgumentsCommand.cxx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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 "cmParseArgumentsCommand.h"
  4. #include <map>
  5. #include <set>
  6. #include <sstream>
  7. #include <stddef.h>
  8. #include <utility>
  9. #include "cmAlgorithms.h"
  10. #include "cmMakefile.h"
  11. #include "cmSystemTools.h"
  12. #include "cmake.h"
  13. class cmExecutionStatus;
  14. static std::string escape_arg(const std::string& arg)
  15. {
  16. // replace ";" with "\;" so output argument lists will split correctly
  17. std::string escapedArg;
  18. for (size_t i = 0; i < arg.size(); ++i) {
  19. if (arg[i] == ';') {
  20. escapedArg += '\\';
  21. }
  22. escapedArg += arg[i];
  23. }
  24. return escapedArg;
  25. }
  26. bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
  27. cmExecutionStatus&)
  28. {
  29. // cmake_parse_arguments(prefix options single multi <ARGN>)
  30. // 1 2 3 4
  31. // or
  32. // cmake_parse_arguments(PARSE_ARGV N prefix options single multi)
  33. if (args.size() < 4) {
  34. this->SetError("must be called with at least 4 arguments.");
  35. return false;
  36. }
  37. std::vector<std::string>::const_iterator argIter = args.begin(),
  38. argEnd = args.end();
  39. bool parseFromArgV = false;
  40. unsigned long argvStart = 0;
  41. if (*argIter == "PARSE_ARGV") {
  42. if (args.size() != 6) {
  43. this->Makefile->IssueMessage(
  44. cmake::FATAL_ERROR,
  45. "PARSE_ARGV must be called with exactly 6 arguments.");
  46. cmSystemTools::SetFatalErrorOccured();
  47. return true;
  48. }
  49. parseFromArgV = true;
  50. argIter++; // move past PARSE_ARGV
  51. if (!cmSystemTools::StringToULong(argIter->c_str(), &argvStart)) {
  52. this->Makefile->IssueMessage(cmake::FATAL_ERROR, "PARSE_ARGV index '" +
  53. *argIter +
  54. "' is not an unsigned integer");
  55. cmSystemTools::SetFatalErrorOccured();
  56. return true;
  57. }
  58. argIter++; // move past N
  59. }
  60. // the first argument is the prefix
  61. const std::string prefix = (*argIter++) + "_";
  62. // define the result maps holding key/value pairs for
  63. // options, single values and multi values
  64. typedef std::map<std::string, bool> options_map;
  65. typedef std::map<std::string, std::string> single_map;
  66. typedef std::map<std::string, std::vector<std::string> > multi_map;
  67. options_map options;
  68. single_map single;
  69. multi_map multi;
  70. // anything else is put into a vector of unparsed strings
  71. std::vector<std::string> unparsed;
  72. // remember already defined keywords
  73. std::set<std::string> used_keywords;
  74. const std::string dup_warning = "keyword defined more than once: ";
  75. // the second argument is a (cmake) list of options without argument
  76. std::vector<std::string> list;
  77. cmSystemTools::ExpandListArgument(*argIter++, list);
  78. for (std::vector<std::string>::const_iterator iter = list.begin(),
  79. end = list.end();
  80. iter != end; ++iter) {
  81. if (!used_keywords.insert(*iter).second) {
  82. this->GetMakefile()->IssueMessage(cmake::WARNING, dup_warning + *iter);
  83. }
  84. options[*iter]; // default initialize
  85. }
  86. // the third argument is a (cmake) list of single argument options
  87. list.clear();
  88. cmSystemTools::ExpandListArgument(*argIter++, list);
  89. for (std::vector<std::string>::const_iterator iter = list.begin(),
  90. end = list.end();
  91. iter != end; ++iter) {
  92. if (!used_keywords.insert(*iter).second) {
  93. this->GetMakefile()->IssueMessage(cmake::WARNING, dup_warning + *iter);
  94. }
  95. single[*iter]; // default initialize
  96. }
  97. // the fourth argument is a (cmake) list of multi argument options
  98. list.clear();
  99. cmSystemTools::ExpandListArgument(*argIter++, list);
  100. for (std::vector<std::string>::const_iterator iter = list.begin(),
  101. end = list.end();
  102. iter != end; ++iter) {
  103. if (!used_keywords.insert(*iter).second) {
  104. this->GetMakefile()->IssueMessage(cmake::WARNING, dup_warning + *iter);
  105. }
  106. multi[*iter]; // default initialize
  107. }
  108. enum insideValues
  109. {
  110. NONE,
  111. SINGLE,
  112. MULTI
  113. } insideValues = NONE;
  114. std::string currentArgName;
  115. list.clear();
  116. if (!parseFromArgV) {
  117. // Flatten ;-lists in the arguments into a single list as was done
  118. // by the original function(CMAKE_PARSE_ARGUMENTS).
  119. for (; argIter != argEnd; ++argIter) {
  120. cmSystemTools::ExpandListArgument(*argIter, list);
  121. }
  122. } else {
  123. // in the PARSE_ARGV move read the arguments from ARGC and ARGV#
  124. std::string argc = this->Makefile->GetSafeDefinition("ARGC");
  125. unsigned long count;
  126. if (!cmSystemTools::StringToULong(argc.c_str(), &count)) {
  127. this->Makefile->IssueMessage(cmake::FATAL_ERROR,
  128. "PARSE_ARGV called with ARGC='" + argc +
  129. "' that is not an unsigned integer");
  130. cmSystemTools::SetFatalErrorOccured();
  131. return true;
  132. }
  133. for (unsigned long i = argvStart; i < count; ++i) {
  134. std::ostringstream argName;
  135. argName << "ARGV" << i;
  136. const char* arg = this->Makefile->GetDefinition(argName.str());
  137. if (!arg) {
  138. this->Makefile->IssueMessage(cmake::FATAL_ERROR,
  139. "PARSE_ARGV called with " +
  140. argName.str() + " not set");
  141. cmSystemTools::SetFatalErrorOccured();
  142. return true;
  143. }
  144. list.push_back(arg);
  145. }
  146. }
  147. // iterate over the arguments list and fill in the values where applicable
  148. for (argIter = list.begin(), argEnd = list.end(); argIter != argEnd;
  149. ++argIter) {
  150. const options_map::iterator optIter = options.find(*argIter);
  151. if (optIter != options.end()) {
  152. insideValues = NONE;
  153. optIter->second = true;
  154. continue;
  155. }
  156. const single_map::iterator singleIter = single.find(*argIter);
  157. if (singleIter != single.end()) {
  158. insideValues = SINGLE;
  159. currentArgName = *argIter;
  160. continue;
  161. }
  162. const multi_map::iterator multiIter = multi.find(*argIter);
  163. if (multiIter != multi.end()) {
  164. insideValues = MULTI;
  165. currentArgName = *argIter;
  166. continue;
  167. }
  168. switch (insideValues) {
  169. case SINGLE:
  170. single[currentArgName] = *argIter;
  171. insideValues = NONE;
  172. break;
  173. case MULTI:
  174. if (parseFromArgV) {
  175. multi[currentArgName].push_back(escape_arg(*argIter));
  176. } else {
  177. multi[currentArgName].push_back(*argIter);
  178. }
  179. break;
  180. default:
  181. if (parseFromArgV) {
  182. unparsed.push_back(escape_arg(*argIter));
  183. } else {
  184. unparsed.push_back(*argIter);
  185. }
  186. break;
  187. }
  188. }
  189. // now iterate over the collected values and update their definition
  190. // within the current scope. undefine if necessary.
  191. for (options_map::const_iterator iter = options.begin(), end = options.end();
  192. iter != end; ++iter) {
  193. this->Makefile->AddDefinition(prefix + iter->first,
  194. iter->second ? "TRUE" : "FALSE");
  195. }
  196. for (single_map::const_iterator iter = single.begin(), end = single.end();
  197. iter != end; ++iter) {
  198. if (!iter->second.empty()) {
  199. this->Makefile->AddDefinition(prefix + iter->first,
  200. iter->second.c_str());
  201. } else {
  202. this->Makefile->RemoveDefinition(prefix + iter->first);
  203. }
  204. }
  205. for (multi_map::const_iterator iter = multi.begin(), end = multi.end();
  206. iter != end; ++iter) {
  207. if (!iter->second.empty()) {
  208. this->Makefile->AddDefinition(
  209. prefix + iter->first, cmJoin(cmMakeRange(iter->second), ";").c_str());
  210. } else {
  211. this->Makefile->RemoveDefinition(prefix + iter->first);
  212. }
  213. }
  214. if (!unparsed.empty()) {
  215. this->Makefile->AddDefinition(prefix + "UNPARSED_ARGUMENTS",
  216. cmJoin(cmMakeRange(unparsed), ";").c_str());
  217. } else {
  218. this->Makefile->RemoveDefinition(prefix + "UNPARSED_ARGUMENTS");
  219. }
  220. return true;
  221. }