|  | /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | 
|  | file Copyright.txt or https://cmake.org/licensing for details.  */ | 
|  | #include "cmParseArgumentsCommand.h" | 
|  |  | 
|  | #include <map> | 
|  | #include <set> | 
|  | #include <sstream> | 
|  | #include <utility> | 
|  |  | 
|  | #include <cm/string_view> | 
|  |  | 
|  | #include "cmArgumentParser.h" | 
|  | #include "cmExecutionStatus.h" | 
|  | #include "cmMakefile.h" | 
|  | #include "cmMessageType.h" | 
|  | #include "cmProperty.h" | 
|  | #include "cmRange.h" | 
|  | #include "cmStringAlgorithms.h" | 
|  | #include "cmSystemTools.h" | 
|  |  | 
|  | static std::string EscapeArg(const std::string& arg) | 
|  | { | 
|  | // replace ";" with "\;" so output argument lists will split correctly | 
|  | std::string escapedArg; | 
|  | for (char i : arg) { | 
|  | if (i == ';') { | 
|  | escapedArg += '\\'; | 
|  | } | 
|  | escapedArg += i; | 
|  | } | 
|  | return escapedArg; | 
|  | } | 
|  |  | 
|  | static std::string JoinList(std::vector<std::string> const& arg, bool escape) | 
|  | { | 
|  | return escape ? cmJoin(cmMakeRange(arg).transform(EscapeArg), ";") | 
|  | : cmJoin(cmMakeRange(arg), ";"); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using options_map = std::map<std::string, bool>; | 
|  | using single_map = std::map<std::string, std::string>; | 
|  | using multi_map = std::map<std::string, std::vector<std::string>>; | 
|  | using options_set = std::set<std::string>; | 
|  |  | 
|  | struct UserArgumentParser : public cmArgumentParser<void> | 
|  | { | 
|  | template <typename T, typename H> | 
|  | void Bind(std::vector<std::string> const& names, | 
|  | std::map<std::string, T>& ref, H duplicateKey) | 
|  | { | 
|  | for (std::string const& key : names) { | 
|  | auto const it = ref.emplace(key, T{}).first; | 
|  | bool const inserted = this->cmArgumentParser<void>::Bind( | 
|  | cm::string_view(it->first), it->second); | 
|  | if (!inserted) { | 
|  | duplicateKey(key); | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // namespace | 
|  |  | 
|  | static void PassParsedArguments( | 
|  | const std::string& prefix, cmMakefile& makefile, const options_map& options, | 
|  | const single_map& singleValArgs, const multi_map& multiValArgs, | 
|  | const std::vector<std::string>& unparsed, | 
|  | const options_set& keywordsMissingValues, bool parseFromArgV) | 
|  | { | 
|  | for (auto const& iter : options) { | 
|  | makefile.AddDefinition(prefix + iter.first, | 
|  | iter.second ? "TRUE" : "FALSE"); | 
|  | } | 
|  |  | 
|  | for (auto const& iter : singleValArgs) { | 
|  | if (!iter.second.empty()) { | 
|  | makefile.AddDefinition(prefix + iter.first, iter.second); | 
|  | } else { | 
|  | makefile.RemoveDefinition(prefix + iter.first); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (auto const& iter : multiValArgs) { | 
|  | if (!iter.second.empty()) { | 
|  | makefile.AddDefinition(prefix + iter.first, | 
|  | JoinList(iter.second, parseFromArgV)); | 
|  | } else { | 
|  | makefile.RemoveDefinition(prefix + iter.first); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!unparsed.empty()) { | 
|  | makefile.AddDefinition(prefix + "UNPARSED_ARGUMENTS", | 
|  | JoinList(unparsed, parseFromArgV)); | 
|  | } else { | 
|  | makefile.RemoveDefinition(prefix + "UNPARSED_ARGUMENTS"); | 
|  | } | 
|  |  | 
|  | if (!keywordsMissingValues.empty()) { | 
|  | makefile.AddDefinition(prefix + "KEYWORDS_MISSING_VALUES", | 
|  | cmJoin(cmMakeRange(keywordsMissingValues), ";")); | 
|  | } else { | 
|  | makefile.RemoveDefinition(prefix + "KEYWORDS_MISSING_VALUES"); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool cmParseArgumentsCommand(std::vector<std::string> const& args, | 
|  | cmExecutionStatus& status) | 
|  | { | 
|  | // cmake_parse_arguments(prefix options single multi <ARGN>) | 
|  | //                         1       2      3      4 | 
|  | // or | 
|  | // cmake_parse_arguments(PARSE_ARGV N prefix options single multi) | 
|  | if (args.size() < 4) { | 
|  | status.SetError("must be called with at least 4 arguments."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | auto argIter = args.begin(); | 
|  | auto argEnd = args.end(); | 
|  | bool parseFromArgV = false; | 
|  | unsigned long argvStart = 0; | 
|  | if (*argIter == "PARSE_ARGV") { | 
|  | if (args.size() != 6) { | 
|  | status.GetMakefile().IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | "PARSE_ARGV must be called with exactly 6 arguments."); | 
|  | cmSystemTools::SetFatalErrorOccured(); | 
|  | return true; | 
|  | } | 
|  | parseFromArgV = true; | 
|  | argIter++; // move past PARSE_ARGV | 
|  | if (!cmStrToULong(*argIter, &argvStart)) { | 
|  | status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, | 
|  | "PARSE_ARGV index '" + *argIter + | 
|  | "' is not an unsigned integer"); | 
|  | cmSystemTools::SetFatalErrorOccured(); | 
|  | return true; | 
|  | } | 
|  | argIter++; // move past N | 
|  | } | 
|  | // the first argument is the prefix | 
|  | const std::string prefix = (*argIter++) + "_"; | 
|  |  | 
|  | UserArgumentParser parser; | 
|  |  | 
|  | // define the result maps holding key/value pairs for | 
|  | // options, single values and multi values | 
|  | options_map options; | 
|  | single_map singleValArgs; | 
|  | multi_map multiValArgs; | 
|  |  | 
|  | // anything else is put into a vector of unparsed strings | 
|  | std::vector<std::string> unparsed; | 
|  |  | 
|  | auto const duplicateKey = [&status](std::string const& key) { | 
|  | status.GetMakefile().IssueMessage( | 
|  | MessageType::WARNING, "keyword defined more than once: " + key); | 
|  | }; | 
|  |  | 
|  | // the second argument is a (cmake) list of options without argument | 
|  | std::vector<std::string> list = cmExpandedList(*argIter++); | 
|  | parser.Bind(list, options, duplicateKey); | 
|  |  | 
|  | // the third argument is a (cmake) list of single argument options | 
|  | list.clear(); | 
|  | cmExpandList(*argIter++, list); | 
|  | parser.Bind(list, singleValArgs, duplicateKey); | 
|  |  | 
|  | // the fourth argument is a (cmake) list of multi argument options | 
|  | list.clear(); | 
|  | cmExpandList(*argIter++, list); | 
|  | parser.Bind(list, multiValArgs, duplicateKey); | 
|  |  | 
|  | list.clear(); | 
|  | if (!parseFromArgV) { | 
|  | // Flatten ;-lists in the arguments into a single list as was done | 
|  | // by the original function(CMAKE_PARSE_ARGUMENTS). | 
|  | for (; argIter != argEnd; ++argIter) { | 
|  | cmExpandList(*argIter, list); | 
|  | } | 
|  | } else { | 
|  | // in the PARSE_ARGV move read the arguments from ARGC and ARGV# | 
|  | std::string argc = status.GetMakefile().GetSafeDefinition("ARGC"); | 
|  | unsigned long count; | 
|  | if (!cmStrToULong(argc, &count)) { | 
|  | status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, | 
|  | "PARSE_ARGV called with ARGC='" + | 
|  | argc + | 
|  | "' that is not an unsigned integer"); | 
|  | cmSystemTools::SetFatalErrorOccured(); | 
|  | return true; | 
|  | } | 
|  | for (unsigned long i = argvStart; i < count; ++i) { | 
|  | std::ostringstream argName; | 
|  | argName << "ARGV" << i; | 
|  | cmProp arg = status.GetMakefile().GetDefinition(argName.str()); | 
|  | if (!arg) { | 
|  | status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, | 
|  | "PARSE_ARGV called with " + | 
|  | argName.str() + " not set"); | 
|  | cmSystemTools::SetFatalErrorOccured(); | 
|  | return true; | 
|  | } | 
|  | list.emplace_back(*arg); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<std::string> keywordsMissingValues; | 
|  |  | 
|  | parser.Parse(list, &unparsed, &keywordsMissingValues); | 
|  |  | 
|  | PassParsedArguments( | 
|  | prefix, status.GetMakefile(), options, singleValArgs, multiValArgs, | 
|  | unparsed, | 
|  | options_set(keywordsMissingValues.begin(), keywordsMissingValues.end()), | 
|  | parseFromArgV); | 
|  |  | 
|  | return true; | 
|  | } |