Преглед на файлове

execute_process: Add ENCODING option for Windows child process output

Different applications can use different output encodings.
Dāvis Mosāns преди 9 години
родител
ревизия
2ed473b3b8

+ 10 - 1
Help/command/execute_process.rst

@@ -18,7 +18,8 @@ Execute one or more child processes.
                   [OUTPUT_QUIET]
                   [ERROR_QUIET]
                   [OUTPUT_STRIP_TRAILING_WHITESPACE]
-                  [ERROR_STRIP_TRAILING_WHITESPACE])
+                  [ERROR_STRIP_TRAILING_WHITESPACE]
+                  [ENCODING <name>])
 
 Runs the given sequence of one or more commands in parallel with the standard
 output of each process piped to the standard input of the next.
@@ -66,6 +67,14 @@ Options:
 ``OUTPUT_QUIET``, ``ERROR_QUIET``
  The standard output or standard error results will be quietly ignored.
 
+``ENCODING <name>``
+ On Windows, the encoding that is used to decode output from the process.
+ Ignored on other platforms.
+ Valid encoding names are: ``AUTO`` (the default), ``NONE``, ``UTF8``,
+ ``ANSI`` and ``OEM``.
+ ``AUTO`` encoding means current active console's codepage will be used
+ or if that isn't available then ``ANSI`` codepage will be used.
+
 If more than one ``OUTPUT_*`` or ``ERROR_*`` option is given for the
 same pipe the precedence is not specified.
 If no ``OUTPUT_*`` or ``ERROR_*`` options are given the output will

+ 5 - 0
Help/release/dev/execute_process-encoding.rst

@@ -0,0 +1,5 @@
+execute_process-encoding
+------------------------
+
+* The :command:`execute_process` command gained an ``ENCODING`` option to
+  specify on Windows which encoding is used for output from child process.

+ 10 - 1
Source/cmExecuteProcessCommand.cxx

@@ -47,6 +47,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
   std::string error_variable;
   std::string result_variable;
   std::string working_directory;
