Browse Source

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 years ago
parent
commit
044dcf9f8d

+ 5 - 0
Help/command/execute_process.rst

@@ -18,6 +18,7 @@ Execute one or more child processes.
                   [ERROR_FILE <file>]
                   [ERROR_FILE <file>]
                   [OUTPUT_QUIET]
                   [OUTPUT_QUIET]
                   [ERROR_QUIET]
                   [ERROR_QUIET]
+                  [COMMAND_ECHO <where>]
                   [OUTPUT_STRIP_TRAILING_WHITESPACE]
                   [OUTPUT_STRIP_TRAILING_WHITESPACE]
                   [ERROR_STRIP_TRAILING_WHITESPACE]
                   [ERROR_STRIP_TRAILING_WHITESPACE]
                   [ENCODING <name>])
                   [ENCODING <name>])
@@ -77,6 +78,10 @@ Options:
 ``OUTPUT_QUIET``, ``ERROR_QUIET``
 ``OUTPUT_QUIET``, ``ERROR_QUIET``
  The standard output or standard error results will be quietly ignored.
  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>``
 ``ENCODING <name>``
  On Windows, the encoding that is used to decode output from the process.
  On Windows, the encoding that is used to decode output from the process.
  Ignored on other platforms.
  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_ECLIPSE_VERSION
    /variable/CMAKE_ERROR_DEPRECATED
    /variable/CMAKE_ERROR_DEPRECATED
    /variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
    /variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
+   /variable/CMAKE_EXECUTE_PROCESS_COMMAND_ECHO
    /variable/CMAKE_EXPORT_COMPILE_COMMANDS
    /variable/CMAKE_EXPORT_COMPILE_COMMANDS
    /variable/CMAKE_EXPORT_PACKAGE_REGISTRY
    /variable/CMAKE_EXPORT_PACKAGE_REGISTRY
    /variable/CMAKE_EXPORT_NO_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 "cmsys/Process.h"
 #include <algorithm>
 #include <algorithm>
 #include <ctype.h> /* isspace */
 #include <ctype.h> /* isspace */
+#include <iostream>
 #include <stdio.h>
 #include <stdio.h>
 
 
 #include "cmAlgorithms.h"
 #include "cmAlgorithms.h"
 #include "cmArgumentParser.h"
 #include "cmArgumentParser.h"
 #include "cmMakefile.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmProcessOutput.h"
 #include "cmProcessOutput.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
 
 
@@ -47,6 +49,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
     std::string OutputFile;
     std::string OutputFile;
     std::string ErrorFile;
     std::string ErrorFile;
     std::string Timeout;
     std::string Timeout;
+    std::string CommandEcho;
     bool OutputQuiet = false;
     bool OutputQuiet = false;
     bool ErrorQuiet = false;
     bool ErrorQuiet = false;
     bool OutputStripTrailingWhitespace = false;
     bool OutputStripTrailingWhitespace = false;
@@ -57,6 +60,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
   static auto const parser =
   static auto const parser =
     cmArgumentParser<Arguments>{}
     cmArgumentParser<Arguments>{}
       .Bind("COMMAND"_s, &Arguments::Commands)
       .Bind("COMMAND"_s, &Arguments::Commands)
+      .Bind("COMMAND_ECHO"_s, &Arguments::CommandEcho)
       .Bind("OUTPUT_VARIABLE"_s, &Arguments::OutputVariable)
       .Bind("OUTPUT_VARIABLE"_s, &Arguments::OutputVariable)
       .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable)
       .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable)
       .Bind("RESULT_VARIABLE"_s, &Arguments::ResultVariable)
       .Bind("RESULT_VARIABLE"_s, &Arguments::ResultVariable)
@@ -117,7 +121,6 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
       return false;
       return false;
     }
     }
   }
   }
-
   // Create a process instance.
   // Create a process instance.
   std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr(
   std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr(
     cmsysProcess_New(), cmsysProcess_Delete);
     cmsysProcess_New(), cmsysProcess_Delete);
@@ -171,6 +174,51 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
     cmsysProcess_SetTimeout(cp, timeout);
     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.
   // Start the process.
   cmsysProcess_Execute(cp);
   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)
 if(EXIT_CODE_EXE)
   run_cmake_command(ExitValues ${CMAKE_COMMAND} -DEXIT_CODE_EXE=${EXIT_CODE_EXE} -P ${RunCMake_SOURCE_DIR}/ExitValues.cmake)
   run_cmake_command(ExitValues ${CMAKE_COMMAND} -DEXIT_CODE_EXE=${EXIT_CODE_EXE} -P ${RunCMake_SOURCE_DIR}/ExitValues.cmake)
 endif()
 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)