Преглед изворни кода

execute_process: Add option to echo command lines

Add COMMAND_ECHO option to the execute_process command. This will allow
execute_process to show the command it will run. Also add a cmake variable
CMAKE_EXECUTE_PROCESS_COMMAND_ECHO. Both the option and the variable can
be set to one of the following: STDERR|STDOUT|NONE. The command will be
printed to stderr or stdout or not at all.

Fixes: #18933
Bill Hoffman пре 6 година
родитељ
комит
044dcf9f8d

+ 5 - 0
Help/command/execute_process.rst

@@ -18,6 +18,7 @@ Execute one or more child processes.
                   [ERROR_FILE <file>]
                   [OUTPUT_QUIET]
                   [ERROR_QUIET]
+                  [COMMAND_ECHO <where>]
                   [OUTPUT_STRIP_TRAILING_WHITESPACE]
                   [ERROR_STRIP_TRAILING_WHITESPACE]
                   [ENCODING <name>])
@@ -77,6 +78,10 @@ Options:
 ``OUTPUT_QUIET``, ``ERROR_QUIET``
  The standard output or standard error results will be quietly ignored.
 
+``COMMAND_ECHO <where>``
+ The command being run will be echo'ed to ``<where>`` with ``<where>``
+ being set to ``STDERR``|``STDOUT``|``NONE``.
+
 ``ENCODING <name>``
  On Windows, the encoding that is used to decode output from the process.
  Ignored on other platforms.

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -158,6 +158,7 @@ Variables that Change Behavior
    /variable/CMAKE_ECLIPSE_VERSION
    /variable/CMAKE_ERROR_DEPRECATED
    /variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
+   /variable/CMAKE_EXECUTE_PROCESS_COMMAND_ECHO
    /variable/CMAKE_EXPORT_COMPILE_COMMANDS
    /variable/CMAKE_EXPORT_PACKAGE_REGISTRY
    /variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY

+ 6 - 0
Help/release/dev/add-execute_process-command-echo.rst

@@ -0,0 +1,6 @@
+add-execute_process-command-echo
+--------------------------------
+
+* The :command:`execute_process` command gained a `COMMAND_ECHO` option
+  and supporting :variable:`CMAKE_EXECUTE_PROCESS_COMMAND_ECHO` variable
+  to enable echoing of the command-line string before execution.

+ 6 - 0
Help/variable/CMAKE_EXECUTE_PROCESS_COMMAND_ECHO.rst

@@ -0,0 +1,6 @@
+CMAKE_EXECUTE_PROCESS_COMMAND_ECHO
+----------------------------------
+
+If this variable is set to ``STDERR``|``STDOUT``|``NONE`` then commands in
+:command:`execute_process` calls will be printed to either stderr or stdout
+or not at all.

+ 49 - 1
Source/cmExecuteProcessCommand.cxx

@@ -6,11 +6,13 @@
 #include "cmsys/Process.h"
 #include <algorithm>
 #include <ctype.h> /* isspace */
+#include <iostream>
 #include <stdio.h>
 
 #include "cmAlgorithms.h"
 #include "cmArgumentParser.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmProcessOutput.h"
 #include "cmSystemTools.h"
 
@@ -47,6 +49,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
     std::string OutputFile;
     std::string ErrorFile;
     std::string Timeout;
+    std::string CommandEcho;
     bool OutputQuiet = false;
     bool ErrorQuiet = false;
     bool OutputStripTrailingWhitespace = false;
@@ -57,6 +60,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
   static auto const parser =
     cmArgumentParser<Arguments>{}
       .Bind("COMMAND"_s, &Arguments::Commands)
+      .Bind("COMMAND_ECHO"_s, &Arguments::CommandEcho)
       .Bind("OUTPUT_VARIABLE"_s, &Arguments::OutputVariable)
       .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable)
       .Bind("RESULT_VARIABLE"_s, &Arguments::ResultVariable)
@@ -117,7 +121,6 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
       return false;
     }
   }
-
   // Create a process instance.
   std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr(
     cmsysProcess_New(), cmsysProcess_Delete);
@@ -171,6 +174,51 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
     cmsysProcess_SetTimeout(cp, timeout);
   }
 
