diff options
52 files changed, 1261 insertions, 95 deletions
diff --git a/Help/command/execute_process.rst b/Help/command/execute_process.rst index 2d71352..e6ad037 100644 --- a/Help/command/execute_process.rst +++ b/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. diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index 7f4761f..3dc3221 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -530,8 +530,7 @@ Output-Related Expressions Content of ``...`` converted to a C identifier. The conversion follows the same behavior as :command:`string(MAKE_C_IDENTIFIER)`. ``$<TARGET_OBJECTS:objLib>`` - List of objects resulting from build of ``objLib``. ``objLib`` must be an - object of type ``OBJECT_LIBRARY``. + List of objects resulting from build of ``objLib``. ``$<SHELL_PATH:...>`` Content of ``...`` converted to shell path style. For example, slashes are converted to backslashes in Windows shells and drive letters are converted diff --git a/Help/manual/cmake-server.7.rst b/Help/manual/cmake-server.7.rst index 25d364c..8f10b9f 100644 --- a/Help/manual/cmake-server.7.rst +++ b/Help/manual/cmake-server.7.rst @@ -7,6 +7,11 @@ cmake-server(7) .. contents:: +.. deprecated:: 3.15 + + This will be removed from a future version of CMake. + Clients should use the :manual:`cmake-file-api(7)` instead. + Introduction ============ diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 3e8c884..6d93ae9 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -159,6 +159,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 diff --git a/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt index 2bf71a9..6c61341 100644 --- a/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt +++ b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt @@ -13,3 +13,8 @@ The value is ignored on non-MSVC compilers but an unsupported value will be rejected as an error when using a compiler targeting the MSVC ABI. + +The value may also be the empty string (``""``) in which case no runtime +library selection flag will be added explicitly by CMake. Note that with +:ref:`Visual Studio Generators` the native build system may choose to +add its own default runtime library selection flag. diff --git a/Help/release/dev/add-execute_process-command-echo.rst b/Help/release/dev/add-execute_process-command-echo.rst new file mode 100644 index 0000000..a44e40e --- /dev/null +++ b/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. diff --git a/Help/release/dev/relax-TARGET_OBJECTS-generator-expression.rst b/Help/release/dev/relax-TARGET_OBJECTS-generator-expression.rst new file mode 100644 index 0000000..25ca0c9 --- /dev/null +++ b/Help/release/dev/relax-TARGET_OBJECTS-generator-expression.rst @@ -0,0 +1,5 @@ +relax-TARGET_OBJECTS-generator-expression +----------------------------------------- + +* The ``TARGET_OBJECTS`` :manual:`generator expression <cmake-generator-expressions(7)>` + is now supported on ``SHARED``, ``STATIC``, ``MODULE`` libraries and executables. diff --git a/Help/release/dev/server-deprecate.rst b/Help/release/dev/server-deprecate.rst new file mode 100644 index 0000000..65c86e1 --- /dev/null +++ b/Help/release/dev/server-deprecate.rst @@ -0,0 +1,6 @@ +server-deprecate +---------------- + +* The :manual:`cmake-server(7)` mode has been deprecated and will be + removed from a future version of CMake. Please port clients to use + the :manual:`cmake-file-api(7)` instead. diff --git a/Help/variable/CMAKE_EXECUTE_PROCESS_COMMAND_ECHO.rst b/Help/variable/CMAKE_EXECUTE_PROCESS_COMMAND_ECHO.rst new file mode 100644 index 0000000..4a3121c --- /dev/null +++ b/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. diff --git a/Modules/Compiler/Intel-DetermineCompiler.cmake b/Modules/Compiler/Intel-DetermineCompiler.cmake index d7e4532..c31aa77 100644 --- a/Modules/Compiler/Intel-DetermineCompiler.cmake +++ b/Modules/Compiler/Intel-DetermineCompiler.cmake @@ -18,9 +18,23 @@ set(_compiler_id_version_compute " /* _MSC_VER = VVRR */ # define @PREFIX@SIMULATE_VERSION_MAJOR @MACRO_DEC@(_MSC_VER / 100) # define @PREFIX@SIMULATE_VERSION_MINOR @MACRO_DEC@(_MSC_VER % 100) +# endif +# if defined(__GNUC__) +# define @PREFIX@SIMULATE_VERSION_MAJOR @MACRO_DEC@(__GNUC__) +# elif defined(__GNUG__) +# define @PREFIX@SIMULATE_VERSION_MAJOR @MACRO_DEC@(__GNUG__) +# endif +# if defined(__GNUC_MINOR__) +# define @PREFIX@SIMULATE_VERSION_MINOR @MACRO_DEC@(__GNUC_MINOR__) +# endif +# if defined(__GNUC_PATCHLEVEL__) +# define @PREFIX@SIMULATE_VERSION_PATCH @MACRO_DEC@(__GNUC_PATCHLEVEL__) # endif") set(_compiler_id_simulate " # if defined(_MSC_VER) # define @PREFIX@SIMULATE_ID \"MSVC\" +# endif +# if defined(__GNUC__) +# define @PREFIX@SIMULATE_ID \"GNU\" # endif") diff --git a/Modules/Compiler/MSVC-C.cmake b/Modules/Compiler/MSVC-C.cmake index a722130..f56227b 100644 --- a/Modules/Compiler/MSVC-C.cmake +++ b/Modules/Compiler/MSVC-C.cmake @@ -21,9 +21,13 @@ macro(cmake_record_c_compile_features) c_std_99 c_std_11 c_function_prototypes - c_variadic_macros ) list(APPEND CMAKE_C90_COMPILE_FEATURES c_std_90 c_function_prototypes) - list(APPEND CMAKE_C99_COMPILE_FEATURES c_std_99 c_variadic_macros) + list(APPEND CMAKE_C99_COMPILE_FEATURES c_std_99) list(APPEND CMAKE_C11_COMPILE_FEATURES c_std_11) + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0) + list(APPEND CMAKE_C_COMPILE_FEATURES c_variadic_macros) + list(APPEND CMAKE_C99_COMPILE_FEATURES c_variadic_macros) + endif() + set(_result 0) # expected by cmake_determine_compile_features endmacro() diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake index 8c45a8c..5288640 100644 --- a/Modules/FindMPI.cmake +++ b/Modules/FindMPI.cmake @@ -770,18 +770,20 @@ function (_MPI_interrogate_compiler LANG) endforeach() # Add the link directories given explicitly that we haven't used back as linker directories. - foreach(_MPI_LINK_DIRECTORY IN LISTS MPI_LINK_DIRECTORIES_LEFTOVER) - file(TO_NATIVE_PATH "${_MPI_LINK_DIRECTORY}" _MPI_LINK_DIRECTORY_ACTUAL) - string(FIND "${_MPI_LINK_DIRECTORY_ACTUAL}" " " _MPI_LINK_DIRECTORY_CONTAINS_SPACE) - if(NOT _MPI_LINK_DIRECTORY_CONTAINS_SPACE EQUAL -1) - set(_MPI_LINK_DIRECTORY_ACTUAL "\"${_MPI_LINK_DIRECTORY_ACTUAL}\"") - endif() - if(MPI_LINK_FLAGS_WORK) - string(APPEND MPI_LINK_FLAGS_WORK " ${CMAKE_LIBRARY_PATH_FLAG}${_MPI_LINK_DIRECTORY_ACTUAL}") - else() - set(MPI_LINK_FLAGS_WORK "${CMAKE_LIBRARY_PATH_FLAG}${_MPI_LINK_DIRECTORY_ACTUAL}") - endif() - endforeach() + if(NOT WIN32) + foreach(_MPI_LINK_DIRECTORY IN LISTS MPI_LINK_DIRECTORIES_LEFTOVER) + file(TO_NATIVE_PATH "${_MPI_LINK_DIRECTORY}" _MPI_LINK_DIRECTORY_ACTUAL) + string(FIND "${_MPI_LINK_DIRECTORY_ACTUAL}" " " _MPI_LINK_DIRECTORY_CONTAINS_SPACE) + if(NOT _MPI_LINK_DIRECTORY_CONTAINS_SPACE EQUAL -1) + set(_MPI_LINK_DIRECTORY_ACTUAL "\"${_MPI_LINK_DIRECTORY_ACTUAL}\"") + endif() + if(MPI_LINK_FLAGS_WORK) + string(APPEND MPI_LINK_FLAGS_WORK " ${CMAKE_LIBRARY_PATH_FLAG}${_MPI_LINK_DIRECTORY_ACTUAL}") + else() + set(MPI_LINK_FLAGS_WORK "${CMAKE_LIBRARY_PATH_FLAG}${_MPI_LINK_DIRECTORY_ACTUAL}") + endif() + endforeach() + endif() # Deal with the libraries given with full path next unset(MPI_DIRECT_LIB_NAMES_WORK) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 49f237f..01c6cd7 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -264,6 +264,8 @@ set(SRCS cmGeneratorExpression.h cmGeneratorTarget.cxx cmGeneratorTarget.h + cmGetPipes.cxx + cmGetPipes.h cmGlobalCommonGenerator.cxx cmGlobalCommonGenerator.h cmGlobalGenerator.cxx @@ -386,6 +388,7 @@ set(SRCS cmUuid.cxx cmUVHandlePtr.cxx cmUVHandlePtr.h + cmUVStreambuf.h cmUVSignalHackRAII.h cmVariableWatch.cxx cmVariableWatch.h diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 63ca76a..a7e8ee1 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 14) -set(CMake_VERSION_PATCH 20190427) +set(CMake_VERSION_PATCH 20190430) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx index bfe8d70..a2c30bb 100644 --- a/Source/CTest/cmProcess.cxx +++ b/Source/CTest/cmProcess.cxx @@ -5,61 +5,19 @@ #include "cmCTest.h" #include "cmCTestRunTest.h" #include "cmCTestTestHandler.h" +#include "cmGetPipes.h" #include "cmsys/Process.h" -#include <fcntl.h> #include <iostream> #include <signal.h> #include <string> #if defined(_WIN32) # include "cm_kwiml.h" -#else -# include <unistd.h> #endif #include <utility> #define CM_PROCESS_BUF_SIZE 65536 -#if defined(_WIN32) && !defined(__CYGWIN__) -# include <io.h> - -static int cmProcessGetPipes(int* fds) -{ - SECURITY_ATTRIBUTES attr; - HANDLE readh, writeh; - attr.nLength = sizeof(attr); - attr.lpSecurityDescriptor = nullptr; - attr.bInheritHandle = FALSE; - if (!CreatePipe(&readh, &writeh, &attr, 0)) - return uv_translate_sys_error(GetLastError()); - fds[0] = _open_osfhandle((intptr_t)readh, 0); - fds[1] = _open_osfhandle((intptr_t)writeh, 0); - if (fds[0] == -1 || fds[1] == -1) { - CloseHandle(readh); - CloseHandle(writeh); - return uv_translate_sys_error(GetLastError()); - } - return 0; -} -#else -# include <errno.h> - -static int cmProcessGetPipes(int* fds) -{ - if (pipe(fds) == -1) { - return uv_translate_sys_error(errno); - } - - if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || - fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { - close(fds[0]); - close(fds[1]); - return uv_translate_sys_error(errno); - } - return 0; -} -#endif - cmProcess::cmProcess(cmCTestRunTest& runner) : Runner(runner) , Conv(cmProcessOutput::UTF8, CM_PROCESS_BUF_SIZE) @@ -120,7 +78,7 @@ bool cmProcess::StartProcess(uv_loop_t& loop, std::vector<size_t>* affinity) pipe_reader.init(loop, 0, this); int fds[2] = { -1, -1 }; - status = cmProcessGetPipes(fds); + status = cmGetPipes(fds); if (status != 0) { cmCTestLog(this->Runner.GetCTest(), ERROR_MESSAGE, "Error initializing pipe: " << uv_strerror(status) diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index ff6340f..0d9859e 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/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,9 +121,10 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, return false; } } - // Create a process instance. - cmsysProcess* cp = cmsysProcess_New(); + std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr( + cmsysProcess_New(), cmsysProcess_Delete); + cmsysProcess* cp = cp_ptr.get(); // Set the command sequence. for (std::vector<std::string> const& cmd : arguments.Commands) { @@ -169,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); @@ -297,9 +347,6 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, } } - // Delete the process instance. - cmsysProcess_Delete(cp); - return true; } diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 8b3d9d6..8803830 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -1600,10 +1600,16 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode reportError(context, content->GetOriginalExpression(), e.str()); return std::string(); } - if (gt->GetType() != cmStateEnums::OBJECT_LIBRARY) { + cmStateEnums::TargetType type = gt->GetType(); + if (type != cmStateEnums::EXECUTABLE && + type != cmStateEnums::STATIC_LIBRARY && + type != cmStateEnums::SHARED_LIBRARY && + type != cmStateEnums::MODULE_LIBRARY && + type != cmStateEnums::OBJECT_LIBRARY) { std::ostringstream e; e << "Objects of target \"" << tgtName - << "\" referenced but is not an OBJECT library."; + << "\" referenced but is not an allowed library types (EXECUTABLE, " + << "STATIC, SHARED, MODULE, OBJECT)."; reportError(context, content->GetOriginalExpression(), e.str()); return std::string(); } diff --git a/Source/cmGetPipes.cxx b/Source/cmGetPipes.cxx new file mode 100644 index 0000000..ad323f7 --- /dev/null +++ b/Source/cmGetPipes.cxx @@ -0,0 +1,48 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmGetPipes.h" + +#include "cm_uv.h" + +#include <fcntl.h> + +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <io.h> + +int cmGetPipes(int* fds) +{ + SECURITY_ATTRIBUTES attr; + HANDLE readh, writeh; + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = nullptr; + attr.bInheritHandle = FALSE; + if (!CreatePipe(&readh, &writeh, &attr, 0)) + return uv_translate_sys_error(GetLastError()); + fds[0] = _open_osfhandle((intptr_t)readh, 0); + fds[1] = _open_osfhandle((intptr_t)writeh, 0); + if (fds[0] == -1 || fds[1] == -1) { + CloseHandle(readh); + CloseHandle(writeh); + return uv_translate_sys_error(GetLastError()); + } + return 0; +} +#else +# include <errno.h> +# include <unistd.h> + +int cmGetPipes(int* fds) +{ + if (pipe(fds) == -1) { + return uv_translate_sys_error(errno); + } + + if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || + fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { + close(fds[0]); + close(fds[1]); + return uv_translate_sys_error(errno); + } + return 0; +} +#endif diff --git a/Source/cmGetPipes.h b/Source/cmGetPipes.h new file mode 100644 index 0000000..2a46b51 --- /dev/null +++ b/Source/cmGetPipes.h @@ -0,0 +1,8 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmGetPipes_h +#define cmGetPipes_h + +int cmGetPipes(int* fds); + +#endif diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 203ee93..dad8821 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -8,6 +8,7 @@ #include "cmGlobalGenerator.h" #include "cmJsonObjectDictionary.h" #include "cmJsonObjects.h" +#include "cmMessageType.h" #include "cmServer.h" #include "cmServerDictionary.h" #include "cmState.h" @@ -600,6 +601,10 @@ cmServerResponse cmServerProtocol1::ProcessConfigure( } int ret = cm->Configure(); + cm->IssueMessage( + MessageType::DEPRECATION_WARNING, + "The 'cmake-server(7)' is deprecated. " + "Please port clients to use the 'cmake-file-api(7)' instead."); if (ret < 0) { return request.ReportError("Configuration failed."); } diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx index 2bc4c39..5cdacaa 100644 --- a/Source/cmSourceGroupCommand.cxx +++ b/Source/cmSourceGroupCommand.cxx @@ -80,7 +80,10 @@ std::vector<std::string> prepareFilesPathsForTree( prepared.reserve(filesPaths.size()); for (auto const& filePath : filesPaths) { - prepared.push_back(prepareFilePathForTree(filePath, currentSourceDir)); + // If provided file path is actually not a file, silently ignore it. + if (cmSystemTools::FileExists(filePath, /*isFile=*/true)) { + prepared.push_back(prepareFilePathForTree(filePath, currentSourceDir)); + } } return prepared; diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx index fd07d2d..27069ee 100644 --- a/Source/cmUVHandlePtr.cxx +++ b/Source/cmUVHandlePtr.cxx @@ -11,19 +11,59 @@ namespace cm { -static void close_delete(uv_handle_t* h) +struct uv_loop_deleter { - free(h); + void operator()(uv_loop_t* loop) const; +}; + +void uv_loop_deleter::operator()(uv_loop_t* loop) const +{ + uv_run(loop, UV_RUN_DEFAULT); + int result = uv_loop_close(loop); + (void)result; + assert(result >= 0); + free(loop); +} + +int uv_loop_ptr::init(void* data) +{ + this->reset(); + + this->loop.reset(static_cast<uv_loop_t*>(calloc(1, sizeof(uv_loop_t))), + uv_loop_deleter()); + this->loop->data = data; + + return uv_loop_init(this->loop.get()); +} + +void uv_loop_ptr::reset() +{ + this->loop.reset(); +} + +uv_loop_ptr::operator uv_loop_t*() +{ + return this->loop.get(); +} + +uv_loop_t* uv_loop_ptr::operator->() const noexcept +{ + return this->loop.get(); +} + +uv_loop_t* uv_loop_ptr::get() const +{ + return this->loop.get(); } template <typename T> -static void default_delete(T* type_handle) +static void handle_default_delete(T* type_handle) { auto handle = reinterpret_cast<uv_handle_t*>(type_handle); if (handle) { assert(!uv_is_closing(handle)); if (!uv_is_closing(handle)) { - uv_close(handle, &close_delete); + uv_close(handle, [](uv_handle_t* h) { free(h); }); } } } @@ -34,7 +74,7 @@ static void default_delete(T* type_handle) template <typename T> struct uv_handle_deleter { - void operator()(T* type_handle) const { default_delete(type_handle); } + void operator()(T* type_handle) const { handle_default_delete(type_handle); } }; template <typename T> @@ -107,7 +147,7 @@ struct uv_handle_deleter<uv_async_t> void operator()(uv_async_t* handle) { std::lock_guard<std::mutex> lock(*handleMutex); - default_delete(handle); + handle_default_delete(handle); } }; @@ -136,7 +176,7 @@ struct uv_handle_deleter<uv_signal_t> { if (handle) { uv_signal_stop(handle); - default_delete(handle); + handle_default_delete(handle); } } }; diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h index 992c429..c09e4bf 100644 --- a/Source/cmUVHandlePtr.h +++ b/Source/cmUVHandlePtr.h @@ -30,7 +30,45 @@ namespace cm { /*** - * RAII class to simplify and insure the safe usage of uv_*_t types. This + * RAII class to simplify and ensure the safe usage of uv_loop_t. This includes + * making sure resources are properly freed. + */ +class uv_loop_ptr +{ +protected: + std::shared_ptr<uv_loop_t> loop; + +public: + uv_loop_ptr(uv_loop_ptr const&) = delete; + uv_loop_ptr& operator=(uv_loop_ptr const&) = delete; + uv_loop_ptr(uv_loop_ptr&&) noexcept; + uv_loop_ptr& operator=(uv_loop_ptr&&) noexcept; + + // Dtor and ctor need to be inline defined like this for default ctors and + // dtors to work. Some compilers do not like '= default' here. + uv_loop_ptr() {} // NOLINT(modernize-use-equals-default) + uv_loop_ptr(std::nullptr_t) {} + ~uv_loop_ptr() { this->reset(); } + + int init(void* data = nullptr); + + /** + * Properly close the handle if needed and sets the inner handle to nullptr + */ + void reset(); + + /** + * Allow less verbose calling of uv_loop_* functions + * @return reinterpreted handle + */ + operator uv_loop_t*(); + + uv_loop_t* get() const; + uv_loop_t* operator->() const noexcept; +}; + +/*** + * RAII class to simplify and ensure the safe usage of uv_*_t types. This * includes making sure resources are properly freed and contains casting * operators which allow for passing into relevant uv_* functions. * diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h new file mode 100644 index 0000000..29e4fde --- /dev/null +++ b/Source/cmUVStreambuf.h @@ -0,0 +1,219 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmUVStreambuf_h +#define cmUVStreambuf_h + +#include "cmUVHandlePtr.h" + +#include "cm_uv.h" + +#include <algorithm> +#include <cstring> +#include <streambuf> +#include <vector> + +/* + * This file is based on example code from: + * + * http://www.voidcn.com/article/p-vjnlygmc-gy.html + * + * The example code was distributed under the following license: + * + * Copyright 2007 Edd Dawson. + * Distributed under the Boost Software License, Version 1.0. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +template <typename CharT, typename Traits = std::char_traits<CharT>> +class cmBasicUVStreambuf : public std::basic_streambuf<CharT, Traits> +{ +public: + cmBasicUVStreambuf(std::size_t bufSize = 256, std::size_t putBack = 8); + ~cmBasicUVStreambuf() override; + + bool is_open() const; + + cmBasicUVStreambuf* open(uv_stream_t* stream); + + cmBasicUVStreambuf* close(); + +protected: + typename cmBasicUVStreambuf::int_type underflow() override; + std::streamsize showmanyc() override; + + // FIXME: Add write support + +private: + uv_stream_t* Stream = nullptr; + void* OldStreamData; + const std::size_t PutBack; + std::vector<CharT> InputBuffer; + bool EndOfFile; + + void StreamReadStartStop(); + + void StreamRead(ssize_t nread); + void HandleAlloc(uv_buf_t* buf); +}; + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>::cmBasicUVStreambuf(std::size_t bufSize, + std::size_t putBack) + : PutBack(std::max<std::size_t>(putBack, 1)) + , InputBuffer(std::max<std::size_t>(this->PutBack, bufSize) + this->PutBack) +{ + this->close(); +} + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>::~cmBasicUVStreambuf() +{ + this->close(); +} + +template <typename CharT, typename Traits> +bool cmBasicUVStreambuf<CharT, Traits>::is_open() const +{ + return this->Stream != nullptr; +} + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::open( + uv_stream_t* stream) +{ + this->close(); + this->Stream = stream; + this->EndOfFile = false; + if (this->Stream) { + this->OldStreamData = this->Stream->data; + this->Stream->data = this; + } + this->StreamReadStartStop(); + return this; +} + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::close() +{ + if (this->Stream) { + uv_read_stop(this->Stream); + this->Stream->data = this->OldStreamData; + } + this->Stream = nullptr; + CharT* readEnd = this->InputBuffer.data() + this->InputBuffer.size(); + this->setg(readEnd, readEnd, readEnd); + return this; +} + +template <typename CharT, typename Traits> +typename cmBasicUVStreambuf<CharT, Traits>::int_type +cmBasicUVStreambuf<CharT, Traits>::underflow() +{ + if (!this->is_open()) { + return Traits::eof(); + } + + if (this->gptr() < this->egptr()) { + return Traits::to_int_type(*this->gptr()); + } + + this->StreamReadStartStop(); + while (this->in_avail() == 0) { + uv_run(this->Stream->loop, UV_RUN_ONCE); + } + if (this->in_avail() == -1) { + return Traits::eof(); + } + return Traits::to_int_type(*this->gptr()); +} + +template <typename CharT, typename Traits> +std::streamsize cmBasicUVStreambuf<CharT, Traits>::showmanyc() +{ + if (!this->is_open() || this->EndOfFile) { + return -1; + } + return 0; +} + +template <typename CharT, typename Traits> +void cmBasicUVStreambuf<CharT, Traits>::StreamReadStartStop() +{ + if (this->Stream) { + uv_read_stop(this->Stream); + if (this->gptr() >= this->egptr()) { + uv_read_start( + this->Stream, + [](uv_handle_t* handle, size_t /* unused */, uv_buf_t* buf) { + auto streambuf = + static_cast<cmBasicUVStreambuf<CharT, Traits>*>(handle->data); + streambuf->HandleAlloc(buf); + }, + [](uv_stream_t* stream2, ssize_t nread, const uv_buf_t* /* unused */) { + auto streambuf = + static_cast<cmBasicUVStreambuf<CharT, Traits>*>(stream2->data); + streambuf->StreamRead(nread); + }); + } + } +} + +template <typename CharT, typename Traits> +void cmBasicUVStreambuf<CharT, Traits>::HandleAlloc(uv_buf_t* buf) +{ + auto size = this->egptr() - this->gptr(); + std::memmove(this->InputBuffer.data(), this->gptr(), + this->egptr() - this->gptr()); + this->setg(this->InputBuffer.data(), this->InputBuffer.data(), + this->InputBuffer.data() + size); + buf->base = this->egptr(); +#ifdef _WIN32 +# define BUF_LEN_TYPE ULONG +#else +# define BUF_LEN_TYPE size_t +#endif + buf->len = BUF_LEN_TYPE( + (this->InputBuffer.data() + this->InputBuffer.size() - this->egptr()) * + sizeof(CharT)); +#undef BUF_LEN_TYPE +} + +template <typename CharT, typename Traits> +void cmBasicUVStreambuf<CharT, Traits>::StreamRead(ssize_t nread) +{ + if (nread > 0) { + this->setg(this->eback(), this->gptr(), + this->egptr() + nread / sizeof(CharT)); + uv_read_stop(this->Stream); + } else if (nread < 0 || nread == UV_EOF) { + this->EndOfFile = true; + uv_read_stop(this->Stream); + } +} + +using cmUVStreambuf = cmBasicUVStreambuf<char>; + +#endif diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 031ab01..e04bba2 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -16,9 +16,11 @@ set(CMakeLib_TESTS testXMLSafe.cxx testFindPackageCommand.cxx testUVRAII.cxx + testUVStreambuf.cxx ) set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR}) +set(testUVStreambuf_ARGS $<TARGET_FILE:cmake>) if(WIN32) list(APPEND CMakeLib_TESTS @@ -43,7 +45,7 @@ target_link_libraries(testEncoding cmsys) foreach(testfile ${CMakeLib_TESTS}) get_filename_component(test "${testfile}" NAME_WE) - add_test(CMakeLib.${test} CMakeLibTests ${test} ${${test}_ARGS}) + add_test(NAME CMakeLib.${test} COMMAND CMakeLibTests ${test} ${${test}_ARGS}) endforeach() if(TEST_CompileCommandOutput) diff --git a/Tests/CMakeLib/testUVRAII.cxx b/Tests/CMakeLib/testUVRAII.cxx index 1c1da76..2aeaf2c 100644 --- a/Tests/CMakeLib/testUVRAII.cxx +++ b/Tests/CMakeLib/testUVRAII.cxx @@ -171,11 +171,59 @@ static bool testAllMoves() return true; }; +static bool testLoopReset() +{ + bool closed = false; + cm::uv_loop_ptr loop; + loop.init(); + + uv_timer_t timer; + uv_timer_init(loop, &timer); + timer.data = &closed; + uv_close(reinterpret_cast<uv_handle_t*>(&timer), [](uv_handle_t* handle) { + auto closedPtr = static_cast<bool*>(handle->data); + *closedPtr = true; + }); + + loop.reset(); + if (!closed) { + std::cerr << "uv_loop_ptr did not finish" << std::endl; + return false; + } + + return true; +}; + +static bool testLoopDestructor() +{ + bool closed = false; + + uv_timer_t timer; + { + cm::uv_loop_ptr loop; + loop.init(); + + uv_timer_init(loop, &timer); + timer.data = &closed; + uv_close(reinterpret_cast<uv_handle_t*>(&timer), [](uv_handle_t* handle) { + auto closedPtr = static_cast<bool*>(handle->data); + *closedPtr = true; + }); + } + + if (!closed) { + std::cerr << "uv_loop_ptr did not finish" << std::endl; + return false; + } + + return true; +}; + int testUVRAII(int, char** const) { if ((testAsyncShutdown() && testAsyncDtor() & testAsyncMove() & testCrossAssignment() & - testAllMoves()) == 0) { + testAllMoves() & testLoopReset() & testLoopDestructor()) == 0) { return -1; } return 0; diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx new file mode 100644 index 0000000..39655f3 --- /dev/null +++ b/Tests/CMakeLib/testUVStreambuf.cxx @@ -0,0 +1,457 @@ +#include "cmUVStreambuf.h" + +#include "cmGetPipes.h" +#include "cmUVHandlePtr.h" + +#include "cm_uv.h" + +#include <iostream> +#include <string> +#include <vector> + +#include <cstring> + +#include <stdint.h> + +#define TEST_STR_LINE_1 "This string must be exactly 128 characters long so" +#define TEST_STR_LINE_2 "that we can test CMake's std::streambuf integration" +#define TEST_STR_LINE_3 "with libuv's uv_stream_t." +#define TEST_STR TEST_STR_LINE_1 "\n" TEST_STR_LINE_2 "\n" TEST_STR_LINE_3 + +bool writeDataToStreamPipe(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe, + char* outputData, unsigned int outputDataLength, + const char* /* unused */) +{ + int err; + + // Create the pipe + int pipeHandles[2]; + if (cmGetPipes(pipeHandles) < 0) { + std::cout << "Could not open pipe" << std::endl; + return false; + } + + cm::uv_pipe_ptr outputPipe; + inputPipe.init(loop, 0); + outputPipe.init(loop, 0); + uv_pipe_open(inputPipe, pipeHandles[0]); + uv_pipe_open(outputPipe, pipeHandles[1]); + + // Write data for reading + uv_write_t writeReq; + struct WriteCallbackData + { + bool Finished = false; + int Status; + } writeData; + writeReq.data = &writeData; + uv_buf_t outputBuf; + outputBuf.base = outputData; + outputBuf.len = outputDataLength; + if ((err = uv_write(&writeReq, outputPipe, &outputBuf, 1, + [](uv_write_t* req, int status) { + auto data = static_cast<WriteCallbackData*>(req->data); + data->Finished = true; + data->Status = status; + })) < 0) { + std::cout << "Could not write to pipe: " << uv_strerror(err) << std::endl; + return false; + } + while (!writeData.Finished) { + uv_run(&loop, UV_RUN_ONCE); + } + if (writeData.Status < 0) { + std::cout << "Status is " << uv_strerror(writeData.Status) + << ", should be 0" << std::endl; + return false; + } + + return true; +} + +bool writeDataToStreamProcess(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe, + char* outputData, unsigned int /* unused */, + const char* cmakeCommand) +{ + int err; + + inputPipe.init(loop, 0); + std::vector<std::string> arguments = { cmakeCommand, "-E", "echo_append", + outputData }; + std::vector<const char*> processArgs; + for (auto const& arg : arguments) { + processArgs.push_back(arg.c_str()); + } + processArgs.push_back(nullptr); + std::vector<uv_stdio_container_t> stdio(3); + stdio[0].flags = UV_IGNORE; + stdio[0].data.stream = nullptr; + stdio[1].flags = + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + stdio[1].data.stream = inputPipe; + stdio[2].flags = UV_IGNORE; + stdio[2].data.stream = nullptr; + + struct ProcessExitData + { + bool Finished = false; + int64_t ExitStatus; + int TermSignal; + } exitData; + cm::uv_process_ptr process; + auto options = uv_process_options_t(); + options.file = cmakeCommand; + options.args = const_cast<char**>(processArgs.data()); + options.flags = UV_PROCESS_WINDOWS_HIDE; + options.stdio = stdio.data(); + options.stdio_count = static_cast<int>(stdio.size()); + options.exit_cb = [](uv_process_t* handle, int64_t exitStatus, + int termSignal) { + auto data = static_cast<ProcessExitData*>(handle->data); + data->Finished = true; + data->ExitStatus = exitStatus; + data->TermSignal = termSignal; + }; + if ((err = process.spawn(loop, options, &exitData)) < 0) { + std::cout << "Could not spawn process: " << uv_strerror(err) << std::endl; + return false; + } + while (!exitData.Finished) { + uv_run(&loop, UV_RUN_ONCE); + } + if (exitData.ExitStatus != 0) { + std::cout << "Process exit status is " << exitData.ExitStatus + << ", should be 0" << std::endl; + return false; + } + if (exitData.TermSignal != 0) { + std::cout << "Process term signal is " << exitData.TermSignal + << ", should be 0" << std::endl; + return false; + } + + return true; +} + +bool testUVStreambufRead( + bool (*cb)(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe, char* outputData, + unsigned int outputDataLength, const char* cmakeCommand), + const char* cmakeCommand) +{ + char outputData[] = TEST_STR; + bool success = false; + cm::uv_loop_ptr loop; + loop.init(); + cm::uv_pipe_ptr inputPipe; + std::vector<char> inputData(128); + std::streamsize readLen; + std::string line; + cm::uv_timer_ptr timer; + + // Create the streambuf + cmUVStreambuf inputBuf(64); + std::istream inputStream(&inputBuf); + if (inputBuf.is_open()) { + std::cout << "is_open() is true, should be false" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != -1) { + std::cout << "in_avail() returned " << readLen << ", should be -1" + << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) { + std::cout << "sgetn() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + + // Perform first read test - read all the data + if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) { + goto end; + } + inputBuf.open(inputPipe); + if (!inputBuf.is_open()) { + std::cout << "is_open() is false, should be true" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != 0) { + std::cout << "in_avail() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 128) { + std::cout << "sgetn() returned " << readLen << ", should be 128" + << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != 0) { + std::cout << "in_avail() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + if (std::memcmp(inputData.data(), outputData, 128)) { + std::cout << "Read data does not match write data" << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) { + std::cout << "sgetn() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != -1) { + std::cout << "in_avail() returned " << readLen << ", should be -1" + << std::endl; + goto end; + } + inputData.assign(128, char{}); + inputBuf.close(); + if (inputBuf.is_open()) { + std::cout << "is_open() is true, should be false" << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) { + std::cout << "sgetn() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != -1) { + std::cout << "in_avail() returned " << readLen << ", should be -1" + << std::endl; + goto end; + } + + // Perform second read test - read some data and then close + if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) { + goto end; + } + inputBuf.open(inputPipe); + if (!inputBuf.is_open()) { + std::cout << "is_open() is false, should be true" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != 0) { + std::cout << "in_avail() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 64)) != 64) { + std::cout << "sgetn() returned " << readLen << ", should be 64" + << std::endl; + goto end; + } + if (std::memcmp(inputData.data(), outputData, 64)) { + std::cout << "Read data does not match write data" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != 8) { + std::cout << "in_avail() returned " << readLen << ", should be 8" + << std::endl; + goto end; + } + inputData.assign(128, char{}); + inputBuf.close(); + if (inputBuf.is_open()) { + std::cout << "is_open() is true, should be false" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != -1) { + std::cout << "in_avail() returned " << readLen << ", should be -1" + << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) { + std::cout << "sgetn() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + + // Perform third read test - read line by line + if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) { + goto end; + } + inputBuf.open(inputPipe); + if (!inputBuf.is_open()) { + std::cout << "is_open() is false, should be true" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != 0) { + std::cout << "in_avail() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + + if (!std::getline(inputStream, line)) { + std::cout << "getline returned false, should be true" << std::endl; + goto end; + } + if (line != TEST_STR_LINE_1) { + std::cout << "Line 1 is \"" << line + << "\", should be \"" TEST_STR_LINE_1 "\"" << std::endl; + goto end; + } + + if (!std::getline(inputStream, line)) { + std::cout << "getline returned false, should be true" << std::endl; + goto end; + } + if (line != TEST_STR_LINE_2) { + std::cout << "Line 2 is \"" << line + << "\", should be \"" TEST_STR_LINE_2 "\"" << std::endl; + goto end; + } + + if (!std::getline(inputStream, line)) { + std::cout << "getline returned false, should be true" << std::endl; + goto end; + } + if (line != TEST_STR_LINE_3) { + std::cout << "Line 3 is \"" << line + << "\", should be \"" TEST_STR_LINE_3 "\"" << std::endl; + goto end; + } + + if (std::getline(inputStream, line)) { + std::cout << "getline returned true, should be false" << std::endl; + goto end; + } + + inputBuf.close(); + if (inputBuf.is_open()) { + std::cout << "is_open() is true, should be false" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != -1) { + std::cout << "in_avail() returned " << readLen << ", should be -1" + << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) { + std::cout << "sgetn() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + + // Perform fourth read test - run the event loop outside of underflow() + if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) { + goto end; + } + inputBuf.open(inputPipe); + if (!inputBuf.is_open()) { + std::cout << "is_open() is false, should be true" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != 0) { + std::cout << "in_avail() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + uv_run(loop, UV_RUN_DEFAULT); + if ((readLen = inputBuf.in_avail()) != 72) { + std::cout << "in_avail() returned " << readLen << ", should be 72" + << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 128) { + std::cout << "sgetn() returned " << readLen << ", should be 128" + << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != 0) { + std::cout << "in_avail() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) { + std::cout << "sgetn() returned " << readLen << ", should be 128" + << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != -1) { + std::cout << "in_avail() returned " << readLen << ", should be -1" + << std::endl; + goto end; + } + + inputBuf.close(); + if (inputBuf.is_open()) { + std::cout << "is_open() is true, should be false" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != -1) { + std::cout << "in_avail() returned " << readLen << ", should be -1" + << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) { + std::cout << "sgetn() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + + // Perform fifth read test - close the streambuf in the middle of a read + timer.init(*loop, &inputBuf); + if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) { + goto end; + } + inputBuf.open(inputPipe); + if (!inputBuf.is_open()) { + std::cout << "is_open() is false, should be true" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != 0) { + std::cout << "in_avail() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + uv_timer_start(timer, + [](uv_timer_t* handle) { + auto buf = static_cast<cmUVStreambuf*>(handle->data); + buf->close(); + }, + 0, 0); + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) { + std::cout << "sgetn() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + if (inputBuf.is_open()) { + std::cout << "is_open() is true, should be false" << std::endl; + goto end; + } + if ((readLen = inputBuf.in_avail()) != -1) { + std::cout << "in_avail() returned " << readLen << ", should be -1" + << std::endl; + goto end; + } + if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) { + std::cout << "sgetn() returned " << readLen << ", should be 0" + << std::endl; + goto end; + } + + success = true; + +end: + return success; +} + +int testUVStreambuf(int argc, char** const argv) +{ + if (argc < 2) { + std::cout << "Invalid arguments.\n"; + return -1; + } + + if (!testUVStreambufRead(writeDataToStreamPipe, argv[1])) { + std::cout << "While executing testUVStreambufRead() with pipe.\n"; + return -1; + } + + if (!testUVStreambufRead(writeDataToStreamProcess, argv[1])) { + std::cout << "While executing testUVStreambufRead() with process.\n"; + return -1; + } + + return 0; +} diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index df0c78d..5ba0dc0 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -374,4 +374,49 @@ if(NOT CMAKE_GENERATOR STREQUAL Xcode OR NOT CMAKE_OSX_ARCHITECTURES MATCHES "[; -P "${CMAKE_CURRENT_SOURCE_DIR}/check_object_files.cmake" DEPENDS objlib ) + + + add_library(sharedlib SHARED objlib1.c objlib2.c) + file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/sharedlib_files_$<CONFIGURATION>" + CONTENT "$<JOIN:$<TARGET_OBJECTS:sharedlib>,\n>\n" + ) + + add_custom_target(check_sharedlib_objs ALL + COMMAND ${CMAKE_COMMAND} + "-DOBJLIB_LISTFILE=${CMAKE_CURRENT_BINARY_DIR}/sharedlib_files_$<CONFIGURATION>" + -DEXPECTED_NUM_OBJECTFILES=2 + -P "${CMAKE_CURRENT_SOURCE_DIR}/check_object_files.cmake" + DEPENDS sharedlib + ) + + + add_library(staticlib STATIC objlib1.c objlib2.c) + file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/staticlib_files_$<CONFIGURATION>" + CONTENT "$<JOIN:$<TARGET_OBJECTS:staticlib>,\n>\n" + ) + + add_custom_target(check_staticlib_objs ALL + COMMAND ${CMAKE_COMMAND} + "-DOBJLIB_LISTFILE=${CMAKE_CURRENT_BINARY_DIR}/staticlib_files_$<CONFIGURATION>" + -DEXPECTED_NUM_OBJECTFILES=2 + -P "${CMAKE_CURRENT_SOURCE_DIR}/check_object_files.cmake" + DEPENDS staticlib + ) + + + add_executable(execobjs objlib1.c objlib2.c echo.c) + file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/execobjs_files_$<CONFIGURATION>" + CONTENT "$<JOIN:$<TARGET_OBJECTS:execobjs>,\n>\n" + ) + + add_custom_target(check_exec_objs ALL + COMMAND ${CMAKE_COMMAND} + "-DOBJLIB_LISTFILE=${CMAKE_CURRENT_BINARY_DIR}/execobjs_files_$<CONFIGURATION>" + -DEXPECTED_NUM_OBJECTFILES=3 + -P "${CMAKE_CURRENT_SOURCE_DIR}/check_object_files.cmake" + DEPENDS execobjs + ) endif() diff --git a/Tests/MSVCRuntimeLibrary/CMakeLists.txt b/Tests/MSVCRuntimeLibrary/CMakeLists.txt index b7a6e86..6994d8d 100644 --- a/Tests/MSVCRuntimeLibrary/CMakeLists.txt +++ b/Tests/MSVCRuntimeLibrary/CMakeLists.txt @@ -42,7 +42,22 @@ endfunction() function(verify lang src) add_library(default-${lang} ${src}) target_compile_definitions(default-${lang} PRIVATE VERIFY_MT VERIFY_DLL "$<$<CONFIG:Debug>:VERIFY_DEBUG>") + verify_combinations(MultiThreaded ${lang} ${src}) + + # Test known MSVC default behavior when no flag is given. + if(CMAKE_${lang}_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_MSVC_RUNTIME_LIBRARY "") + add_library(empty-${lang} ${src}) + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 14) + # VS 2005 and above default to multi-threaded. + target_compile_definitions(empty-${lang} PRIVATE VERIFY_MT) + endif() + if(CMAKE_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])") + # VS 2010 and above have a different default runtime library for projects than 'cl'. + target_compile_definitions(empty-${lang} PRIVATE VERIFY_DLL) + endif() + endif() endfunction() verify(C verify.c) diff --git a/Tests/ObjectLibrary/CMakeLists.txt b/Tests/ObjectLibrary/CMakeLists.txt index 4bffd12..7897ab9 100644 --- a/Tests/ObjectLibrary/CMakeLists.txt +++ b/Tests/ObjectLibrary/CMakeLists.txt @@ -62,4 +62,14 @@ add_custom_target(UseABinternalDep COMMAND ${CMAKE_COMMAND} -E touch UseABintern add_custom_command(TARGET UseABinternal POST_BUILD COMMAND ${CMAKE_COMMAND} -P UseABinternalDep.cmake) add_dependencies(UseABinternal UseABinternalDep) +# Test a static library with sources from a different static library +add_library(UseCstaticObjs STATIC $<TARGET_OBJECTS:Cstatic> $<TARGET_OBJECTS:A> $<TARGET_OBJECTS:Bexport>) + +# Test a shared library with sources from a different shared library +add_library(UseCsharedObjs SHARED $<TARGET_OBJECTS:Cshared> $<TARGET_OBJECTS:A> $<TARGET_OBJECTS:Bexport>) + +# Test a shared executable with sources from a different shared library +add_executable(UseABstaticObjs $<TARGET_OBJECTS:UseABstatic>) +target_link_libraries(UseABstaticObjs ABstatic) + add_subdirectory(ExportLanguages) diff --git a/Tests/RunCMake/File_Generate/OutputNameMatchesObjects-stderr.txt b/Tests/RunCMake/File_Generate/OutputNameMatchesObjects-stderr.txt index b08ef5a..4c2e35f 100644 --- a/Tests/RunCMake/File_Generate/OutputNameMatchesObjects-stderr.txt +++ b/Tests/RunCMake/File_Generate/OutputNameMatchesObjects-stderr.txt @@ -3,6 +3,7 @@ CMake Error at OutputNameMatchesObjects.cmake:[0-9]+ \(file\): \$<TARGET_OBJECTS:foo> - Objects of target "foo" referenced but is not an OBJECT library. + Objects of target "foo" referenced but is not an allowed library types + \(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT\). Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/File_Generate/OutputNameMatchesObjects.cmake b/Tests/RunCMake/File_Generate/OutputNameMatchesObjects.cmake index daa7c49..84825fe 100644 --- a/Tests/RunCMake/File_Generate/OutputNameMatchesObjects.cmake +++ b/Tests/RunCMake/File_Generate/OutputNameMatchesObjects.cmake @@ -1,11 +1,8 @@ enable_language(CXX) file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<BOOL:$<TARGET_OBJECTS:foo>>somefile.cpp" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>/somefile.cpp" CONTENT "static const char content[] = \"$<TARGET_OBJECTS:foo>\";\n" ) -add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/input.txt" - COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/input.txt" "${CMAKE_CURRENT_BINARY_DIR}") - -add_executable(foo empty.cpp "${CMAKE_CURRENT_BINARY_DIR}/1somefile.cpp" "${CMAKE_CURRENT_BINARY_DIR}/input.txt") +add_library(foo INTERFACE ) diff --git a/Tests/RunCMake/ObjectLibrary/BadSourceExpression3-stderr.txt b/Tests/RunCMake/ObjectLibrary/BadSourceExpression3-stderr.txt index 838b3d8..4dbd861 100644 --- a/Tests/RunCMake/ObjectLibrary/BadSourceExpression3-stderr.txt +++ b/Tests/RunCMake/ObjectLibrary/BadSourceExpression3-stderr.txt @@ -3,6 +3,7 @@ CMake Error at BadSourceExpression3.cmake:2 \(add_library\): \$<TARGET_OBJECTS:NotObjLib> - Objects of target "NotObjLib" referenced but is not an OBJECT library. + Objects of target "NotObjLib" referenced but is not an allowed library + types \(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT\). Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/ObjectLibrary/BadSourceExpression3.cmake b/Tests/RunCMake/ObjectLibrary/BadSourceExpression3.cmake index c3d9a62..4e07ea6 100644 --- a/Tests/RunCMake/ObjectLibrary/BadSourceExpression3.cmake +++ b/Tests/RunCMake/ObjectLibrary/BadSourceExpression3.cmake @@ -1,2 +1,2 @@ -add_library(NotObjLib STATIC a.c) +add_library(NotObjLib INTERFACE) add_library(A STATIC a.c $<TARGET_OBJECTS:NotObjLib>) diff --git a/Tests/RunCMake/ObjectLibrary/CheckTargetObjects.cmake b/Tests/RunCMake/ObjectLibrary/CheckTargetObjects.cmake new file mode 100644 index 0000000..0c85c72 --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/CheckTargetObjects.cmake @@ -0,0 +1,32 @@ +add_library(StaticLib STATIC a.c) + +add_custom_command(TARGET StaticLib POST_BUILD + VERBATIM + COMMAND ${CMAKE_COMMAND} + "-DTARGET_OBJECTS=$<TARGET_OBJECTS:StaticLib>" + -DEXPECTED_NUM_OBJECTFILES=2 + -P "${CMAKE_CURRENT_SOURCE_DIR}/check_object_files.cmake" + ) + +add_library(SharedLib SHARED a.c b.c) +target_compile_definitions(SharedLib PRIVATE REQUIRED) + +add_custom_command(TARGET SharedLib POST_BUILD + VERBATIM + COMMAND ${CMAKE_COMMAND} + "-DTARGET_OBJECTS:STRING=$<TARGET_OBJECTS:SharedLib>" + -DEXPECTED_NUM_OBJECTFILES=2 + -P "${CMAKE_CURRENT_SOURCE_DIR}/check_object_files.cmake" + ) + +add_executable(ExecObjs a.c b.c exe.c) +target_compile_definitions(ExecObjs PRIVATE REQUIRED) + +add_custom_target(check_exec_objs ALL + VERBATIM + COMMAND ${CMAKE_COMMAND} + "-DTARGET_OBJECTS=$<TARGET_OBJECTS:ExecObjs>" + -DEXPECTED_NUM_OBJECTFILES=3 + -P "${CMAKE_CURRENT_SOURCE_DIR}/check_object_files.cmake" + DEPENDS ExecObjs + ) diff --git a/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake b/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake index 6ca33b8..5ec4018 100644 --- a/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake +++ b/Tests/RunCMake/ObjectLibrary/RunCMakeTest.cmake @@ -37,6 +37,10 @@ function (run_object_lib_build2 name) run_cmake_command(${name}-build ${CMAKE_COMMAND} --build .) endfunction () +if(NOT (RunCMake_GENERATOR STREQUAL "Xcode" AND "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]")) + run_object_lib_build(CheckTargetObjects) +endif() + run_object_lib_build(LinkObjLHSShared) run_object_lib_build(LinkObjLHSStatic) run_object_lib_build(LinkObjRHSShared) @@ -54,6 +58,7 @@ run_cmake(PostBuild) run_cmake(PreBuild) run_cmake(PreLink) + function(run_Dependencies) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Dependencies-build) set(RunCMake_TEST_NO_CLEAN 1) diff --git a/Tests/RunCMake/ObjectLibrary/check_object_files.cmake b/Tests/RunCMake/ObjectLibrary/check_object_files.cmake new file mode 100644 index 0000000..3c34229 --- /dev/null +++ b/Tests/RunCMake/ObjectLibrary/check_object_files.cmake @@ -0,0 +1,17 @@ + +if (NOT TARGET_OBJECTS) + message(SEND_ERROR "Object not passed as -DTARGET_OBJECTS") +endif() + +foreach(objlib_file IN LISTS objects) + message(STATUS "objlib_file: =${objlib_file}=") + + set(file_exists False) + if (EXISTS "${objlib_file}") + set(file_exists True) + endif() + + if (NOT file_exists) + message(SEND_ERROR "File \"${objlib_file}\" does not exist!${tried}") + endif() +endforeach() diff --git a/Tests/RunCMake/TargetObjects/NotObjlibTarget-stderr.txt b/Tests/RunCMake/TargetObjects/NotObjlibTarget-stderr.txt index a66794c..77c4afd 100644 --- a/Tests/RunCMake/TargetObjects/NotObjlibTarget-stderr.txt +++ b/Tests/RunCMake/TargetObjects/NotObjlibTarget-stderr.txt @@ -1,8 +1,9 @@ -CMake Error at NotObjlibTarget.cmake:3 \(file\): +CMake Error at NotObjlibTarget.cmake:[0-9]+ \(file\): Error evaluating generator expression: - \$<TARGET_OBJECTS:StaticLib> + \$<TARGET_OBJECTS:IFaceLib> - Objects of target "StaticLib" referenced but is not an OBJECT library. + Objects of target "IFaceLib" referenced but is not an allowed library types + \(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT\). Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/TargetObjects/NotObjlibTarget.cmake b/Tests/RunCMake/TargetObjects/NotObjlibTarget.cmake index 3bb3e37..9fec369 100644 --- a/Tests/RunCMake/TargetObjects/NotObjlibTarget.cmake +++ b/Tests/RunCMake/TargetObjects/NotObjlibTarget.cmake @@ -1,3 +1,3 @@ -add_library(StaticLib empty.cpp) +add_library(IFaceLib INTERFACE ) -file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test_output CONTENT $<TARGET_OBJECTS:StaticLib>) +file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test_output CONTENT $<TARGET_OBJECTS:IFaceLib>) diff --git a/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake b/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake index 6b43d47..689b35f 100644 --- a/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake +++ b/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake @@ -20,7 +20,7 @@ macro(RuntimeLibrary_check tgt rtl_expect) endif() endforeach() - if(NOT HAVE_Runtimelibrary) + if(NOT HAVE_Runtimelibrary AND NOT "${rtl_expect}" STREQUAL "") set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a RuntimeLibrary field.") return() endif() @@ -28,6 +28,8 @@ endmacro() RuntimeLibrary_check(default-C MultiThreadedDebugDLL) RuntimeLibrary_check(default-CXX MultiThreadedDebugDLL) +RuntimeLibrary_check(empty-C "") +RuntimeLibrary_check(empty-CXX "") RuntimeLibrary_check(MTd-C MultiThreadedDebug) RuntimeLibrary_check(MTd-CXX MultiThreadedDebug) RuntimeLibrary_check(MT-C MultiThreaded) diff --git a/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake b/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake index 6c77a25..d7787c8 100644 --- a/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake +++ b/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake @@ -6,6 +6,10 @@ enable_language(CXX) add_library(default-C empty.c) add_library(default-CXX empty.cxx) +set(CMAKE_MSVC_RUNTIME_LIBRARY "") +add_library(empty-C empty.c) +add_library(empty-CXX empty.cxx) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") add_library(MTd-C empty.c) add_library(MTd-CXX empty.cxx) diff --git a/Tests/RunCMake/execute_process/EchoCommand-result.txt b/Tests/RunCMake/execute_process/EchoCommand-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/execute_process/EchoCommand-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/execute_process/EchoCommand-stderr.txt b/Tests/RunCMake/execute_process/EchoCommand-stderr.txt new file mode 100644 index 0000000..f10ece8 --- /dev/null +++ b/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$ diff --git a/Tests/RunCMake/execute_process/EchoCommand-stdout.txt b/Tests/RunCMake/execute_process/EchoCommand-stdout.txt new file mode 100644 index 0000000..0954b3b --- /dev/null +++ b/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$ diff --git a/Tests/RunCMake/execute_process/EchoCommand.cmake b/Tests/RunCMake/execute_process/EchoCommand.cmake new file mode 100644 index 0000000..9c7d13d --- /dev/null +++ b/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() diff --git a/Tests/RunCMake/execute_process/EchoCommand2-result.txt b/Tests/RunCMake/execute_process/EchoCommand2-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/execute_process/EchoCommand2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/execute_process/EchoCommand2-stderr.txt b/Tests/RunCMake/execute_process/EchoCommand2-stderr.txt new file mode 100644 index 0000000..4ae01c4 --- /dev/null +++ b/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.$ diff --git a/Tests/RunCMake/execute_process/EchoCommand2-stdout.txt b/Tests/RunCMake/execute_process/EchoCommand2-stdout.txt new file mode 100644 index 0000000..0954b3b --- /dev/null +++ b/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$ diff --git a/Tests/RunCMake/execute_process/EchoCommand3-result.txt b/Tests/RunCMake/execute_process/EchoCommand3-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/execute_process/EchoCommand3-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt b/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt new file mode 100644 index 0000000..e27f1e6 --- /dev/null +++ b/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. diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake index cb40b40..b203aab 100644 --- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake +++ b/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) diff --git a/Tests/SourceGroups/CMakeLists.txt b/Tests/SourceGroups/CMakeLists.txt index 813774d..a5740bb 100644 --- a/Tests/SourceGroups/CMakeLists.txt +++ b/Tests/SourceGroups/CMakeLists.txt @@ -42,8 +42,16 @@ set(tree_files_with_prefix ${root}/tree_prefix_foo.c set(tree_files_with_empty_prefix ${root}/tree_empty_prefix_foo.c tree_empty_prefix_bar.c) +set(tree_files_which_are_actually_directories ${root} + ${root}/ + ${root}/sub1 + ${root}/sub1/) + source_group(TREE ${root} FILES ${tree_files_without_prefix}) +# Should not crash and not add any files - just silently ignore the directories +source_group(TREE ${root} FILES ${tree_files_which_are_actually_directories}) + source_group(FILES ${tree_files_with_prefix} PREFIX tree_root/subgroup TREE ${root}) source_group(PREFIX "" FILES ${tree_files_with_empty_prefix} TREE ${root}) |