Browse Source

cmake_language(CALL): Accept empty ${var} expansions

Factor out an internal helper.  Generalize partial argument expansion
and call the helper on a clean boundary between raw arguments.
Brad King 5 years ago
parent
commit
4f33f3dcff

+ 65 - 63
Source/cmCMakeLanguageCommand.cxx

@@ -29,6 +29,38 @@ std::array<cm::static_string_view, 12> InvalidCommands{
   } // clang-format on
 };
 
+bool cmCMakeLanguageCommandCALL(std::vector<cmListFileArgument> const& args,
+                                std::string const& callCommand,
+                                size_t startArg, cmExecutionStatus& status)
+{
+  // ensure specified command is valid
+  // start/end flow control commands are not allowed
+  auto cmd = cmSystemTools::LowerCase(callCommand);
+  if (std::find(InvalidCommands.cbegin(), InvalidCommands.cend(), cmd) !=
+      InvalidCommands.cend()) {
+    status.SetError(cmStrCat("invalid command specified: "_s, callCommand));
+    return false;
+  }
+
+  cmMakefile& makefile = status.GetMakefile();
+  cmListFileContext context = makefile.GetBacktrace().Top();
+
+  cmListFileFunction func;
+  func.Name = callCommand;
+  func.Line = context.Line;
+
+  // The rest of the arguments are passed to the function call above
+  for (size_t i = startArg; i < args.size(); ++i) {
+    cmListFileArgument lfarg;
+    lfarg.Delim = args[i].Delim;
+    lfarg.Line = context.Line;
+    lfarg.Value = args[i].Value;
+    func.Arguments.emplace_back(lfarg);
+  }
+
+  return makefile.ExecuteCommand(func, status);
+}
+
 bool cmCMakeLanguageCommandEVAL(std::vector<cmListFileArgument> const& args,
                                 cmExecutionStatus& status)
 {
@@ -64,82 +96,52 @@ bool cmCMakeLanguageCommandEVAL(std::vector<cmListFileArgument> const& args,
 bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
                             cmExecutionStatus& status)
 {
-  if (args.empty()) {
-    status.SetError("called with incorrect number of arguments");
-    return false;
-  }
-
-  cmMakefile& makefile = status.GetMakefile();
-  cmListFileContext context = makefile.GetBacktrace().Top();
-
-  bool result = false;
-
-  std::vector<std::string> dispatchExpandedArgs;
-  std::vector<cmListFileArgument> dispatchArgs;
-  dispatchArgs.emplace_back(args[0]);
-  makefile.ExpandArguments(dispatchArgs, dispatchExpandedArgs);
+  std::vector<std::string> expArgs;
+  size_t rawArg = 0;
+  size_t expArg = 0;
+
+  // Helper to consume and expand one raw argument at a time.
+  auto moreArgs = [&]() -> bool {
+    while (expArg >= expArgs.size()) {
+      if (rawArg >= args.size()) {
+        return false;
+      }
+      std::vector<cmListFileArgument> tmpArg;
+      tmpArg.emplace_back(args[rawArg++]);
+      status.GetMakefile().ExpandArguments(tmpArg, expArgs);
+    }
+    return true;
+  };
 
-  if (dispatchExpandedArgs.empty()) {
+  if (!moreArgs()) {
     status.SetError("called with incorrect number of arguments");
     return false;
   }
 
-  if (dispatchExpandedArgs[0] == "CALL") {
-    if ((args.size() == 1 && dispatchExpandedArgs.size() != 2) ||
-        dispatchExpandedArgs.size() > 2) {
-      status.SetError("called with incorrect number of arguments");
-      return false;
-    }
+  if (expArgs[expArg] == "CALL") {
+    ++expArg; // Consume "CALL".
 
-    // First argument is the name of the function to call
-    std::string callCommand;
-    size_t startArg;
-    if (dispatchExpandedArgs.size() == 1) {
-      std::vector<std::string> functionExpandedArg;
-      std::vector<cmListFileArgument> functionArg;
-      functionArg.emplace_back(args[1]);
-      makefile.ExpandArguments(functionArg, functionExpandedArg);
-
-      if (functionExpandedArg.size() != 1) {
-        status.SetError("called with incorrect number of arguments");
-        return false;
-      }
-
-      callCommand = functionExpandedArg[0];
-      startArg = 2;
-    } else {
-      callCommand = dispatchExpandedArgs[1];
-      startArg = 1;
+    // CALL requires a command name.
+    if (!moreArgs()) {
+      status.SetError("CALL missing command name");
+      return false;
     }
+    std::string const& callCommand = expArgs[expArg++];
 
-    // ensure specified command is valid
-    // start/end flow control commands are not allowed
-    auto cmd = cmSystemTools::LowerCase(callCommand);
-    if (std::find(InvalidCommands.cbegin(), InvalidCommands.cend(), cmd) !=
-        InvalidCommands.cend()) {
-      status.SetError(cmStrCat("invalid command specified: "_s, callCommand));
+    // CALL accepts no further expanded arguments.
+    if (expArg != expArgs.size()) {
+      status.SetError("CALL command's arguments must be literal");
       return false;
     }
 
-    cmListFileFunction func;
-    func.Name = callCommand;
-    func.Line = context.Line;
-
-    // The rest of the arguments are passed to the function call above
-    for (size_t i = startArg; i < args.size(); ++i) {
-      cmListFileArgument lfarg;
-      lfarg.Delim = args[i].Delim;
-      lfarg.Line = context.Line;
-      lfarg.Value = args[i].Value;
-      func.Arguments.emplace_back(lfarg);
-    }
+    // Run the CALL.
+    return cmCMakeLanguageCommandCALL(args, callCommand, rawArg, status);
+  }
 
-    result = makefile.ExecuteCommand(func, status);
-  } else if (dispatchExpandedArgs[0] == "EVAL") {
+  if (expArgs[expArg] == "EVAL") {
     return cmCMakeLanguageCommandEVAL(args, status);
-  } else {
-    status.SetError("called with unknown meta-operation");
   }
 
-  return result;
+  status.SetError("called with unknown meta-operation");
+  return false;
 }

+ 1 - 1
Tests/RunCMake/cmake_language/call_expand_command_name.cmake

@@ -1,2 +1,2 @@
 set (my_call "CALL")
-cmake_language (${my_call} message "OK!")
+cmake_language (${my_call} ${empty} message "OK!")

+ 1 - 1
Tests/RunCMake/cmake_language/call_expanded_command.cmake

@@ -3,4 +3,4 @@ function (itsok)
 endfunction()
 
 set (cmd CALL itsok)
-cmake_language (${cmd})
+cmake_language (${empty} ${cmd})

+ 1 - 1
Tests/RunCMake/cmake_language/call_expanded_command_and_arguments-stderr.txt

@@ -1,4 +1,4 @@
 CMake Error at call_expanded_command_and_arguments.cmake:2 \(cmake_language\):
-  cmake_language called with incorrect number of arguments
+  cmake_language CALL command's arguments must be literal
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)

+ 1 - 1
Tests/RunCMake/cmake_language/call_message.cmake

@@ -1 +1 @@
-cmake_language(CALL message WORKS!)
+cmake_language(CALL ${empty} message WORKS!)

+ 1 - 1
Tests/RunCMake/cmake_language/call_no_parameters-stderr.txt

@@ -1,2 +1,2 @@
 CMake Error at call_no_parameters.cmake:1 \(cmake_language\):
-  cmake_language called with incorrect number of arguments
+  cmake_language CALL missing command name

+ 1 - 1
Tests/RunCMake/cmake_language/no_parameters-stderr.txt

@@ -1,2 +1,2 @@
 CMake Error at no_parameters.cmake:1 \(cmake_language\):
-  cmake_language called with incorrect number of arguments
+  cmake_language CALL missing command name