From 2ed473b3b8e28485e6a14a49c7e0e4133eb02eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C4=81vis=20Mos=C4=81ns?= Date: Wed, 23 Nov 2016 14:11:40 +0200 Subject: execute_process: Add ENCODING option for Windows child process output Different applications can use different output encodings. --- Help/command/execute_process.rst | 11 ++++- Help/release/dev/execute_process-encoding.rst | 5 +++ Source/cmExecuteProcessCommand.cxx | 11 ++++- Source/cmProcessOutput.cxx | 15 +++++++ Source/cmProcessOutput.h | 7 ++++ Tests/CMakeLib/CMakeLists.txt | 3 ++ Tests/CMakeLib/testEncoding.cxx | 49 ++++++++++++++++++++++ Tests/RunCMake/CMakeLists.txt | 3 ++ Tests/RunCMake/RunCMake.cmake | 2 + Tests/RunCMake/execute_process/CMakeLists.txt | 3 ++ Tests/RunCMake/execute_process/Encoding.cmake | 6 +++ .../execute_process/EncodingMissing-result.txt | 1 + .../execute_process/EncodingMissing-stderr.txt | 4 ++ .../RunCMake/execute_process/EncodingMissing.cmake | 1 + .../execute_process/EncodingUTF8-stderr.txt | 1 + Tests/RunCMake/execute_process/RunCMakeTest.cmake | 5 +++ 16 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 Help/release/dev/execute_process-encoding.rst create mode 100644 Tests/CMakeLib/testEncoding.cxx create mode 100644 Tests/RunCMake/execute_process/CMakeLists.txt create mode 100644 Tests/RunCMake/execute_process/Encoding.cmake create mode 100644 Tests/RunCMake/execute_process/EncodingMissing-result.txt create mode 100644 Tests/RunCMake/execute_process/EncodingMissing-stderr.txt create mode 100644 Tests/RunCMake/execute_process/EncodingMissing.cmake create mode 100644 Tests/RunCMake/execute_process/EncodingUTF8-stderr.txt diff --git a/Help/command/execute_process.rst b/Help/command/execute_process.rst index e9a5eb0..71233d9 100644 --- a/Help/command/execute_process.rst +++ b/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 ]) 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 `` + 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 diff --git a/Help/release/dev/execute_process-encoding.rst b/Help/release/dev/execute_process-encoding.rst new file mode 100644 index 0000000..1c7cd8c --- /dev/null +++ b/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. diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 1562223..eb26a50 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -47,6 +47,7 @@ bool cmExecuteProcessCommand::InitialPass(std::vector 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 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 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. diff --git a/Source/cmProcessOutput.cxx b/Source/cmProcessOutput.cxx index 1440223..8b4d0e1 100644 --- a/Source/cmProcessOutput.cxx +++ b/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) diff --git a/Source/cmProcessOutput.h b/Source/cmProcessOutput.h index 6a4e7d5..6046f57 100644 --- a/Source/cmProcessOutput.h +++ b/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; diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 405917a..d680205 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/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 diff --git a/Tests/CMakeLib/testEncoding.cxx b/Tests/CMakeLib/testEncoding.cxx new file mode 100644 index 0000000..88743b0 --- /dev/null +++ b/Tests/CMakeLib/testEncoding.cxx @@ -0,0 +1,49 @@ +#include +#include +#include + +#include + +#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 " << 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(file)), + std::istreambuf_iterator()); + std::cout << text; + return 0; +} diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 9dc540f..c02b917 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/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=$) +endif() add_RunCMake_test(execute_process) add_RunCMake_test(export) add_RunCMake_test(cmake_minimum_required) diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index 247855a..6cc3054 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/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() diff --git a/Tests/RunCMake/execute_process/CMakeLists.txt b/Tests/RunCMake/execute_process/CMakeLists.txt new file mode 100644 index 0000000..a640c56 --- /dev/null +++ b/Tests/RunCMake/execute_process/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.7) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/execute_process/Encoding.cmake b/Tests/RunCMake/execute_process/Encoding.cmake new file mode 100644 index 0000000..3dc7c39 --- /dev/null +++ b/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}") diff --git a/Tests/RunCMake/execute_process/EncodingMissing-result.txt b/Tests/RunCMake/execute_process/EncodingMissing-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingMissing-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt b/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt new file mode 100644 index 0000000..1a69579 --- /dev/null +++ b/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\)$ diff --git a/Tests/RunCMake/execute_process/EncodingMissing.cmake b/Tests/RunCMake/execute_process/EncodingMissing.cmake new file mode 100644 index 0000000..ae97480 --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingMissing.cmake @@ -0,0 +1 @@ +execute_process(ENCODING) diff --git a/Tests/RunCMake/execute_process/EncodingUTF8-stderr.txt b/Tests/RunCMake/execute_process/EncodingUTF8-stderr.txt new file mode 100644 index 0000000..0ac68de --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingUTF8-stderr.txt @@ -0,0 +1 @@ +यूनिकोड είναι very здорово! diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake index 2080437..62e18c6 100644 --- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake +++ b/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() -- cgit v0.12