+  cmProcessOutput::Encoding encoding = cmProcessOutput::Auto;
   for (size_t i = 0; i < args.size(); ++i) {
     if (args[i] == "COMMAND") {
       doing_command = true;
@@ -128,6 +129,14 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
     } else if (args[i] == "ERROR_STRIP_TRAILING_WHITESPACE") {
       doing_command = false;
       error_strip_trailing_whitespace = true;
+    } else if (args[i] == "ENCODING") {
+      doing_command = false;
+      if (++i < args.size()) {
+        encoding = cmProcessOutput::FindEncoding(args[i]);
+      } else {
+        this->SetError(" called with no value for ENCODING.");
+        return false;
+      }
     } else if (doing_command) {
       cmds[command_index].push_back(args[i].c_str());
     } else {
@@ -223,7 +232,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
   int length;
   char* data;
   int p;
-  cmProcessOutput processOutput;
+  cmProcessOutput processOutput(encoding);
   std::string strdata;
   while ((p = cmsysProcess_WaitForData(cp, &data, &length, CM_NULLPTR), p)) {
     // Put the output in the right place.

+ 15 - 0
Source/cmProcessOutput.cxx

@@ -9,6 +9,21 @@ unsigned int cmProcessOutput::defaultCodepage =
   KWSYS_ENCODING_DEFAULT_CODEPAGE;
 #endif
 
+cmProcessOutput::Encoding cmProcessOutput::FindEncoding(std::string name)
+{
+  Encoding encoding = Auto;
+  if (name == "UTF8") {
+    encoding = UTF8;
+  } else if (name == "NONE") {
+    encoding = None;
+  } else if (name == "ANSI") {
+    encoding = ANSI;
+  } else if (name == "OEM") {
+    encoding = OEM;
+  }
+  return encoding;
+}
+
 cmProcessOutput::cmProcessOutput(Encoding encoding, unsigned int maxSize)
 {
 #if defined(_WIN32)

+ 7 - 0
Source/cmProcessOutput.h

@@ -27,6 +27,13 @@ public:
     OEM
   };
 
+  /**
+  * Find encoding enum value for given encoding \a name.
+  * \param name a encoding name.
+  * \return encoding enum value or Auto if \a name was not found.
+  */
+  static Encoding FindEncoding(std::string name);
+
   /// The code page that is used as internal encoding to which we will encode.
   static unsigned int defaultCodepage;
 

+ 3 - 0
Tests/CMakeLib/CMakeLists.txt

@@ -31,6 +31,9 @@ create_test_sourcelist(CMakeLib_TEST_SRCS CMakeLibTests.cxx ${CMakeLib_TESTS})
 add_executable(CMakeLibTests ${CMakeLib_TEST_SRCS})
 target_link_libraries(CMakeLibTests CMakeLib)
 
+add_executable(testEncoding testEncoding.cxx)
+target_link_libraries(testEncoding cmsys)
+
 # Xcode 2.x forgets to create the output directory before linking
 # the individual architectures.
 if(CMAKE_OSX_ARCHITECTURES AND XCODE

+ 49 - 0
Tests/CMakeLib/testEncoding.cxx

@@ -0,0 +1,49 @@
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <cmsys/ConsoleBuf.hxx>
+
+#ifdef _WIN32
+void setEncoding(cmsys::ConsoleBuf::Manager& buf, UINT codepage)
+{
+  cmsys::ConsoleBuf* cb = buf.GetConsoleBuf();
+  if (cb) {
+    cb->input_pipe_codepage = codepage;
+    cb->output_pipe_codepage = codepage;
+    cb->input_file_codepage = codepage;
+    cb->output_file_codepage = codepage;
+    cb->activateCodepageChange();
+  }
+}
+#endif
+
+int main(int argc, char* argv[])
+{
+#ifdef _WIN32
+  cmsys::ConsoleBuf::Manager consoleOut(std::cout);
+#endif
+  if (argc <= 2) {
+    std::cout << "Usage: testEncoding <encoding> <file>" << std::endl;
+    return 1;
+  }
+  const std::string encoding(argv[1]);
+#ifdef _WIN32
+  if (encoding == "UTF8") {
+    setEncoding(consoleOut, CP_UTF8);
+  } else if (encoding == "ANSI") {
+    setEncoding(consoleOut, CP_ACP);
+  } else if (encoding == "OEM") {
+    setEncoding(consoleOut, CP_OEMCP);
+  } // else AUTO
+#endif
+  std::ifstream file(argv[2]);
+  if (!file.is_open()) {
+    std::cout << "Failed to open file: " << argv[2] << std::endl;
+    return 2;
+  }
+  std::string text((std::istreambuf_iterator<char>(file)),
+                   std::istreambuf_iterator<char>());
+  std::cout << text;
+  return 0;
+}

+ 3 - 0
Tests/RunCMake/CMakeLists.txt

@@ -179,6 +179,9 @@ add_RunCMake_test(add_custom_target)
 add_RunCMake_test(add_dependencies)
 add_RunCMake_test(add_subdirectory)
 add_RunCMake_test(build_command)
+if(NOT CMake_TEST_EXTERNAL_CMAKE)
+  set(execute_process_ARGS -DTEST_ENCODING_EXE=$<TARGET_FILE:testEncoding>)
+endif()
 add_RunCMake_test(execute_process)
 add_RunCMake_test(export)
 add_RunCMake_test(cmake_minimum_required)

+ 2 - 0
Tests/RunCMake/RunCMake.cmake

@@ -75,6 +75,7 @@ function(run_cmake test)
       OUTPUT_VARIABLE actual_stdout
       ERROR_VARIABLE ${actual_stderr_var}
       RESULT_VARIABLE actual_result
+      ENCODING UTF8
       ${maybe_timeout}
       )
   else()
@@ -90,6 +91,7 @@ function(run_cmake test)
       OUTPUT_VARIABLE actual_stdout
       ERROR_VARIABLE ${actual_stderr_var}
       RESULT_VARIABLE actual_result
+      ENCODING UTF8
       ${maybe_timeout}
       )
   endif()

+ 3 - 0
Tests/RunCMake/execute_process/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.7)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 6 - 0
Tests/RunCMake/execute_process/Encoding.cmake

@@ -0,0 +1,6 @@
+execute_process(
+  COMMAND ${TEST_ENCODING_EXE} ${TEST_ENCODING} ${CMAKE_CURRENT_LIST_DIR}/EncodingUTF8-stderr.txt
+  OUTPUT_VARIABLE out
+  ENCODING ${TEST_ENCODING}
+  )
+message("${out}")

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

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

+ 4 - 0
Tests/RunCMake/execute_process/EncodingMissing-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at EncodingMissing.cmake:[0-9]+ \(execute_process\):
+  execute_process called with no value for ENCODING.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$

+ 1 - 0
Tests/RunCMake/execute_process/EncodingMissing.cmake

@@ -0,0 +1 @@
+execute_process(ENCODING)

+ 1 - 0
Tests/RunCMake/execute_process/EncodingUTF8-stderr.txt

@@ -0,0 +1 @@
+यूनिकोड είναι very здорово!

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

@@ -6,3 +6,8 @@ unset(RunCMake_TEST_OUTPUT_MERGE)
 
 run_cmake_command(MergeOutputFile ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/MergeOutputFile.cmake)
 run_cmake_command(MergeOutputVars ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/MergeOutputVars.cmake)
+
+run_cmake(EncodingMissing)
+if(TEST_ENCODING_EXE)
+  run_cmake_command(EncodingUTF8 ${CMAKE_COMMAND} -DTEST_ENCODING=UTF8 -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE} -P ${RunCMake_SOURCE_DIR}/Encoding.cmake)
+endif()