+  bool echo_stdout = false;
+  bool echo_stderr = false;
+  bool echo_output_from_variable = true;
+  std::string echo_output =
+    this->Makefile->GetSafeDefinition("CMAKE_EXECUTE_PROCESS_COMMAND_ECHO");
+  if (!arguments.CommandEcho.empty()) {
+    echo_output_from_variable = false;
+    echo_output = arguments.CommandEcho;
+  }
+
+  if (!echo_output.empty()) {
+    if (echo_output == "STDERR") {
+      echo_stderr = true;
+    } else if (echo_output == "STDOUT") {
+      echo_stdout = true;
+    } else if (echo_output != "NONE") {
+      std::string error;
+      if (echo_output_from_variable) {
+        error = "CMAKE_EXECUTE_PROCESS_COMMAND_ECHO set to '";
+      } else {
+        error = " called with '";
+      }
+      error += echo_output;
+      error += "' expected STDERR|STDOUT|NONE";
+      if (!echo_output_from_variable) {
+        error += " for COMMAND_ECHO.";
+      }
+      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, error);
+      return true;
+    }
+  }
+  if (echo_stdout || echo_stderr) {
+    std::string command;
+    for (auto& cmd : arguments.Commands) {
+      command += "'";
+      command += cmJoin(cmd, "' '");
+      command += "'";
+      command += "\n";
+    }
+    if (echo_stdout) {
+      std::cout << command;
+    } else if (echo_stderr) {
+      std::cerr << command;
+    }
+  }
   // Start the process.
   cmsysProcess_Execute(cp);
 

+ 1 - 0
Tests/RunCMake/execute_process/EchoCommand-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/execute_process/EchoCommand-stderr.txt

@@ -0,0 +1,5 @@
+.*cmake.*-E' 'echo' '--   2 COMMAND_ECHO STDERR'
+.*cmake.*-E' 'echo' '--   4 COMMAND_ECHO STDERR'
+.*cmake.*-E' 'echo' '--   8 COMMAND_ECHO STDOUT COMMAND_ECHO STDERR'
+CMake Error at .*EchoCommand.cmake:.* \(execute_process\):
+   CMAKE_EXECUTE_PROCESS_COMMAND_ECHO set to 'BAD' expected STDERR|STDOUT|NONE$

+ 12 - 0
Tests/RunCMake/execute_process/EchoCommand-stdout.txt

@@ -0,0 +1,12 @@
+.*cmake.*-E' 'echo' '--   1 COMMAND_ECHO STDOUT'
+--   1 COMMAND_ECHO STDOUT
+--   2 COMMAND_ECHO STDERR
+.*cmake.* '-E' 'echo' '--   3 COMMAND_ECHO STDOUT'
+--   3 COMMAND_ECHO STDOUT
+--   4 COMMAND_ECHO STDERR
+.*cmake.* '-E' 'echo' '--   5 COMMAND_ECHO STDOUT'
+--   5 COMMAND_ECHO STDOUT
+--   6 COMMAND_ECHO NONE
+.*cmake.* '-E' 'echo' '--   7 COMMAND_ECHO STDERR COMMAND_ECHO STDOUT'
+--   7 COMMAND_ECHO STDERR COMMAND_ECHO STDOUT
+--   8 COMMAND_ECHO STDOUT COMMAND_ECHO STDERR$

+ 41 - 0
Tests/RunCMake/execute_process/EchoCommand.cmake

@@ -0,0 +1,41 @@
+if(CHECK_ERROR_OUTPUT_LOCATION)
+  execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+    "--   1 COMMAND_ECHO " COMMAND_ECHO  )
+endif()
+# test COMMAND_ECHO STDOUT
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+  "--   1 COMMAND_ECHO STDOUT" COMMAND_ECHO STDOUT )
+# test COMMAND_ECHO STDERR
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+  "--   2 COMMAND_ECHO STDERR" COMMAND_ECHO STDERR )
+# test CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDOUT
+set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDOUT)
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+  "--   3 COMMAND_ECHO STDOUT" )
+# test CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDERR
+set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDERR)
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+  "--   4 COMMAND_ECHO STDERR" )
+# make sure local will override global settings
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+  "--   5 COMMAND_ECHO STDOUT" COMMAND_ECHO STDOUT )
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+  "--   6 COMMAND_ECHO NONE" COMMAND_ECHO NONE)
+# test both and make sure override works
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+  "--   7 COMMAND_ECHO STDERR COMMAND_ECHO STDOUT" COMMAND_ECHO STDERR
+  COMMAND_ECHO STDOUT)
+execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+  "--   8 COMMAND_ECHO STDOUT COMMAND_ECHO STDERR" COMMAND_ECHO STDOUT
+  COMMAND_ECHO STDERR)
+
+# check for bad arguments to global and local
+if(CHECK_GLOBAL)
+  # make sure a non STDERR or STDOUT value is an error
+  set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO BAD)
+  execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+    "--   9 - 1 CMAKE_EXECUTE_PROCESS_COMMAND_ECHO BAD" )
+else()
+  execute_process(COMMAND ${CMAKE_COMMAND} -E echo
+    "--   9 - 2 COMMAND_ECHO BAD" COMMAND_ECHO BAD)
+endif()

+ 1 - 0
Tests/RunCMake/execute_process/EchoCommand2-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/execute_process/EchoCommand2-stderr.txt

@@ -0,0 +1,5 @@
+.*cmake.*-E' 'echo' '--   2 COMMAND_ECHO STDERR'
+.*cmake.*-E' 'echo' '--   4 COMMAND_ECHO STDERR'
+.*cmake.*-E' 'echo' '--   8 COMMAND_ECHO STDOUT COMMAND_ECHO STDERR'
+CMake Error at .*EchoCommand.cmake:.* \(execute_process\):
+    called with 'BAD' expected STDERR|STDOUT|NONE for COMMAND_ECHO.$

+ 12 - 0
Tests/RunCMake/execute_process/EchoCommand2-stdout.txt

@@ -0,0 +1,12 @@
+.*cmake.*-E' 'echo' '--   1 COMMAND_ECHO STDOUT'
+--   1 COMMAND_ECHO STDOUT
+--   2 COMMAND_ECHO STDERR
+.*cmake.* '-E' 'echo' '--   3 COMMAND_ECHO STDOUT'
+--   3 COMMAND_ECHO STDOUT
+--   4 COMMAND_ECHO STDERR
+.*cmake.* '-E' 'echo' '--   5 COMMAND_ECHO STDOUT'
+--   5 COMMAND_ECHO STDOUT
+--   6 COMMAND_ECHO NONE
+.*cmake.* '-E' 'echo' '--   7 COMMAND_ECHO STDERR COMMAND_ECHO STDOUT'
+--   7 COMMAND_ECHO STDERR COMMAND_ECHO STDOUT
+--   8 COMMAND_ECHO STDOUT COMMAND_ECHO STDERR$

+ 1 - 0
Tests/RunCMake/execute_process/EchoCommand3-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/execute_process/EchoCommand3-stderr.txt

@@ -0,0 +1,2 @@
+CMake Error at .*EchoCommand.cmake:.*\(execute_process\):
+  execute_process called with no value for COMMAND_ECHO.

+ 8 - 0
Tests/RunCMake/execute_process/RunCMakeTest.cmake

@@ -16,3 +16,11 @@ endif()
 if(EXIT_CODE_EXE)
   run_cmake_command(ExitValues ${CMAKE_COMMAND} -DEXIT_CODE_EXE=${EXIT_CODE_EXE} -P ${RunCMake_SOURCE_DIR}/ExitValues.cmake)
 endif()
+
+run_cmake_command(EchoCommand ${CMAKE_COMMAND} -DCHECK_GLOBAL=TRUE
+  -P ${RunCMake_SOURCE_DIR}/EchoCommand.cmake)
+run_cmake_command(EchoCommand2 ${CMAKE_COMMAND} -P
+  ${RunCMake_SOURCE_DIR}/EchoCommand.cmake)
+run_cmake_command(EchoCommand3 ${CMAKE_COMMAND}
+  -DCHECK_ERROR_OUTPUT_LOCATION=TRUE -P
+  ${RunCMake_SOURCE_DIR}/EchoCommand.cmake)