diff options
51 files changed, 1196 insertions, 760 deletions
diff --git a/Help/command/ctest_build.rst b/Help/command/ctest_build.rst index e7a54d1..e1b7793 100644 --- a/Help/command/ctest_build.rst +++ b/Help/command/ctest_build.rst @@ -1,34 +1,73 @@ ctest_build ----------- -Build the project. +Perform the :ref:`CTest Build Step` as a :ref:`Dashboard Client`. :: - ctest_build([BUILD build_dir] [TARGET target] [RETURN_VALUE res] - [APPEND][NUMBER_ERRORS val] [NUMBER_WARNINGS val]) + ctest_build([BUILD <build-dir>] [APPEND] + [CONFIGURATION <config>] + [FLAGS <flags>] + [PROJECT_NAME <project-name>] + [TARGET <target-name>] + [NUMBER_ERRORS <num-err-var>] + [NUMBER_WARNINGS <num-warn-var>] + [RETURN_VALUE <result-var>] + ) -Builds the given build directory and stores results in Build.xml. If -no BUILD is given, the CTEST_BINARY_DIRECTORY variable is used. +Build the project and store results in ``Build.xml`` +for submission with the :command:`ctest_submit` command. -The TARGET variable can be used to specify a build target. If none is -specified, the "all" target will be built. +The :variable:`CTEST_BUILD_COMMAND` variable may be set to explicitly +specify the build command line. Otherwise the build command line is +computed automatically based on the options given. -The RETURN_VALUE option specifies a variable in which to store the -return value of the native build tool. The NUMBER_ERRORS and -NUMBER_WARNINGS options specify variables in which to store the number -of build errors and warnings detected. +The options are: -The APPEND option marks results for append to those previously -submitted to a dashboard server since the last ctest_start. Append -semantics are defined by the dashboard server in use. +``BUILD <build-dir>`` + Specify the top-level build directory. If not given, the + :variable:`CTEST_BINARY_DIRECTORY` variable is used. -The QUIET option suppresses any CTest-specific non-error output -that would have been printed to the console otherwise. The summary -of warnings / errors, as well as the output from the native build tool -is unaffected by this option. +``APPEND`` + Mark results for append to those previously submitted to a + dashboard server since the last :command:`ctest_start` call. + Append semantics are defined by the dashboard server in use. -If set, the contents of the variable CTEST_BUILD_FLAGS are passed as -additional arguments to the underlying build command. This can e.g. be -used to trigger a parallel build using the -j option of make. See -:module:`ProcessorCount` for an example. +``CONFIGURATION <config>`` + Specify the build configuration (e.g. ``Debug``). If not + specified the ``CTEST_BUILD_CONFIGURATION`` variable will be checked. + Otherwise the ``-C <cfg>`` option given to the :manual:`ctest(1)` + command will be used, if any. + +``FLAGS <flags>`` + Pass additional arguments to the underlying build command. + If not specified the ``CTEST_BUILD_FLAGS`` variable will be checked. + This can, e.g., be used to trigger a parallel build using the + ``-j`` option of make. See the :module:`ProcessorCount` module + for an example. + +``PROJECT_NAME <project-name>`` + Set the name of the project to build. This should correspond + to the top-level call to the :command:`project` command. + If not specified the ``CTEST_PROJECT_NAME`` variable will be checked. + +``TARGET <target-name>`` + Specify the name of a target to build. If not specified the + ``CTEST_BUILD_TARGET`` variable will be checked. Otherwise the + default target will be built. This is the "all" target + (called ``ALL_BUILD`` in :ref:`Visual Studio Generators`). + +``NUMBER_ERRORS <num-err-var>`` + Store the number of build errors detected in the given variable. + +``NUMBER_WARNINGS <num-warn-var>`` + Store the number of build warnings detected in the given variable. + +``RETURN_VALUE <result-var>`` + Store the return value of the native build tool in the given variable. + +``QUIET`` + Suppress any CTest-specific non-error output that would have been + printed to the console otherwise. The summary of warnings / errors, + as well as the output from the native build tool is unaffected by + this option. diff --git a/Help/command/ctest_configure.rst b/Help/command/ctest_configure.rst index 61d9320..851c292 100644 --- a/Help/command/ctest_configure.rst +++ b/Help/command/ctest_configure.rst @@ -1,25 +1,39 @@ ctest_configure --------------- -Configure the project build tree. +Perform the :ref:`CTest Configure Step` as a :ref:`Dashboard Client`. :: - ctest_configure([BUILD build_dir] [SOURCE source_dir] [APPEND] - [OPTIONS options] [RETURN_VALUE res] [QUIET]) + ctest_configure([BUILD <build-dir>] [SOURCE <source-dir>] [APPEND] + [OPTIONS <options>] [RETURN_VALUE <result-var>] [QUIET]) -Configures the given build directory and stores results in -Configure.xml. If no BUILD is given, the CTEST_BINARY_DIRECTORY -variable is used. If no SOURCE is given, the CTEST_SOURCE_DIRECTORY -variable is used. The OPTIONS argument specifies command line -arguments to pass to the configuration tool. The RETURN_VALUE option -specifies a variable in which to store the return value of the native -build tool. +Configure the project build tree and record results in ``Configure.xml`` +for submission with the :command:`ctest_submit` command. -The APPEND option marks results for append to those previously -submitted to a dashboard server since the last ctest_start. Append -semantics are defined by the dashboard server in use. +The options are: -The QUIET option suppresses any CTest-specific non-error messages -that would have otherwise been printed to the console. Output from -the underlying configure command is not affected. +``BUILD <build-dir>`` + Specify the top-level build directory. If not given, the + :variable:`CTEST_BINARY_DIRECTORY` variable is used. + +``SOURCE <source-dir>`` + Specify the source directory. If not given, the + :variable:`CTEST_SOURCE_DIRECTORY` variable is used. + +``APPEND`` + Mark results for append to those previously submitted to a + dashboard server since the last :command:`ctest_start` call. + Append semantics are defined by the dashboard server in use. + +``OPTIONS <options>`` + Specify command-line arguments to pass to the configuration tool. + +``RETURN_VALUE <result-var>`` + Store in the ``<result-var>`` variable the return value of the native + configuration tool. + +``QUIET`` + Suppress any CTest-specific non-error messages that would have + otherwise been printed to the console. Output from the underlying + configure command is not affected. diff --git a/Help/command/ctest_coverage.rst b/Help/command/ctest_coverage.rst index bac5c1c..12429b9 100644 --- a/Help/command/ctest_coverage.rst +++ b/Help/command/ctest_coverage.rst @@ -1,25 +1,39 @@ ctest_coverage -------------- -Collect coverage tool results. +Perform the :ref:`CTest Coverage Step` as a :ref:`Dashboard Client`. :: - ctest_coverage([BUILD build_dir] [RETURN_VALUE res] [APPEND] - [LABELS label1 [label2 [...]]]) + ctest_coverage([BUILD <build-dir>] [APPEND] + [LABELS <label>...] + [RETURN_VALUE <result-var>] + [QUIET] + ) -Perform the coverage of the given build directory and stores results -in Coverage.xml. The second argument is a variable that will hold -value. +Collect coverage tool results and stores them in ``Coverage.xml`` +for submission with the :command:`ctest_submit` command. -The LABELS option filters the coverage report to include only source -files labeled with at least one of the labels specified. +The options are: -The APPEND option marks results for append to those previously -submitted to a dashboard server since the last ctest_start. Append -semantics are defined by the dashboard server in use. +``BUILD <build-dir>`` + Specify the top-level build directory. If not given, the + :variable:`CTEST_BINARY_DIRECTORY` variable is used. -The QUIET option suppresses any CTest-specific non-error output -that would have been printed to the console otherwise. The summary -indicating how many lines of code were covered is unaffected by this -option. +``APPEND`` + Mark results for append to those previously submitted to a + dashboard server since the last :command:`ctest_start` call. + Append semantics are defined by the dashboard server in use. + +``LABELS`` + Filter the coverage report to include only source files labeled + with at least one of the labels specified. + +``RETURN_VALUE <result-var>`` + Store in the ``<result-var>`` variable ``0`` if coverage tools + ran without error and non-zero otherwise. + +``QUIET`` + Suppress any CTest-specific non-error output that would have been + printed to the console otherwise. The summary indicating how many + lines of code were covered is unaffected by this option. diff --git a/Help/command/ctest_memcheck.rst b/Help/command/ctest_memcheck.rst index 56a2490..2800511 100644 --- a/Help/command/ctest_memcheck.rst +++ b/Help/command/ctest_memcheck.rst @@ -1,32 +1,28 @@ ctest_memcheck -------------- -Run tests with a dynamic analysis tool. +Perform the :ref:`CTest MemCheck Step` as a :ref:`Dashboard Client`. :: - ctest_memcheck([BUILD build_dir] [RETURN_VALUE res] [APPEND] - [START start number] [END end number] - [STRIDE stride number] [EXCLUDE exclude regex ] - [INCLUDE include regex] - [EXCLUDE_LABEL exclude regex] - [INCLUDE_LABEL label regex] - [PARALLEL_LEVEL level] ) + ctest_memcheck([BUILD <build-dir>] [APPEND] + [START <start-number>] + [END <end-number>] + [STRIDE <stride-number>] + [EXCLUDE <exclude-regex>] + [INCLUDE <include-regex>] + [EXCLUDE_LABEL <label-exclude-regex>] + [INCLUDE_LABEL <label-include-regex>] + [PARALLEL_LEVEL <level>] + [SCHEDULE_RANDOM <ON|OFF>] + [STOP_TIME <time-of-day>] + [RETURN_VALUE <result-var>] + [QUIET] + ) -Tests the given build directory and stores results in MemCheck.xml. -The second argument is a variable that will hold value. Optionally, -you can specify the starting test number START, the ending test number -END, the number of tests to skip between each test STRIDE, a regular -expression for tests to run INCLUDE, or a regular expression for tests -not to run EXCLUDE. EXCLUDE_LABEL and INCLUDE_LABEL are regular -expressions for tests to be included or excluded by the test property -LABEL. PARALLEL_LEVEL should be set to a positive number representing -the number of tests to be run in parallel. -The APPEND option marks results for append to those previously -submitted to a dashboard server since the last ctest_start. Append -semantics are defined by the dashboard server in use. +Run tests with a dynamic analysis tool and store results in +``MemCheck.xml`` for submission with the :command:`ctest_submit` +command. -The QUIET option suppresses any CTest-specific non-error messages -that would have otherwise been printed to the console. Output from -the underlying tests are not affected. +The options are the same as those for the :command:`ctest_test` command. diff --git a/Help/command/ctest_submit.rst b/Help/command/ctest_submit.rst index 6fa1191..6830b59 100644 --- a/Help/command/ctest_submit.rst +++ b/Help/command/ctest_submit.rst @@ -1,46 +1,55 @@ ctest_submit ------------ -Submit results to a dashboard server. +Perform the :ref:`CTest Submit Step` as a :ref:`Dashboard Client`. :: - ctest_submit([PARTS ...] [FILES ...] - [RETRY_COUNT count] - [RETRY_DELAY delay] - [RETURN_VALUE res] + ctest_submit([PARTS <part>...] [FILES <file>...] + [RETRY_COUNT <count>] + [RETRY_DELAY <delay>] + [RETURN_VALUE <result-var>] [QUIET] ) -By default all available parts are submitted if no PARTS or FILES are -specified. The PARTS option lists a subset of parts to be submitted. -Valid part names are: +Submit results to a dashboard server. +By default all available parts are submitted. -:: +The options are: + +``PARTS <part>...`` + Specify a subset of parts to submit. Valid part names are:: + + Start = nothing + Update = ctest_update results, in Update.xml + Configure = ctest_configure results, in Configure.xml + Build = ctest_build results, in Build.xml + Test = ctest_test results, in Test.xml + Coverage = ctest_coverage results, in Coverage.xml + MemCheck = ctest_memcheck results, in DynamicAnalysis.xml + Notes = Files listed by CTEST_NOTES_FILES, in Notes.xml + ExtraFiles = Files listed by CTEST_EXTRA_SUBMIT_FILES + Upload = Files prepared for upload by ctest_upload(), in Upload.xml + Submit = nothing + +``FILES <file>...`` + Specify an explicit list of specific files to be submitted. + Each individual file must exist at the time of the call. + +``RETRY_COUNT <count>`` + Specify how many times to retry a timed-out submission. + +``RETRY_DELAY <delay>`` + Specify how long (in seconds) to wait after a timed-out submission + before attempting to re-submit. + +``RETURN_VALUE <result-var>`` + Store in the ``<result-var>`` variable ``0`` for success and + non-zero on failure. - Start = nothing - Update = ctest_update results, in Update.xml - Configure = ctest_configure results, in Configure.xml - Build = ctest_build results, in Build.xml - Test = ctest_test results, in Test.xml - Coverage = ctest_coverage results, in Coverage.xml - MemCheck = ctest_memcheck results, in DynamicAnalysis.xml - Notes = Files listed by CTEST_NOTES_FILES, in Notes.xml - ExtraFiles = Files listed by CTEST_EXTRA_SUBMIT_FILES - Upload = Files prepared for upload by ctest_upload(), in Upload.xml - Submit = nothing - -The FILES option explicitly lists specific files to be submitted. -Each individual file must exist at the time of the call. - -The RETRY_DELAY option specifies how long in seconds to wait after a -timed-out submission before attempting to re-submit. - -The RETRY_COUNT option specifies how many times to retry a timed-out -submission. - -The QUIET option suppresses all non-error messages that would have -otherwise been printed by this call to ctest_submit(). +``QUIET`` + Suppress all non-error messages that would have otherwise been + printed to the console. Submit to CDash Upload API ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/command/ctest_test.rst b/Help/command/ctest_test.rst index ee76e91..8cbb9ec 100644 --- a/Help/command/ctest_test.rst +++ b/Help/command/ctest_test.rst @@ -1,39 +1,79 @@ ctest_test ---------- -Run tests in the project build tree. +Perform the :ref:`CTest Test Step` as a :ref:`Dashboard Client`. :: - ctest_test([BUILD build_dir] [APPEND] - [START start number] [END end number] - [STRIDE stride number] [EXCLUDE exclude regex ] - [INCLUDE include regex] [RETURN_VALUE res] - [EXCLUDE_LABEL exclude regex] - [INCLUDE_LABEL label regex] - [PARALLEL_LEVEL level] - [SCHEDULE_RANDOM on] - [STOP_TIME time of day]) - -Tests the given build directory and stores results in Test.xml. The -second argument is a variable that will hold value. Optionally, you -can specify the starting test number START, the ending test number -END, the number of tests to skip between each test STRIDE, a regular -expression for tests to run INCLUDE, or a regular expression for tests -to not run EXCLUDE. EXCLUDE_LABEL and INCLUDE_LABEL are regular -expression for test to be included or excluded by the test property -LABEL. PARALLEL_LEVEL should be set to a positive number representing -the number of tests to be run in parallel. SCHEDULE_RANDOM will -launch tests in a random order, and is typically used to detect -implicit test dependencies. STOP_TIME is the time of day at which the -tests should all stop running. - -The APPEND option marks results for append to those previously -submitted to a dashboard server since the last ctest_start. Append -semantics are defined by the dashboard server in use. - -The QUIET option suppresses any CTest-specific non-error messages -that would have otherwise been printed to the console. Output from -the underlying test command is not affected. Summary info detailing -the percentage of passing tests is also unaffected by the QUIET -option. + ctest_test([BUILD <build-dir>] [APPEND] + [START <start-number>] + [END <end-number>] + [STRIDE <stride-number>] + [EXCLUDE <exclude-regex>] + [INCLUDE <include-regex>] + [EXCLUDE_LABEL <label-exclude-regex>] + [INCLUDE_LABEL <label-include-regex>] + [PARALLEL_LEVEL <level>] + [SCHEDULE_RANDOM <ON|OFF>] + [STOP_TIME <time-of-day>] + [RETURN_VALUE <result-var>] + [QUIET] + ) + +Run tests in the project build tree and store results in +``Test.xml`` for submission with the :command:`ctest_submit` command. + +The options are: + +``BUILD <build-dir>`` + Specify the top-level build directory. If not given, the + :variable:`CTEST_BINARY_DIRECTORY` variable is used. + +``APPEND`` + Mark results for append to those previously submitted to a + dashboard server since the last :command:`ctest_start` call. + Append semantics are defined by the dashboard server in use. + +``START <start-number>`` + Specify the beginning of a range of test numbers. + +``END <end-number>`` + Specify the end of a range of test numbers. + +``STRIDE <stride-number>`` + Specify the stride by which to step acorss a range of test numbers. + +``EXCLUDE <exclude-regex>`` + Specify a regular expression matching test names to exclude. + +``INCLUDE <include-regex>`` + Specify a regular expression matching test names to include. + Tests not matching this expression are excluded. + +``EXCLUDE_LABEL <label-exclude-regex>`` + Specify a regular expression matching test labels to exclude. + +``INCLUDE_LABEL <label-include-regex>`` + Specify a regular expression matching test labels to include. + Tests not matching this expression are excluded. + +``PARALLEL_LEVEL <level>`` + Specify a positive number representing the number of tests to + be run in parallel. + +``SCHEDULE_RANDOM <ON|OFF>`` + Launch tests in a random order. This may be useful for detecting + implicit test dependencies. + +``STOP_TIME <time-of-day>`` + Specify a time of day at which the tests should all stop running. + +``RETURN_VALUE <result-var>`` + Store in the ``<result-var>`` variable ``0`` if all tests passed. + Store non-zero if anything went wrong. + +``QUIET`` + Suppress any CTest-specific non-error messages that would have otherwise + been printed to the console. Output from the underlying test command is not + affected. Summary info detailing the percentage of passing tests is also + unaffected by the ``QUIET`` option. diff --git a/Help/command/ctest_update.rst b/Help/command/ctest_update.rst index 01e357b..74af1f7 100644 --- a/Help/command/ctest_update.rst +++ b/Help/command/ctest_update.rst @@ -1,18 +1,27 @@ ctest_update ------------ -Update the work tree from version control. +Perform the :ref:`CTest Update Step` as a :ref:`Dashboard Client`. :: - ctest_update([SOURCE source] [RETURN_VALUE res] [QUIET]) + ctest_update([SOURCE <source-dir>] [RETURN_VALUE <result-var>] [QUIET]) -Updates the given source directory and stores results in Update.xml. -If no SOURCE is given, the CTEST_SOURCE_DIRECTORY variable is used. -The RETURN_VALUE option specifies a variable in which to store the -result, which is the number of files updated or -1 on error. +Update the source tree from version control and record results in +``Update.xml`` for submission with the :command:`ctest_submit` command. -If QUIET is specified then CTest will suppress most non-error -messages that it would have otherwise printed to the console. -CTest will still report the new revision of the repository -and any conflicting files that were found. +The options are: + +``SOURCE <source-dir>`` + Specify the source directory. If not given, the + :variable:`CTEST_SOURCE_DIRECTORY` variable is used. + +``RETURN_VALUE <result-var>`` + Store in the ``<result-var>`` variable the number of files + updated or ``-1`` on error. + +``QUIET`` + Tell CTest to suppress most non-error messages that it would + have otherwise printed to the console. CTest will still report + the new revision of the repository and any conflicting files + that were found. diff --git a/Help/command/ctest_upload.rst b/Help/command/ctest_upload.rst index fcd9fe4..d9630d2 100644 --- a/Help/command/ctest_upload.rst +++ b/Help/command/ctest_upload.rst @@ -1,14 +1,18 @@ ctest_upload ------------ -Upload files to a dashboard server. +Upload files to a dashboard server as a :ref:`Dashboard Client`. :: - ctest_upload(FILES ... [QUIET]) + ctest_upload(FILES <file>... [QUIET]) -Pass a list of files to be sent along with the build results to the -dashboard server. +The options are: -The QUIET option suppresses any CTest-specific non-error output -that would have been printed to the console otherwise. +``FILES <file>...`` + Specify a list of files to be sent along with the build results to the + dashboard server. + +``QUIET`` + Suppress any CTest-specific non-error output that would have been + printed to the console otherwise. diff --git a/Help/command/execute_process.rst b/Help/command/execute_process.rst index 478b30e..c38ec1a 100644 --- a/Help/command/execute_process.rst +++ b/Help/command/execute_process.rst @@ -57,7 +57,8 @@ OUTPUT_VARIABLE, ERROR_VARIABLE INPUT_FILE, OUTPUT_FILE, ERROR_FILE The file named will be attached to the standard input of the first process, standard output of the last process, or standard error of - all processes, respectively. + all processes, respectively. If the same file is named for both + output and error then it will be used for both. OUTPUT_QUIET, ERROR_QUIET The standard output or standard error results will be quietly ignored. diff --git a/Help/manual/cmake-generators.7.rst b/Help/manual/cmake-generators.7.rst index 723c308..4482f95 100644 --- a/Help/manual/cmake-generators.7.rst +++ b/Help/manual/cmake-generators.7.rst @@ -73,7 +73,6 @@ Visual Studio Generators .. toctree:: :maxdepth: 1 - /generator/Green Hills MULTI /generator/Visual Studio 6 /generator/Visual Studio 7 /generator/Visual Studio 7 .NET 2003 @@ -84,12 +83,13 @@ Visual Studio Generators /generator/Visual Studio 12 2013 /generator/Visual Studio 14 2015 -Xcode Generator -^^^^^^^^^^^^^^^ +Other Generators +^^^^^^^^^^^^^^^^ .. toctree:: :maxdepth: 1 + /generator/Green Hills MULTI /generator/Xcode Extra Generators diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index cbc8a07..e6c966b 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -268,6 +268,7 @@ Variables that Control the Build /variable/CMAKE_TRY_COMPILE_CONFIGURATION /variable/CMAKE_USE_RELATIVE_PATHS /variable/CMAKE_VISIBILITY_INLINES_HIDDEN + /variable/CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD /variable/CMAKE_WIN32_EXECUTABLE /variable/CMAKE_XCODE_ATTRIBUTE_an-attribute /variable/EXECUTABLE_OUTPUT_PATH diff --git a/Help/release/dev/cpack-deb-component-auto-discovery.rst b/Help/release/dev/cpack-deb-component-auto-discovery.rst new file mode 100644 index 0000000..cc74db2 --- /dev/null +++ b/Help/release/dev/cpack-deb-component-auto-discovery.rst @@ -0,0 +1,6 @@ +cpack-deb-component-auto-discovery +---------------------------------- + +* The :module:`CPackDeb` module learned a new + :variable:`CPACK_DEBIAN_<COMPONENT>_PACKAGE_SHLIBDEPS` + variable to specify per-component use of ``dpkg-shlibdeps``. diff --git a/Help/release/dev/execute_process-merge-output.rst b/Help/release/dev/execute_process-merge-output.rst new file mode 100644 index 0000000..4c80cdd --- /dev/null +++ b/Help/release/dev/execute_process-merge-output.rst @@ -0,0 +1,5 @@ +execute_process-merge-output +---------------------------- + +* The :command:`execute_process` command learned to support specifying + the same file for ``OUTPUT_FILE`` and ``ERROR_FILE``. diff --git a/Help/release/dev/vs-install-in-default-build.rst b/Help/release/dev/vs-install-in-default-build.rst new file mode 100644 index 0000000..b57592e --- /dev/null +++ b/Help/release/dev/vs-install-in-default-build.rst @@ -0,0 +1,7 @@ +vs-install-in-default-build +--------------------------- + +* The :ref:`Visual Studio Generators` learned a new + :variable:`CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD` option + to put the ``INSTALL`` target in the default build of a + solution (``.sln``) file. diff --git a/Help/variable/CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD.rst b/Help/variable/CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD.rst new file mode 100644 index 0000000..68f1ff6 --- /dev/null +++ b/Help/variable/CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD.rst @@ -0,0 +1,8 @@ +CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD +----------------------------------------- + +Include INSTALL target to default build. + +In Visual Studio solution, by default the INSTALL target will not be part of +the default build. Setting this variable will enable the INSTALL target to be +part of the default build. diff --git a/Modules/CPackDeb.cmake b/Modules/CPackDeb.cmake index 24452a6..d1d5d09 100644 --- a/Modules/CPackDeb.cmake +++ b/Modules/CPackDeb.cmake @@ -103,16 +103,23 @@ # characters such as <>. # # .. variable:: CPACK_DEBIAN_PACKAGE_SHLIBDEPS -# -# * Mandatory : NO -# * Default : OFF +# CPACK_DEBIAN_<COMPONENT>_PACKAGE_SHLIBDEPS # # May be set to ON in order to use dpkg-shlibdeps to generate # better package dependency list. -# You may need set CMAKE_INSTALL_RPATH toi appropriate value -# if you use this feature, because if you don't dpkg-shlibdeps -# may fail to find your own shared libs. -# See http://www.cmake.org/Wiki/CMake_RPATH_handling. +# +# * Mandatory : NO +# * Default : +# +# - :variable:`CPACK_DEBIAN_PACKAGE_SHLIBDEPS` if set or +# - OFF +# +# .. note:: +# +# You may need set :variable:`CMAKE_INSTALL_RPATH` to an appropriate value +# if you use this feature, because if you don't :code:`dpkg-shlibdeps` +# may fail to find your own shared libs. +# See http://www.cmake.org/Wiki/CMake_RPATH_handling. # # .. variable:: CPACK_DEBIAN_PACKAGE_DEBUG # @@ -245,92 +252,135 @@ function(cpack_deb_prepare_package_vars) set(CPACK_DEBIAN_FAKEROOT_EXECUTABLE ${FAKEROOT_EXECUTABLE}) endif() + set(WDIR "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_DEB_PACKAGE_COMPONENT_PART_PATH}") + + # per component automatic discover: some of the component might not have + # binaries. + if(CPACK_DEB_PACKAGE_COMPONENT) + string(TOUPPER "${CPACK_DEB_PACKAGE_COMPONENT}" _local_component_name) + set(_component_shlibdeps_var "CPACK_DEBIAN_${_local_component_name}_PACKAGE_SHLIBDEPS") + + # if set, overrides the global configuration + if(DEFINED ${_component_shlibdeps_var}) + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS "${${_component_shlibdeps_var}}") + if(CPACK_DEBIAN_PACKAGE_DEBUG) + message("CPackDeb Debug: component '${CPACK_DEB_PACKAGE_COMPONENT}' dpkg-shlibdeps set to ${CPACK_DEBIAN_PACKAGE_SHLIBDEPS}") + endif() + endif() + endif() + if(CPACK_DEBIAN_PACKAGE_SHLIBDEPS) # dpkg-shlibdeps is a Debian utility for generating dependency list find_program(SHLIBDEPS_EXECUTABLE dpkg-shlibdeps) - # Check version of the dpkg-shlibdeps tool using CPackRPM method if(SHLIBDEPS_EXECUTABLE) + # Check version of the dpkg-shlibdeps tool using CPackRPM method execute_process(COMMAND env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --version OUTPUT_VARIABLE _TMP_VERSION ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) string(REGEX MATCH "dpkg-shlibdeps version ([0-9]+\\.[0-9]+\\.[0-9]+)" SHLIBDEPS_EXECUTABLE_VERSION - ${_TMP_VERSION}) + "${_TMP_VERSION}") set(SHLIBDEPS_EXECUTABLE_VERSION "${CMAKE_MATCH_1}") + if(CPACK_DEBIAN_PACKAGE_DEBUG) - message( "CPackDeb Debug: dpkg-shlibdeps version is <${SHLIBDEPS_EXECUTABLE_VERSION}>") + message("CPackDeb Debug: dpkg-shlibdeps --version output is '${_TMP_VERSION}'") + message("CPackDeb Debug: dpkg-shlibdeps version is <${SHLIBDEPS_EXECUTABLE_VERSION}>") endif() # Generating binary list - Get type of all install files - execute_process(COMMAND find -type f - COMMAND xargs file - WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" - OUTPUT_VARIABLE CPACK_DEB_INSTALL_FILES) - - # Convert to CMake list - string(REPLACE "\n" ";" CPACK_DEB_INSTALL_FILES ${CPACK_DEB_INSTALL_FILES}) + cmake_policy(PUSH) + # Tell file(GLOB_RECURSE) not to follow directory symlinks + # even if the project does not set this policy to NEW. + cmake_policy(SET CMP0009 NEW) + file(GLOB_RECURSE FILE_PATHS_ LIST_DIRECTORIES false RELATIVE "${WDIR}" "${WDIR}/*") + cmake_policy(POP) + + # get file info so that we can determine if file is executable or not + unset(CPACK_DEB_INSTALL_FILES) + foreach(FILE_ IN LISTS FILE_PATHS_) + execute_process(COMMAND file "./${FILE_}" + WORKING_DIRECTORY "${WDIR}" + OUTPUT_VARIABLE INSTALL_FILE_) + list(APPEND CPACK_DEB_INSTALL_FILES "${INSTALL_FILE_}") + endforeach() # Only dynamically linked ELF files are included # Extract only file name infront of ":" - foreach ( _FILE ${CPACK_DEB_INSTALL_FILES}) - if ( ${_FILE} MATCHES "ELF.*dynamically linked") - string(REGEX MATCH "(^.*):" _FILE_NAME ${_FILE}) - list(APPEND CPACK_DEB_BINARY_FILES ${CMAKE_MATCH_1}) + foreach(_FILE ${CPACK_DEB_INSTALL_FILES}) + if( ${_FILE} MATCHES "ELF.*dynamically linked") + string(REGEX MATCH "(^.*):" _FILE_NAME "${_FILE}") + list(APPEND CPACK_DEB_BINARY_FILES "${CMAKE_MATCH_1}") + set(CONTAINS_EXECUTABLE_FILES_ TRUE) endif() endforeach() - message( "CPackDeb: - Generating dependency list") - - # Create blank control file for running dpkg-shlibdeps - # There might be some other way to invoke dpkg-shlibdeps without creating this file - # but standard debian package should not have anything that can collide with this file or directory - file(MAKE_DIRECTORY ${CPACK_TEMPORARY_DIRECTORY}/debian) - file(WRITE ${CPACK_TEMPORARY_DIRECTORY}/debian/control "") - - # Execute dpkg-shlibdeps - # --ignore-missing-info : allow dpkg-shlibdeps to run even if some libs do not belong to a package - # -O : print to STDOUT - execute_process(COMMAND ${SHLIBDEPS_EXECUTABLE} --ignore-missing-info -O ${CPACK_DEB_BINARY_FILES} - WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" - OUTPUT_VARIABLE SHLIBDEPS_OUTPUT - RESULT_VARIABLE SHLIBDEPS_RESULT - ERROR_VARIABLE SHLIBDEPS_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE ) - if(CPACK_DEBIAN_PACKAGE_DEBUG) - # dpkg-shlibdeps will throw some warnings if some input files are not binary - message( "CPackDeb Debug: dpkg-shlibdeps warnings \n${SHLIBDEPS_ERROR}") - endif() - if (NOT SHLIBDEPS_RESULT EQUAL 0) - message (FATAL_ERROR "CPackDeb: dpkg-shlibdeps: ${SHLIBDEPS_ERROR}") - endif () + if(CONTAINS_EXECUTABLE_FILES_) + message("CPackDeb: - Generating dependency list") - #Get rid of prefix generated by dpkg-shlibdeps - string (REGEX REPLACE "^.*Depends=" "" CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS ${SHLIBDEPS_OUTPUT}) + # Create blank control file for running dpkg-shlibdeps + # There might be some other way to invoke dpkg-shlibdeps without creating this file + # but standard debian package should not have anything that can collide with this file or directory + file(MAKE_DIRECTORY ${CPACK_TEMPORARY_DIRECTORY}/debian) + file(WRITE ${CPACK_TEMPORARY_DIRECTORY}/debian/control "") - if(CPACK_DEBIAN_PACKAGE_DEBUG) - message( "CPackDeb Debug: Found dependency: ${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}") - endif() + # only set ignore-missing-info flag for dpkg-shlibdeps that have --version option + # (those are newer and also have --ignore-missing-info flag) + if(SHLIBDEPS_EXECUTABLE_VERSION) + set(IGNORE_MISSING_INFO_FLAG "--ignore-missing-info") + endif() + + # Execute dpkg-shlibdeps + # --ignore-missing-info : allow dpkg-shlibdeps to run even if some libs do not belong to a package + # -O : print to STDOUT + execute_process(COMMAND ${SHLIBDEPS_EXECUTABLE} ${IGNORE_MISSING_INFO_FLAG} -O ${CPACK_DEB_BINARY_FILES} + WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" + OUTPUT_VARIABLE SHLIBDEPS_OUTPUT + RESULT_VARIABLE SHLIBDEPS_RESULT + ERROR_VARIABLE SHLIBDEPS_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE ) + if(CPACK_DEBIAN_PACKAGE_DEBUG) + # dpkg-shlibdeps will throw some warnings if some input files are not binary + message( "CPackDeb Debug: dpkg-shlibdeps warnings \n${SHLIBDEPS_ERROR}") + endif() + if(NOT SHLIBDEPS_RESULT EQUAL 0) + message (FATAL_ERROR "CPackDeb: dpkg-shlibdeps: '${SHLIBDEPS_ERROR}';\n" + "executed command: '${SHLIBDEPS_EXECUTABLE} ${IGNORE_MISSING_INFO_FLAG} -O ${CPACK_DEB_BINARY_FILES}';\n" + "found files: '${INSTALL_FILE_}';\n" + "files info: '${CPACK_DEB_INSTALL_FILES}';\n" + "binary files: '${CPACK_DEB_BINARY_FILES}'") + endif() + + #Get rid of prefix generated by dpkg-shlibdeps + string(REGEX REPLACE "^.*Depends=" "" CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS "${SHLIBDEPS_OUTPUT}") - # Remove blank control file - # Might not be safe if package actual contain file or directory named debian - file(REMOVE_RECURSE "${CPACK_TEMPORARY_DIRECTORY}/debian") + if(CPACK_DEBIAN_PACKAGE_DEBUG) + message( "CPackDeb Debug: Found dependency: ${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}") + endif() - # Append user depend if set - if (CPACK_DEBIAN_PACKAGE_DEPENDS) - set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}, ${CPACK_DEBIAN_PACKAGE_DEPENDS}") - else () - set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}") - endif () + # Remove blank control file + # Might not be safe if package actual contain file or directory named debian + file(REMOVE_RECURSE "${CPACK_TEMPORARY_DIRECTORY}/debian") - else () + # Append user depend if set + if(CPACK_DEBIAN_PACKAGE_DEPENDS) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}, ${CPACK_DEBIAN_PACKAGE_DEPENDS}") + else() + set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}") + endif() + else() + if(CPACK_DEBIAN_PACKAGE_DEBUG) + message( "CPackDeb Debug: Using only user-provided depends because package does not contain executable files that contain dynamically linked libraries.") + endif() + endif() + else() if(CPACK_DEBIAN_PACKAGE_DEBUG) message( "CPackDeb Debug: Using only user-provided depends because dpkg-shlibdeps is not found.") endif() endif() - else () + else() if(CPACK_DEBIAN_PACKAGE_DEBUG) message( "CPackDeb Debug: Using only user-provided depends") endif() @@ -452,8 +502,6 @@ function(cpack_deb_prepare_package_vars) set(CPACK_DEB_PACKAGE_COMPONENT_PART_NAME "") endif() - set(WDIR "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_DEB_PACKAGE_COMPONENT_PART_PATH}") - # Print out some debug information if we were asked for that if(CPACK_DEBIAN_PACKAGE_DEBUG) message("CPackDeb:Debug: CPACK_TOPLEVEL_DIRECTORY = ${CPACK_TOPLEVEL_DIRECTORY}") diff --git a/Modules/CPackRPM.cmake b/Modules/CPackRPM.cmake index fdba90e..e672e72 100644 --- a/Modules/CPackRPM.cmake +++ b/Modules/CPackRPM.cmake @@ -1686,7 +1686,7 @@ mv \"\@CPACK_TOPLEVEL_DIRECTORY\@/tmpBBroot\" $RPM_BUILD_ROOT message("CPackRPM:Debug: - ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_COMPONENT_PART_NAME}.err") message("CPackRPM:Debug: *** ${RPMBUILDERR} ***") message("CPackRPM:Debug: - ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_COMPONENT_PART_NAME}.out") - message("CPackRPM:Debug: *** ${RPMBUILDERR} ***") + message("CPackRPM:Debug: *** ${RPMBUILDOUT} ***") endif() else() if(ALIEN_EXECUTABLE) diff --git a/Modules/Platform/CYGWIN-GNU.cmake b/Modules/Platform/CYGWIN-GNU.cmake index 3144ac4..1a46c10 100644 --- a/Modules/Platform/CYGWIN-GNU.cmake +++ b/Modules/Platform/CYGWIN-GNU.cmake @@ -53,5 +53,9 @@ macro(__cygwin_compiler_gnu lang) set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS} -Wl,--enable-auto-import") set(CMAKE_SHARED_MODULE_CREATE_${lang}_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS}") + if(NOT CMAKE_RC_COMPILER_INIT) + set(CMAKE_RC_COMPILER_INIT windres) + endif() + enable_language(RC) endmacro() diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake index c827c32..c0d7d8c 100644 --- a/Modules/Platform/Windows-GNU.cmake +++ b/Modules/Platform/Windows-GNU.cmake @@ -138,6 +138,10 @@ macro(__windows_compiler_gnu lang) endforeach() endif() + if(NOT CMAKE_RC_COMPILER_INIT) + set(CMAKE_RC_COMPILER_INIT windres) + endif() + enable_language(RC) endmacro() diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index 2905cee..13fe8bc 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake @@ -298,6 +298,9 @@ macro(__windows_compiler_msvc lang) set(CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "/MD /O1 /Ob1 /D NDEBUG") set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON) + if(NOT CMAKE_RC_COMPILER_INIT) + set(CMAKE_RC_COMPILER_INIT rc) + endif() if(NOT CMAKE_RC_FLAGS_INIT) set(CMAKE_RC_FLAGS_INIT "${_PLATFORM_DEFINES} ${_PLATFORM_DEFINES_${lang}}") endif() diff --git a/Modules/WriteCompilerDetectionHeader.cmake b/Modules/WriteCompilerDetectionHeader.cmake index f4dcb21..a3b73bb 100644 --- a/Modules/WriteCompilerDetectionHeader.cmake +++ b/Modules/WriteCompilerDetectionHeader.cmake @@ -246,10 +246,10 @@ function(write_compiler_detection_header file_keyword file_arg prefix_keyword prefix_arg ) - if (NOT file_keyword STREQUAL FILE) + if (NOT "x${file_keyword}" STREQUAL "xFILE") message(FATAL_ERROR "write_compiler_detection_header: FILE parameter missing.") endif() - if (NOT prefix_keyword STREQUAL PREFIX) + if (NOT "x${prefix_keyword}" STREQUAL "xPREFIX") message(FATAL_ERROR "write_compiler_detection_header: PREFIX parameter missing.") endif() set(options) diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 76d3865..cf7a523 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 2) -set(CMake_VERSION_PATCH 20150506) +set(CMake_VERSION_PATCH 20150512) #set(CMake_VERSION_RC 1) diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 1225992..a371390 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -255,7 +255,7 @@ bool cmExecuteProcessCommand cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); // Check the output variables. - bool merge_output = (output_variable == error_variable); + bool merge_output = false; if(!input_file.empty()) { cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN, input_file.c_str()); @@ -267,8 +267,23 @@ bool cmExecuteProcessCommand } if(!error_file.empty()) { - cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR, - error_file.c_str()); + if (error_file == output_file) + { + merge_output = true; + } + else + { + cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR, + error_file.c_str()); + } + } + if (!output_variable.empty() && output_variable == error_variable) + { + merge_output = true; + } + if (merge_output) + { + cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1); } // Set the timeout if any. @@ -289,8 +304,7 @@ bool cmExecuteProcessCommand while((p = cmsysProcess_WaitForData(cp, &data, &length, 0), p)) { // Put the output in the right place. - if((p == cmsysProcess_Pipe_STDOUT && !output_quiet) || - (p == cmsysProcess_Pipe_STDERR && !error_quiet && merge_output)) + if (p == cmsysProcess_Pipe_STDOUT && !output_quiet) { if(output_variable.empty()) { diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 1c90537..746be4d 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -2421,38 +2421,6 @@ bool cmGlobalGenerator::UseFolderProperty() } //---------------------------------------------------------------------------- -void cmGlobalGenerator::EnableMinGWLanguage(cmMakefile *mf) -{ - this->FindMakeProgram(mf); - std::string makeProgram = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); - std::vector<std::string> locations; - locations.push_back(cmSystemTools::GetProgramPath(makeProgram)); - locations.push_back("/mingw/bin"); - locations.push_back("c:/mingw/bin"); - std::string tgcc = cmSystemTools::FindProgram("gcc", locations); - std::string gcc = "gcc.exe"; - if(!tgcc.empty()) - { - gcc = tgcc; - } - std::string tgxx = cmSystemTools::FindProgram("g++", locations); - std::string gxx = "g++.exe"; - if(!tgxx.empty()) - { - gxx = tgxx; - } - std::string trc = cmSystemTools::FindProgram("windres", locations); - std::string rc = "windres.exe"; - if(!trc.empty()) - { - rc = trc; - } - mf->AddDefinition("CMAKE_GENERATOR_CC", gcc.c_str()); - mf->AddDefinition("CMAKE_GENERATOR_CXX", gxx.c_str()); - mf->AddDefinition("CMAKE_GENERATOR_RC", rc.c_str()); -} - -//---------------------------------------------------------------------------- cmTarget cmGlobalGenerator::CreateGlobalTarget( const std::string& name, const char* message, const cmCustomCommandLines* commandLines, diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 7107198..22ba288 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -433,7 +433,6 @@ protected: virtual const char* GetPredefinedTargetsFolder(); virtual bool UseFolderProperty(); - void EnableMinGWLanguage(cmMakefile *mf); private: cmMakefile* TryCompileOuterMakefile; diff --git a/Source/cmGlobalMinGWMakefileGenerator.cxx b/Source/cmGlobalMinGWMakefileGenerator.cxx index c9389aa..b128870 100644 --- a/Source/cmGlobalMinGWMakefileGenerator.cxx +++ b/Source/cmGlobalMinGWMakefileGenerator.cxx @@ -26,7 +26,33 @@ void cmGlobalMinGWMakefileGenerator cmMakefile *mf, bool optional) { - this->EnableMinGWLanguage(mf); + this->FindMakeProgram(mf); + std::string makeProgram = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + std::vector<std::string> locations; + locations.push_back(cmSystemTools::GetProgramPath(makeProgram)); + locations.push_back("/mingw/bin"); + locations.push_back("c:/mingw/bin"); + std::string tgcc = cmSystemTools::FindProgram("gcc", locations); + std::string gcc = "gcc.exe"; + if(!tgcc.empty()) + { + gcc = tgcc; + } + std::string tgxx = cmSystemTools::FindProgram("g++", locations); + std::string gxx = "g++.exe"; + if(!tgxx.empty()) + { + gxx = tgxx; + } + std::string trc = cmSystemTools::FindProgram("windres", locations); + std::string rc = "windres.exe"; + if(!trc.empty()) + { + rc = trc; + } + mf->AddDefinition("CMAKE_GENERATOR_CC", gcc.c_str()); + mf->AddDefinition("CMAKE_GENERATOR_CXX", gxx.c_str()); + mf->AddDefinition("CMAKE_GENERATOR_RC", rc.c_str()); this->cmGlobalUnixMakefileGenerator3::EnableLanguage(l, mf, optional); } diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 678d60b..65e80e4 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -96,7 +96,7 @@ std::string cmGlobalNinjaGenerator::EncodePath(const std::string &path) { std::string result = path; #ifdef _WIN32 - if(UsingMinGW) + if (this->IsGCCOnWindows()) cmSystemTools::ReplaceString(result, "\\", "/"); else cmSystemTools::ReplaceString(result, "/", "\\"); @@ -484,6 +484,7 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator() , CompileCommandsStream(0) , Rules() , AllDependencies() + , UsingGCCOnWindows(false) , ComputingUnknownDependencies(false) , PolicyCMP0058(cmPolicies::WARN) { @@ -544,24 +545,16 @@ void cmGlobalNinjaGenerator::Generate() this->CloseBuildFileStream(); } -// Implemented in all cmGlobaleGenerator sub-classes. -// Used in: -// Source/cmMakefile.cxx: void cmGlobalNinjaGenerator ::EnableLanguage(std::vector<std::string>const& langs, - cmMakefile* makefile, + cmMakefile* mf, bool optional) { - if (makefile->IsOn("CMAKE_COMPILER_IS_MINGW")) - { - UsingMinGW = true; - this->EnableMinGWLanguage(makefile); - } if (std::find(langs.begin(), langs.end(), "Fortran") != langs.end()) { cmSystemTools::Error("The Ninja generator does not support Fortran yet."); } - this->cmGlobalGenerator::EnableLanguage(langs, makefile, optional); + this->cmGlobalGenerator::EnableLanguage(langs, mf, optional); for(std::vector<std::string>::const_iterator l = langs.begin(); l != langs.end(); ++l) { @@ -569,12 +562,20 @@ void cmGlobalNinjaGenerator { continue; } - this->ResolveLanguageCompiler(*l, makefile, optional); + this->ResolveLanguageCompiler(*l, mf, optional); } +#ifdef _WIN32 + if (mf->IsOn("CMAKE_COMPILER_IS_MINGW") || + strcmp(mf->GetSafeDefinition("CMAKE_C_COMPILER_ID"), "GNU") == 0 || + strcmp(mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID"), "GNU") == 0 || + strcmp(mf->GetSafeDefinition("CMAKE_C_SIMULATE_ID"), "GNU") == 0 || + strcmp(mf->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID"), "GNU") == 0) + { + this->UsingGCCOnWindows = true; + } +#endif } -bool cmGlobalNinjaGenerator::UsingMinGW = false; - // Implemented by: // cmGlobalUnixMakefileGenerator3 // cmGlobalGhsMultiGenerator diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index d7b3add..00dc237 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -63,7 +63,7 @@ public: static std::string EncodeIdent(const std::string &ident, std::ostream &vars); static std::string EncodeLiteral(const std::string &lit); - static std::string EncodePath(const std::string &path); + std::string EncodePath(const std::string &path); static std::string EncodeDepfileSpace(const std::string &path); /** @@ -155,9 +155,7 @@ public: const cmNinjaDeps& targets, const std::string& comment = ""); - - static bool IsMinGW() { return UsingMinGW; } - + bool IsGCCOnWindows() const { return UsingGCCOnWindows; } public: /// Default constructor. @@ -362,6 +360,8 @@ private: /// The set of dependencies to add to the "all" target. cmNinjaDeps AllDependencies; + bool UsingGCCOnWindows; + /// The set of custom commands we have seen. std::set<cmCustomCommand const*> CustomCommands; @@ -385,9 +385,6 @@ private: typedef std::map<std::string, cmTarget*> TargetAliasMap; TargetAliasMap TargetAliases; - - static bool UsingMinGW; - }; #endif // ! cmGlobalNinjaGenerator_h diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index de90f7e..ead5ddc 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -1031,6 +1031,24 @@ cmGlobalVisualStudio7Generator::IsPartOfDefaultBuild( int type = target->GetType(); if (type == cmTarget::GLOBAL_TARGET) { + // check if INSTALL target is part of default build + if(target->GetName() == "INSTALL") + { + // inspect CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD properties + for(std::vector<std::string>::iterator i = this->Configurations.begin(); + i != this->Configurations.end(); ++i) + { + const char* propertyValue = target->GetMakefile() + ->GetDefinition("CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD"); + cmGeneratorExpression ge; + cmsys::auto_ptr<cmCompiledGeneratorExpression> + cge = ge.Parse(propertyValue); + if(cmSystemTools::IsOn(cge->Evaluate(target->GetMakefile(), *i))) + { + activeConfigs.insert(*i); + } + } + } return activeConfigs; } if(type == cmTarget::UTILITY && !this->IsDependedOn(projectTargets, target)) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 155a30e..4c51d23 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -202,7 +202,12 @@ cmNinjaNormalTargetGenerator responseFlag += rspfile; // build response file content - rspcontent = "$in_newline $LINK_PATH $LINK_LIBRARIES"; + if (this->GetGlobalGenerator()->IsGCCOnWindows()) { + rspcontent = "$in"; + } else { + rspcontent = "$in_newline"; + } + rspcontent += " $LINK_PATH $LINK_LIBRARIES"; vars.Objects = responseFlag.c_str(); vars.LinkLibraries = ""; } diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index dfd3c04..128a35b 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -175,7 +175,7 @@ cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile const* source, // needed by cmcldeps false, this->GetConfigName()); - if(cmGlobalNinjaGenerator::IsMinGW()) + if (this->GetGlobalGenerator()->IsGCCOnWindows()) cmSystemTools::ReplaceString(includeFlags, "\\", "/"); this->LocalGenerator->AppendFlags(languageFlags, includeFlags); diff --git a/Source/kwsys/.gitattributes b/Source/kwsys/.gitattributes new file mode 100644 index 0000000..248786e --- /dev/null +++ b/Source/kwsys/.gitattributes @@ -0,0 +1,16 @@ +.git* export-ignore +.gitattributes -export-ignore + +/GitSetup export-ignore +/SetupForDevelopment.sh export-ignore eol=lf + +/CONTRIBUTING.rst conflict-marker-size=78 + +*.c whitespace=tab-in-indent,no-lf-at-eof +*.h whitespace=tab-in-indent,no-lf-at-eof +*.h.in whitespace=tab-in-indent,no-lf-at-eof +*.cxx whitespace=tab-in-indent,no-lf-at-eof +*.hxx whitespace=tab-in-indent,no-lf-at-eof +*.hxx.in whitespace=tab-in-indent,no-lf-at-eof +*.txt whitespace=tab-in-indent,no-lf-at-eof +*.cmake whitespace=tab-in-indent,no-lf-at-eof diff --git a/Source/kwsys/DynamicLoader.cxx b/Source/kwsys/DynamicLoader.cxx index 66c7d57..a776f97 100644 --- a/Source/kwsys/DynamicLoader.cxx +++ b/Source/kwsys/DynamicLoader.cxx @@ -48,6 +48,10 @@ DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(const kwsys_stl::string& //---------------------------------------------------------------------------- int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) { + if (!lib) + { + return 0; + } return !shl_unload(lib); } diff --git a/Source/kwsys/Glob.hxx.in b/Source/kwsys/Glob.hxx.in index 39b7ce7..5239ccd 100644 --- a/Source/kwsys/Glob.hxx.in +++ b/Source/kwsys/Glob.hxx.in @@ -59,6 +59,12 @@ public: type(msg.type), content(msg.content) {} + Message& operator=(Message const& msg) + { + this->type = msg.type; + this->content = msg.content; + return *this; + } }; typedef kwsys_stl::vector<Message> GlobMessages; diff --git a/Source/kwsys/Process.h.in b/Source/kwsys/Process.h.in index c5995ea..e35939f 100644 --- a/Source/kwsys/Process.h.in +++ b/Source/kwsys/Process.h.in @@ -36,6 +36,7 @@ # define kwsysProcess_SetPipeShared kwsys_ns(Process_SetPipeShared) # define kwsysProcess_Option_Detach kwsys_ns(Process_Option_Detach) # define kwsysProcess_Option_HideWindow kwsys_ns(Process_Option_HideWindow) +# define kwsysProcess_Option_MergeOutput kwsys_ns(Process_Option_MergeOutput) # define kwsysProcess_Option_Verbatim kwsys_ns(Process_Option_Verbatim) # define kwsysProcess_GetOption kwsys_ns(Process_GetOption) # define kwsysProcess_SetOption kwsys_ns(Process_SetOption) @@ -186,6 +187,12 @@ kwsysEXPORT void kwsysProcess_SetPipeNative(kwsysProcess* cp, int pipe, * 0 = No (default) * 1 = Yes * + * kwsysProcess_Option_MergeOutput = Whether to merge stdout/stderr. + * No content will be returned as stderr. + * Any actual stderr will be on stdout. + * 0 = No (default) + * 1 = Yes + * * kwsysProcess_Option_Verbatim = Whether SetCommand and AddCommand * should treat the first argument * as a verbatim command line @@ -200,6 +207,7 @@ enum kwsysProcess_Option_e { kwsysProcess_Option_HideWindow, kwsysProcess_Option_Detach, + kwsysProcess_Option_MergeOutput, kwsysProcess_Option_Verbatim }; @@ -384,6 +392,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp); # undef kwsysProcess_SetPipeShared # undef kwsysProcess_Option_Detach # undef kwsysProcess_Option_HideWindow +# undef kwsysProcess_Option_MergeOutput # undef kwsysProcess_Option_Verbatim # undef kwsysProcess_GetOption # undef kwsysProcess_SetOption diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c index 1be6d02..0393a6d 100644 --- a/Source/kwsys/ProcessUNIX.c +++ b/Source/kwsys/ProcessUNIX.c @@ -157,7 +157,7 @@ static void kwsysProcessCleanupDescriptor(int* pfd); static void kwsysProcessClosePipes(kwsysProcess* cp); static int kwsysProcessSetNonBlocking(int fd); static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, - kwsysProcessCreateInformation* si, int* readEnd); + kwsysProcessCreateInformation* si); static void kwsysProcessDestroy(kwsysProcess* cp); static int kwsysProcessSetupOutputPipeFile(int* p, const char* name); static int kwsysProcessSetupOutputPipeNative(int* p, int des[2]); @@ -203,6 +203,10 @@ struct kwsysProcess_s the signal pipe. */ int PipeReadEnds[KWSYSPE_PIPE_COUNT]; + /* Descriptors for the child's ends of the pipes. + Used temporarily during process creation. */ + int PipeChildStd[3]; + /* Write descriptor for child termination signal pipe. */ int SignalPipe; @@ -230,6 +234,9 @@ struct kwsysProcess_s /* Whether to treat command lines as verbatim. */ int Verbatim; + /* Whether to merge stdout/stderr of the child. */ + int MergeOutput; + /* Time at which the child started. Negative for no timeout. */ kwsysProcessTime StartTime; @@ -640,6 +647,7 @@ int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) switch(optionId) { case kwsysProcess_Option_Detach: return cp->OptionDetach; + case kwsysProcess_Option_MergeOutput: return cp->MergeOutput; case kwsysProcess_Option_Verbatim: return cp->Verbatim; default: return 0; } @@ -656,6 +664,7 @@ void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) switch(optionId) { case kwsysProcess_Option_Detach: cp->OptionDetach = value; break; + case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break; case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break; default: break; } @@ -717,7 +726,6 @@ const char* kwsysProcess_GetExceptionString(kwsysProcess* cp) void kwsysProcess_Execute(kwsysProcess* cp) { int i; - kwsysProcessCreateInformation si = {-1, -1, -1, {-1, -1}}; /* Do not execute a second copy simultaneously. */ if(!cp || cp->State == kwsysProcess_State_Executing) @@ -785,7 +793,50 @@ void kwsysProcess_Execute(kwsysProcess* cp) } } - /* Setup the stderr pipe to be shared by all processes. */ + /* Setup the stdin pipe for the first process. */ + if(cp->PipeFileSTDIN) + { + /* Open a file for the child's stdin to read. */ + cp->PipeChildStd[0] = open(cp->PipeFileSTDIN, O_RDONLY); + if(cp->PipeChildStd[0] < 0) + { + kwsysProcessCleanup(cp, 1); + return; + } + + /* Set close-on-exec flag on the pipe's end. */ + if(fcntl(cp->PipeChildStd[0], F_SETFD, FD_CLOEXEC) < 0) + { + kwsysProcessCleanup(cp, 1); + return; + } + } + else if(cp->PipeSharedSTDIN) + { + cp->PipeChildStd[0] = 0; + } + else if(cp->PipeNativeSTDIN[0] >= 0) + { + cp->PipeChildStd[0] = cp->PipeNativeSTDIN[0]; + + /* Set close-on-exec flag on the pipe's ends. The read end will + be dup2-ed into the stdin descriptor after the fork but before + the exec. */ + if((fcntl(cp->PipeNativeSTDIN[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(cp->PipeNativeSTDIN[1], F_SETFD, FD_CLOEXEC) < 0)) + { + kwsysProcessCleanup(cp, 1); + return; + } + } + else + { + cp->PipeChildStd[0] = -1; + } + + /* Create the output pipe for the last process. + We always create this so the pipe can be passed to select even if + it will report closed immediately. */ { /* Create the pipe. */ int p[2]; @@ -796,15 +847,14 @@ void kwsysProcess_Execute(kwsysProcess* cp) } /* Store the pipe. */ - cp->PipeReadEnds[KWSYSPE_PIPE_STDERR] = p[0]; - si.StdErr = p[1]; + cp->PipeReadEnds[KWSYSPE_PIPE_STDOUT] = p[0]; + cp->PipeChildStd[1] = p[1]; /* Set close-on-exec flag on the pipe's ends. */ if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupDescriptor(&si.StdErr); return; } @@ -813,41 +863,93 @@ void kwsysProcess_Execute(kwsysProcess* cp) if(!kwsysProcessSetNonBlocking(p[0])) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupDescriptor(&si.StdErr); return; } } - /* Replace the stderr pipe with a file if requested. In this case - the select call will report that stderr is closed immediately. */ - if(cp->PipeFileSTDERR) + if (cp->PipeFileSTDOUT) { - if(!kwsysProcessSetupOutputPipeFile(&si.StdErr, cp->PipeFileSTDERR)) + /* Use a file for stdout. */ + if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1], + cp->PipeFileSTDOUT)) + { + kwsysProcessCleanup(cp, 1); + return; + } + } + else if (cp->PipeSharedSTDOUT) + { + /* Use the parent stdout. */ + kwsysProcessCleanupDescriptor(&cp->PipeChildStd[1]); + cp->PipeChildStd[1] = 1; + } + else if (cp->PipeNativeSTDOUT[1] >= 0) + { + /* Use the given descriptor for stdout. */ + if(!kwsysProcessSetupOutputPipeNative(&cp->PipeChildStd[1], + cp->PipeNativeSTDOUT)) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupDescriptor(&si.StdErr); return; } } - /* Replace the stderr pipe with the parent's if requested. In this - case the select call will report that stderr is closed - immediately. */ - if(cp->PipeSharedSTDERR) + /* Create stderr pipe to be shared by all processes in the pipeline. + We always create this so the pipe can be passed to select even if + it will report closed immediately. */ + { + /* Create the pipe. */ + int p[2]; + if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) + { + kwsysProcessCleanup(cp, 1); + return; + } + + /* Store the pipe. */ + cp->PipeReadEnds[KWSYSPE_PIPE_STDERR] = p[0]; + cp->PipeChildStd[2] = p[1]; + + /* Set close-on-exec flag on the pipe's ends. */ + if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) { - kwsysProcessCleanupDescriptor(&si.StdErr); - si.StdErr = 2; + kwsysProcessCleanup(cp, 1); + return; } - /* Replace the stderr pipe with the native pipe provided if any. In - this case the select call will report that stderr is closed - immediately. */ - if(cp->PipeNativeSTDERR[1] >= 0) + /* Set to non-blocking in case select lies, or for the polling + implementation. */ + if(!kwsysProcessSetNonBlocking(p[0])) { - if(!kwsysProcessSetupOutputPipeNative(&si.StdErr, cp->PipeNativeSTDERR)) + kwsysProcessCleanup(cp, 1); + return; + } + } + + if (cp->PipeFileSTDERR) + { + /* Use a file for stderr. */ + if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2], + cp->PipeFileSTDERR)) + { + kwsysProcessCleanup(cp, 1); + return; + } + } + else if (cp->PipeSharedSTDERR) + { + /* Use the parent stderr. */ + kwsysProcessCleanupDescriptor(&cp->PipeChildStd[2]); + cp->PipeChildStd[2] = 2; + } + else if (cp->PipeNativeSTDERR[1] >= 0) + { + /* Use the given handle for stderr. */ + if(!kwsysProcessSetupOutputPipeNative(&cp->PipeChildStd[2], + cp->PipeNativeSTDERR)) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupDescriptor(&si.StdErr); return; } } @@ -859,54 +961,85 @@ void kwsysProcess_Execute(kwsysProcess* cp) /* Create the pipeline of processes. */ { - int readEnd = -1; - int failed = 0; + kwsysProcessCreateInformation si = {-1, -1, -1, {-1, -1}}; + int nextStdIn = cp->PipeChildStd[0]; for(i=0; i < cp->NumberOfCommands; ++i) { - if(!kwsysProcessCreate(cp, i, &si, &readEnd)) + /* Setup the process's pipes. */ + si.StdIn = nextStdIn; + if (i == cp->NumberOfCommands-1) { - failed = 1; + nextStdIn = -1; + si.StdOut = cp->PipeChildStd[1]; } - - /* Set the output pipe of the last process to be non-blocking in - case select lies, or for the polling implementation. */ - if(i == (cp->NumberOfCommands-1) && !kwsysProcessSetNonBlocking(readEnd)) - { - failed = 1; - } - - if(failed) + else { - kwsysProcessCleanup(cp, 1); - - /* Release resources that may have been allocated for this - process before an error occurred. */ - kwsysProcessCleanupDescriptor(&readEnd); - if(si.StdIn != 0) - { - kwsysProcessCleanupDescriptor(&si.StdIn); - } - if(si.StdOut != 1) + /* Create a pipe to sit between the children. */ + int p[2] = {-1,-1}; + if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) { - kwsysProcessCleanupDescriptor(&si.StdOut); + if (nextStdIn != cp->PipeChildStd[0]) + { + kwsysProcessCleanupDescriptor(&nextStdIn); + } + kwsysProcessCleanup(cp, 1); + return; } - if(si.StdErr != 2) + + /* Set close-on-exec flag on the pipe's ends. */ + if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) { - kwsysProcessCleanupDescriptor(&si.StdErr); + close(p[0]); + close(p[1]); + if (nextStdIn != cp->PipeChildStd[0]) + { + kwsysProcessCleanupDescriptor(&nextStdIn); + } + kwsysProcessCleanup(cp, 1); + return; } + nextStdIn = p[0]; + si.StdOut = p[1]; + } + si.StdErr = cp->MergeOutput? cp->PipeChildStd[1] : cp->PipeChildStd[2]; + + { + int res = kwsysProcessCreate(cp, i, &si); + + /* Close our copies of pipes used between children. */ + if (si.StdIn != cp->PipeChildStd[0]) + { + kwsysProcessCleanupDescriptor(&si.StdIn); + } + if (si.StdOut != cp->PipeChildStd[1]) + { + kwsysProcessCleanupDescriptor(&si.StdOut); + } + if (si.StdErr != cp->PipeChildStd[2] && !cp->MergeOutput) + { + kwsysProcessCleanupDescriptor(&si.StdErr); + } + + if(!res) + { kwsysProcessCleanupDescriptor(&si.ErrorPipe[0]); kwsysProcessCleanupDescriptor(&si.ErrorPipe[1]); + if (nextStdIn != cp->PipeChildStd[0]) + { + kwsysProcessCleanupDescriptor(&nextStdIn); + } + kwsysProcessCleanup(cp, 1); return; } } - /* Save a handle to the output pipe for the last process. */ - cp->PipeReadEnds[KWSYSPE_PIPE_STDOUT] = readEnd; + } } - /* The parent process does not need the output pipe write ends. */ - if(si.StdErr != 2) + /* The parent process does not need the child's pipe ends. */ + for (i=0; i < 3; ++i) { - kwsysProcessCleanupDescriptor(&si.StdErr); + kwsysProcessCleanupDescriptor(&cp->PipeChildStd[i]); } /* Restore the working directory. */ @@ -1414,6 +1547,10 @@ static int kwsysProcessInitialize(kwsysProcess* cp) { cp->PipeReadEnds[i] = -1; } + for(i=0; i < 3; ++i) + { + cp->PipeChildStd[i] = -1; + } cp->SignalPipe = -1; cp->SelectError = 0; cp->StartTime.tv_sec = -1; @@ -1548,13 +1685,17 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error) { kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); } + for(i=0; i < 3; ++i) + { + kwsysProcessCleanupDescriptor(&cp->PipeChildStd[i]); + } } /*--------------------------------------------------------------------------*/ /* Close the given file descriptor if it is open. Reset its value to -1. */ static void kwsysProcessCleanupDescriptor(int* pfd) { - if(pfd && *pfd >= 0) + if(pfd && *pfd > 2) { /* Keep trying to close until it is not interrupted by a * signal. */ @@ -1615,100 +1756,8 @@ int decc$set_child_standard_streams(int fd1, int fd2, int fd3); /*--------------------------------------------------------------------------*/ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, - kwsysProcessCreateInformation* si, int* readEnd) + kwsysProcessCreateInformation* si) { - /* Setup the process's stdin. */ - if(prIndex > 0) - { - si->StdIn = *readEnd; - *readEnd = 0; - } - else if(cp->PipeFileSTDIN) - { - /* Open a file for the child's stdin to read. */ - si->StdIn = open(cp->PipeFileSTDIN, O_RDONLY); - if(si->StdIn < 0) - { - return 0; - } - - /* Set close-on-exec flag on the pipe's end. */ - if(fcntl(si->StdIn, F_SETFD, FD_CLOEXEC) < 0) - { - return 0; - } - } - else if(cp->PipeSharedSTDIN) - { - si->StdIn = 0; - } - else if(cp->PipeNativeSTDIN[0] >= 0) - { - si->StdIn = cp->PipeNativeSTDIN[0]; - - /* Set close-on-exec flag on the pipe's ends. The read end will - be dup2-ed into the stdin descriptor after the fork but before - the exec. */ - if((fcntl(cp->PipeNativeSTDIN[0], F_SETFD, FD_CLOEXEC) < 0) || - (fcntl(cp->PipeNativeSTDIN[1], F_SETFD, FD_CLOEXEC) < 0)) - { - return 0; - } - } - else - { - si->StdIn = -1; - } - - /* Setup the process's stdout. */ - { - /* Create the pipe. */ - int p[2]; - if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) - { - return 0; - } - *readEnd = p[0]; - si->StdOut = p[1]; - - /* Set close-on-exec flag on the pipe's ends. */ - if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || - (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) - { - return 0; - } - } - - /* Replace the stdout pipe with a file if requested. In this case - the select call will report that stdout is closed immediately. */ - if(prIndex == cp->NumberOfCommands-1 && cp->PipeFileSTDOUT) - { - if(!kwsysProcessSetupOutputPipeFile(&si->StdOut, cp->PipeFileSTDOUT)) - { - return 0; - } - } - - /* Replace the stdout pipe with the parent's if requested. In this - case the select call will report that stderr is closed - immediately. */ - if(prIndex == cp->NumberOfCommands-1 && cp->PipeSharedSTDOUT) - { - kwsysProcessCleanupDescriptor(&si->StdOut); - si->StdOut = 1; - } - - /* Replace the stdout pipe with the native pipe provided if any. In - this case the select call will report that stdout is closed - immediately. */ - if(prIndex == cp->NumberOfCommands-1 && cp->PipeNativeSTDOUT[1] >= 0) - { - if(!kwsysProcessSetupOutputPipeNative(&si->StdOut, cp->PipeNativeSTDOUT)) - { - return 0; - } - } - /* Create the error reporting pipe. */ if(pipe(si->ErrorPipe) < 0) { @@ -1819,19 +1868,6 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, } } - /* Successfully created this child process. */ - if(prIndex > 0 || si->StdIn > 0) - { - /* The parent process does not need the input pipe read end. */ - kwsysProcessCleanupDescriptor(&si->StdIn); - } - - /* The parent process does not need the output pipe write ends. */ - if(si->StdOut != 1) - { - kwsysProcessCleanupDescriptor(&si->StdOut); - } - return 1; } diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c index c2965ea..f630171 100644 --- a/Source/kwsys/ProcessWin32.c +++ b/Source/kwsys/ProcessWin32.c @@ -94,6 +94,11 @@ typedef struct kwsysProcessCreateInformation_s { /* Windows child startup control data. */ STARTUPINFOW StartupInfo; + + /* Original handles before making inherited duplicates. */ + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; } kwsysProcessCreateInformation; @@ -107,15 +112,12 @@ static void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp, kwsysProcessPipeData* td); static int kwsysProcessInitialize(kwsysProcess* cp); static int kwsysProcessCreate(kwsysProcess* cp, int index, - kwsysProcessCreateInformation* si, - PHANDLE readEnd); + kwsysProcessCreateInformation* si); static void kwsysProcessDestroy(kwsysProcess* cp, int event); static int kwsysProcessSetupOutputPipeFile(PHANDLE handle, const char* name); -static int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle); -static int kwsysProcessSetupPipeNative(PHANDLE handle, HANDLE p[2], - int isWrite); +static void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle); +static void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle); static void kwsysProcessCleanupHandle(PHANDLE h); -static void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle); static void kwsysProcessCleanup(kwsysProcess* cp, int error); static void kwsysProcessCleanErrorMessage(kwsysProcess* cp); static int kwsysProcessComputeCommandLength(kwsysProcess* cp, @@ -224,6 +226,9 @@ struct kwsysProcess_s /* Whether to treat command lines as verbatim. */ int Verbatim; + /* Whether to merge stdout/stderr of the child. */ + int MergeOutput; + /* Mutex to protect the shared index used by threads to report data. */ HANDLE SharedIndexMutex; @@ -306,6 +311,10 @@ struct kwsysProcess_s /* Real working directory of our own process. */ DWORD RealWorkingDirectoryLength; wchar_t* RealWorkingDirectory; + + /* Own handles for the child's ends of the pipes in the parent process. + Used temporarily during process creation. */ + HANDLE PipeChildStd[3]; }; /*--------------------------------------------------------------------------*/ @@ -446,6 +455,10 @@ kwsysProcess* kwsysProcess_New(void) return 0; } } + for(i=0; i < 3; ++i) + { + cp->PipeChildStd[i] = INVALID_HANDLE_VALUE; + } return cp; } @@ -796,6 +809,7 @@ int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) { case kwsysProcess_Option_Detach: return cp->OptionDetach; case kwsysProcess_Option_HideWindow: return cp->HideWindow; + case kwsysProcess_Option_MergeOutput: return cp->MergeOutput; case kwsysProcess_Option_Verbatim: return cp->Verbatim; default: return 0; } @@ -813,6 +827,7 @@ void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) { case kwsysProcess_Option_Detach: cp->OptionDetach = value; break; case kwsysProcess_Option_HideWindow: cp->HideWindow = value; break; + case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break; case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break; default: break; } @@ -875,9 +890,6 @@ void kwsysProcess_Execute(kwsysProcess* cp) { int i; - /* Child startup control data. */ - kwsysProcessCreateInformation si; - /* Do not execute a second time. */ if(!cp || cp->State == kwsysProcess_State_Executing) { @@ -914,117 +926,211 @@ void kwsysProcess_Execute(kwsysProcess* cp) SetCurrentDirectoryW(cp->WorkingDirectory); } - /* Initialize startup info data. */ - ZeroMemory(&si, sizeof(si)); - si.StartupInfo.cb = sizeof(si.StartupInfo); - - /* Decide whether a child window should be shown. */ - si.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW; - si.StartupInfo.wShowWindow = - (unsigned short)(cp->HideWindow?SW_HIDE:SW_SHOWDEFAULT); - - /* Connect the child's output pipes to the threads. */ - si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; - /* Create stderr pipe to be shared by all processes in the pipeline. - Neither end is directly inherited. */ - if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read, - &cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0)) + /* Setup the stdin pipe for the first process. */ + if(cp->PipeFileSTDIN) { - kwsysProcessCleanup(cp, 1); - return; + /* Create a handle to read a file for stdin. */ + wchar_t* wstdin = kwsysEncoding_DupToWide(cp->PipeFileSTDIN); + cp->PipeChildStd[0] = + CreateFileW(wstdin, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + 0, OPEN_EXISTING, 0, 0); + free(wstdin); + if(cp->PipeChildStd[0] == INVALID_HANDLE_VALUE) + { + kwsysProcessCleanup(cp, 1); + return; + } + } + else if(cp->PipeSharedSTDIN) + { + /* Share this process's stdin with the child. */ + kwsysProcessSetupSharedPipe(STD_INPUT_HANDLE, &cp->PipeChildStd[0]); + } + else if(cp->PipeNativeSTDIN[0]) + { + /* Use the provided native pipe. */ + kwsysProcessSetupPipeNative(cp->PipeNativeSTDIN[0], &cp->PipeChildStd[0]); + } + else + { + /* Explicitly give the child no stdin. */ + cp->PipeChildStd[0] = INVALID_HANDLE_VALUE; } - /* Create an inherited duplicate of the write end, but do not - close the non-inherited version. We need to keep it open - to use in waking up the pipe threads. */ - if(!DuplicateHandle(GetCurrentProcess(), cp->Pipe[KWSYSPE_PIPE_STDERR].Write, - GetCurrentProcess(), &si.StartupInfo.hStdError, - 0, TRUE, DUPLICATE_SAME_ACCESS)) + /* Create the output pipe for the last process. + We always create this so the pipe thread can run even if we + do not end up giving the write end to the child below. */ + if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDOUT].Read, + &cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, 0, 0)) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); return; } - /* Replace the stderr pipe with a file if requested. In this case - the pipe thread will still run but never report data. */ - if(cp->PipeFileSTDERR) + if(cp->PipeFileSTDOUT) { - if(!kwsysProcessSetupOutputPipeFile(&si.StartupInfo.hStdError, - cp->PipeFileSTDERR)) + /* Use a file for stdout. */ + if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1], + cp->PipeFileSTDOUT)) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); return; } } - - /* Replace the stderr pipe with the parent process's if requested. - In this case the pipe thread will still run but never report - data. */ - if(cp->PipeSharedSTDERR) + else if(cp->PipeSharedSTDOUT) + { + /* Use the parent stdout. */ + kwsysProcessSetupSharedPipe(STD_OUTPUT_HANDLE, &cp->PipeChildStd[1]); + } + else if(cp->PipeNativeSTDOUT[1]) { - if(!kwsysProcessSetupSharedPipe(STD_ERROR_HANDLE, - &si.StartupInfo.hStdError)) + /* Use the given handle for stdout. */ + kwsysProcessSetupPipeNative(cp->PipeNativeSTDOUT[1], &cp->PipeChildStd[1]); + } + else + { + /* Use our pipe for stdout. Duplicate the handle since our waker + thread will use the original. Do not make it inherited yet. */ + if(!DuplicateHandle(GetCurrentProcess(), + cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, + GetCurrentProcess(), &cp->PipeChildStd[1], + 0, FALSE, DUPLICATE_SAME_ACCESS)) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, - STD_ERROR_HANDLE); return; } } - /* Replace the stderr pipe with the native pipe provided if any. In - this case the pipe thread will still run but never report - data. */ - if(cp->PipeNativeSTDERR[1]) + /* Create stderr pipe to be shared by all processes in the pipeline. + We always create this so the pipe thread can run even if we do not + end up giving the write end to the child below. */ + if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read, + &cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0)) + { + kwsysProcessCleanup(cp, 1); + return; + } + + if(cp->PipeFileSTDERR) + { + /* Use a file for stderr. */ + if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2], + cp->PipeFileSTDERR)) + { + kwsysProcessCleanup(cp, 1); + return; + } + } + else if(cp->PipeSharedSTDERR) + { + /* Use the parent stderr. */ + kwsysProcessSetupSharedPipe(STD_ERROR_HANDLE, &cp->PipeChildStd[2]); + } + else if(cp->PipeNativeSTDERR[1]) { - if(!kwsysProcessSetupPipeNative(&si.StartupInfo.hStdError, - cp->PipeNativeSTDERR, 1)) + /* Use the given handle for stderr. */ + kwsysProcessSetupPipeNative(cp->PipeNativeSTDERR[1], &cp->PipeChildStd[2]); + } + else + { + /* Use our pipe for stderr. Duplicate the handle since our waker + thread will use the original. Do not make it inherited yet. */ + if(!DuplicateHandle(GetCurrentProcess(), + cp->Pipe[KWSYSPE_PIPE_STDERR].Write, + GetCurrentProcess(), &cp->PipeChildStd[2], + 0, FALSE, DUPLICATE_SAME_ACCESS)) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, - STD_ERROR_HANDLE); return; } } /* Create the pipeline of processes. */ { - HANDLE readEnd = 0; + /* Child startup control data. */ + kwsysProcessCreateInformation si; + HANDLE nextStdInput = cp->PipeChildStd[0]; + + /* Initialize startup info data. */ + ZeroMemory(&si, sizeof(si)); + si.StartupInfo.cb = sizeof(si.StartupInfo); + + /* Decide whether a child window should be shown. */ + si.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW; + si.StartupInfo.wShowWindow = + (unsigned short)(cp->HideWindow?SW_HIDE:SW_SHOWDEFAULT); + + /* Connect the child's output pipes to the threads. */ + si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; + for(i=0; i < cp->NumberOfCommands; ++i) { - if(kwsysProcessCreate(cp, i, &si, &readEnd)) + /* Setup the process's pipes. */ + si.hStdInput = nextStdInput; + if (i == cp->NumberOfCommands-1) + { + /* The last child gets the overall stdout. */ + nextStdInput = INVALID_HANDLE_VALUE; + si.hStdOutput = cp->PipeChildStd[1]; + } + else + { + /* Create a pipe to sit between the children. */ + HANDLE p[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + if (!CreatePipe(&p[0], &p[1], 0, 0)) + { + if (nextStdInput != cp->PipeChildStd[0]) + { + kwsysProcessCleanupHandle(&nextStdInput); + } + kwsysProcessCleanup(cp, 1); + return; + } + nextStdInput = p[0]; + si.hStdOutput = p[1]; + } + si.hStdError = cp->MergeOutput? cp->PipeChildStd[1] : cp->PipeChildStd[2]; + + { + int res = kwsysProcessCreate(cp, i, &si); + + /* Close our copies of pipes used between children. */ + if (si.hStdInput != cp->PipeChildStd[0]) + { + kwsysProcessCleanupHandle(&si.hStdInput); + } + if (si.hStdOutput != cp->PipeChildStd[1]) + { + kwsysProcessCleanupHandle(&si.hStdOutput); + } + if (si.hStdError != cp->PipeChildStd[2] && !cp->MergeOutput) + { + kwsysProcessCleanupHandle(&si.hStdError); + } + if (res) { cp->ProcessEvents[i+1] = cp->ProcessInformation[i].hProcess; } else { + if (nextStdInput != cp->PipeChildStd[0]) + { + kwsysProcessCleanupHandle(&nextStdInput); + } kwsysProcessCleanup(cp, 1); - - /* Release resources that may have been allocated for this - process before an error occurred. */ - kwsysProcessCleanupHandle(&readEnd); - kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdInput, - STD_INPUT_HANDLE); - kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdOutput, - STD_OUTPUT_HANDLE); - kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, - STD_ERROR_HANDLE); return; } } - - /* Save a handle to the output pipe for the last process. */ - cp->Pipe[KWSYSPE_PIPE_STDOUT].Read = readEnd; + } } - /* Close the inherited handles to the stderr pipe shared by all - processes in the pipeline. The stdout and stdin pipes are not - shared among all children and are therefore closed by - kwsysProcessCreate after each child is created. */ - kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, STD_ERROR_HANDLE); + /* The parent process does not need the child's pipe ends. */ + for (i=0; i < 3; ++i) + { + kwsysProcessCleanupHandle(&cp->PipeChildStd[i]); + } /* Restore the working directory. */ if(cp->RealWorkingDirectory) @@ -1543,162 +1649,90 @@ int kwsysProcessInitialize(kwsysProcess* cp) } } } + { + int i; + for (i=0; i < 3; ++i) + { + cp->PipeChildStd[i] = INVALID_HANDLE_VALUE; + } + } return 1; } /*--------------------------------------------------------------------------*/ -int kwsysProcessCreate(kwsysProcess* cp, int index, - kwsysProcessCreateInformation* si, - PHANDLE readEnd) +static int kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn) { - /* Setup the process's stdin. */ - if(*readEnd) - { - /* Create an inherited duplicate of the read end from the output - pipe of the previous process. This also closes the - non-inherited version. */ - if(!DuplicateHandle(GetCurrentProcess(), *readEnd, - GetCurrentProcess(), readEnd, - 0, TRUE, (DUPLICATE_CLOSE_SOURCE | - DUPLICATE_SAME_ACCESS))) - { - return 0; - } - si->StartupInfo.hStdInput = *readEnd; + DWORD flags; - /* This function is done with this handle. */ - *readEnd = 0; - } - else if(cp->PipeFileSTDIN) - { - /* Create a handle to read a file for stdin. */ - wchar_t* wstdin = kwsysEncoding_DupToWide(cp->PipeFileSTDIN); - HANDLE fin = CreateFileW(wstdin, GENERIC_READ|GENERIC_WRITE, - FILE_SHARE_READ|FILE_SHARE_WRITE, - 0, OPEN_EXISTING, 0, 0); - free(wstdin); - if(fin == INVALID_HANDLE_VALUE) - { - return 0; - } - /* Create an inherited duplicate of the handle. This also closes - the non-inherited version. */ - if(!DuplicateHandle(GetCurrentProcess(), fin, - GetCurrentProcess(), &fin, - 0, TRUE, (DUPLICATE_CLOSE_SOURCE | - DUPLICATE_SAME_ACCESS))) - { - return 0; - } - si->StartupInfo.hStdInput = fin; - } - else if(cp->PipeSharedSTDIN) - { - /* Share this process's stdin with the child. */ - if(!kwsysProcessSetupSharedPipe(STD_INPUT_HANDLE, - &si->StartupInfo.hStdInput)) - { - return 0; - } - } - else if(cp->PipeNativeSTDIN[0]) + /* Check whether the handle is valid for this process. */ + if (in != INVALID_HANDLE_VALUE && GetHandleInformation(in, &flags)) { - /* Use the provided native pipe. */ - if(!kwsysProcessSetupPipeNative(&si->StartupInfo.hStdInput, - cp->PipeNativeSTDIN, 0)) + /* Use the handle as-is if it is already inherited. */ + if (flags & HANDLE_FLAG_INHERIT) { - return 0; + *out = in; + return 1; } + + /* Create an inherited copy of this handle. */ + return DuplicateHandle(GetCurrentProcess(), in, + GetCurrentProcess(), out, + 0, TRUE, DUPLICATE_SAME_ACCESS); } else { - /* Explicitly give the child no stdin. */ - si->StartupInfo.hStdInput = INVALID_HANDLE_VALUE; + /* The given handle is not valid for this process. Some child + processes may break if they do not have a valid standard handle, + so open NUL to give to the child. */ + SECURITY_ATTRIBUTES sa; + ZeroMemory(&sa, sizeof(sa)); + sa.nLength = (DWORD)sizeof(sa); + sa.bInheritHandle = 1; + *out = CreateFileW(L"NUL", + (isStdIn ? GENERIC_READ : + (GENERIC_WRITE | FILE_READ_ATTRIBUTES)), + FILE_SHARE_READ|FILE_SHARE_WRITE, + &sa, OPEN_EXISTING, 0, 0); + return *out != INVALID_HANDLE_VALUE; } - /* Setup the process's stdout. */ - { - DWORD maybeClose = DUPLICATE_CLOSE_SOURCE; - HANDLE writeEnd; +} - /* Create the output pipe for this process. Neither end is directly - inherited. */ - if(!CreatePipe(readEnd, &writeEnd, 0, 0)) - { - return 0; - } +/*--------------------------------------------------------------------------*/ +int kwsysProcessCreate(kwsysProcess* cp, int index, + kwsysProcessCreateInformation* si) +{ + int res = - /* Create an inherited duplicate of the write end. Close the - non-inherited version unless this is the last process. Save the - non-inherited write end of the last process. */ - if(index == cp->NumberOfCommands-1) - { - cp->Pipe[KWSYSPE_PIPE_STDOUT].Write = writeEnd; - maybeClose = 0; - } - if(!DuplicateHandle(GetCurrentProcess(), writeEnd, - GetCurrentProcess(), &writeEnd, - 0, TRUE, (maybeClose | DUPLICATE_SAME_ACCESS))) - { - return 0; - } - si->StartupInfo.hStdOutput = writeEnd; - } + /* Create inherited copies the handles. */ + kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput, + si->hStdInput, 1) && + kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput, + si->hStdOutput, 0) && + kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError, + si->hStdError, 0) && - /* Replace the stdout pipe with a file if requested. In this case - the pipe thread will still run but never report data. */ - if(index == cp->NumberOfCommands-1 && cp->PipeFileSTDOUT) - { - if(!kwsysProcessSetupOutputPipeFile(&si->StartupInfo.hStdOutput, - cp->PipeFileSTDOUT)) - { - return 0; - } - } + /* Create the child in a suspended state so we can wait until all + children have been created before running any one. */ + CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, CREATE_SUSPENDED, 0, + 0, &si->StartupInfo, &cp->ProcessInformation[index]); - /* Replace the stdout pipe of the last child with the parent - process's if requested. In this case the pipe thread will still - run but never report data. */ - if(index == cp->NumberOfCommands-1 && cp->PipeSharedSTDOUT) + /* Close the inherited copies of the handles. */ + if (si->StartupInfo.hStdInput != si->hStdInput) { - if(!kwsysProcessSetupSharedPipe(STD_OUTPUT_HANDLE, - &si->StartupInfo.hStdOutput)) - { - return 0; - } + kwsysProcessCleanupHandle(&si->StartupInfo.hStdInput); } - - /* Replace the stdout pipe with the native pipe provided if any. In - this case the pipe thread will still run but never report - data. */ - if(index == cp->NumberOfCommands-1 && cp->PipeNativeSTDOUT[1]) + if (si->StartupInfo.hStdOutput != si->hStdOutput) { - if(!kwsysProcessSetupPipeNative(&si->StartupInfo.hStdOutput, - cp->PipeNativeSTDOUT, 1)) - { - return 0; - } + kwsysProcessCleanupHandle(&si->StartupInfo.hStdOutput); } - - /* Create the child in a suspended state so we can wait until all - children have been created before running any one. */ - if(!CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, CREATE_SUSPENDED, 0, - 0, &si->StartupInfo, &cp->ProcessInformation[index])) + if (si->StartupInfo.hStdError != si->hStdError) { - return 0; + kwsysProcessCleanupHandle(&si->StartupInfo.hStdError); } - /* Successfully created this child process. Close the current - process's copies of the inherited stdout and stdin handles. The - stderr handle is shared among all children and is closed by - kwsysProcess_Execute after all children have been created. */ - kwsysProcessCleanupHandleSafe(&si->StartupInfo.hStdInput, - STD_INPUT_HANDLE); - kwsysProcessCleanupHandleSafe(&si->StartupInfo.hStdOutput, - STD_OUTPUT_HANDLE); - - return 1; + return res; } /*--------------------------------------------------------------------------*/ @@ -1763,7 +1797,7 @@ int kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name) return 1; } - /* Close the existing inherited handle. */ + /* Close the existing handle. */ kwsysProcessCleanupHandle(phandle); /* Create a handle to write a file for the pipe. */ @@ -1776,103 +1810,27 @@ int kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name) return 0; } - /* Create an inherited duplicate of the handle. This also closes - the non-inherited version. */ - if(!DuplicateHandle(GetCurrentProcess(), fout, - GetCurrentProcess(), &fout, - 0, TRUE, (DUPLICATE_CLOSE_SOURCE | - DUPLICATE_SAME_ACCESS))) - { - return 0; - } - /* Assign the replacement handle. */ *phandle = fout; return 1; } /*--------------------------------------------------------------------------*/ -int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle) +void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle) { - /* Check whether the handle to be shared is already inherited. */ - DWORD flags; - int inherited = 0; - if(GetHandleInformation(GetStdHandle(nStdHandle), &flags) && - (flags & HANDLE_FLAG_INHERIT)) - { - inherited = 1; - } - - /* Cleanup the previous handle. */ + /* Close the existing handle. */ kwsysProcessCleanupHandle(handle); - - /* If the standard handle is not inherited then duplicate it to - create an inherited copy. Do not close the original handle when - duplicating! */ - if(inherited) - { - *handle = GetStdHandle(nStdHandle); - return 1; - } - else if(DuplicateHandle(GetCurrentProcess(), GetStdHandle(nStdHandle), - GetCurrentProcess(), handle, - 0, TRUE, DUPLICATE_SAME_ACCESS)) - { - return 1; - } - else - { - /* The given standard handle is not valid for this process. Some - child processes may break if they do not have a valid standard - pipe, so give the child an empty pipe. For the stdin pipe we - want to close the write end and give the read end to the child. - For stdout and stderr we want to close the read end and give - the write end to the child. */ - int child_end = (nStdHandle == STD_INPUT_HANDLE)? 0:1; - int parent_end = (nStdHandle == STD_INPUT_HANDLE)? 1:0; - HANDLE emptyPipe[2]; - if(!CreatePipe(&emptyPipe[0], &emptyPipe[1], 0, 0)) - { - return 0; - } - - /* Close the non-inherited end so the pipe will be broken - immediately. */ - CloseHandle(emptyPipe[parent_end]); - - /* Create an inherited duplicate of the handle. This also - closes the non-inherited version. */ - if(!DuplicateHandle(GetCurrentProcess(), emptyPipe[child_end], - GetCurrentProcess(), &emptyPipe[child_end], - 0, TRUE, (DUPLICATE_CLOSE_SOURCE | - DUPLICATE_SAME_ACCESS))) - { - return 0; - } - - /* Give the inherited handle to the child. */ - *handle = emptyPipe[child_end]; - return 1; - } + /* Store the new standard handle. */ + *handle = GetStdHandle(nStdHandle); } /*--------------------------------------------------------------------------*/ -int kwsysProcessSetupPipeNative(PHANDLE handle, HANDLE p[2], int isWrite) +void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle) { - /* Close the existing inherited handle. */ + /* Close the existing handle. */ kwsysProcessCleanupHandle(handle); - - /* Create an inherited duplicate of the handle. This also closes - the non-inherited version. */ - if(!DuplicateHandle(GetCurrentProcess(), p[isWrite? 1:0], - GetCurrentProcess(), handle, - 0, TRUE, (DUPLICATE_CLOSE_SOURCE | - DUPLICATE_SAME_ACCESS))) - { - return 0; - } - - return 1; + /* Store the new given handle. */ + *handle = native; } /*--------------------------------------------------------------------------*/ @@ -1880,23 +1838,13 @@ int kwsysProcessSetupPipeNative(PHANDLE handle, HANDLE p[2], int isWrite) /* Close the given handle if it is open. Reset its value to 0. */ void kwsysProcessCleanupHandle(PHANDLE h) { - if(h && *h) - { - CloseHandle(*h); - *h = 0; - } -} - -/*--------------------------------------------------------------------------*/ - -/* Close the given handle if it is open and not a standard handle. - Reset its value to 0. */ -void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle) -{ - if(h && *h && (*h != GetStdHandle(nStdHandle))) + if(h && *h && *h != INVALID_HANDLE_VALUE && + *h != GetStdHandle(STD_INPUT_HANDLE) && + *h != GetStdHandle(STD_OUTPUT_HANDLE) && + *h != GetStdHandle(STD_ERROR_HANDLE)) { CloseHandle(*h); - *h = 0; + *h = INVALID_HANDLE_VALUE; } } @@ -1986,6 +1934,10 @@ void kwsysProcessCleanup(kwsysProcess* cp, int error) kwsysProcessCleanupHandle(&cp->Pipe[i].Read); cp->Pipe[i].Closed = 0; } + for(i=0; i < 3; ++i) + { + kwsysProcessCleanupHandle(&cp->PipeChildStd[i]); + } } /*--------------------------------------------------------------------------*/ diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 07545ae..58feefd 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -46,7 +46,9 @@ configure_file(${CMake_SOURCE_DIR}/Tests/EnforceConfig.cmake.in # Testing if(BUILD_TESTING) set(CMake_TEST_DEVENV "") - if(CMAKE_GENERATOR MATCHES "Visual Studio [7-9] " AND + if(CMAKE_VS_DEVENV_COMMAND) + set(CMake_TEST_DEVENV "${CMAKE_VS_DEVENV_COMMAND}") + elseif(CMAKE_GENERATOR MATCHES "Visual Studio [7-9] " AND NOT CMAKE_MAKE_PROGRAM MATCHES "[mM][sS][bB][uU][iI][lL][dD]\\.[eE][xX][eE]") set(CMake_TEST_DEVENV "${CMAKE_MAKE_PROGRAM}") endif() @@ -1010,7 +1012,8 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release set(DEB_TEST_NAMES "CPackComponentsDEB") set(DEB_CONFIGURATIONS_TO_TEST "components-lintian-dpkgdeb-checks" "components-description1" - "components-description2") + "components-description2" + "components-shlibdeps1") set(CPackGen "DEB") set(CPackRun_CPackGen "-DCPackGen=${CPackGen}") @@ -1868,6 +1871,7 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}" --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}" --build-project VSExcludeFromDefaultBuild + --build-target install --test-command ${CMAKE_COMMAND} -D "activeConfig=${config}" -D "allConfigs=${CMAKE_CONFIGURATION_TYPES}" diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-shlibdeps1.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-shlibdeps1.cmake.in new file mode 100644 index 0000000..cfe6df5 --- /dev/null +++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-shlibdeps1.cmake.in @@ -0,0 +1,24 @@ +# +# Activate component packaging +# + +if(CPACK_GENERATOR MATCHES "DEB") + set(CPACK_DEB_COMPONENT_INSTALL "ON") +endif() + +# +# Choose grouping way +# +#set(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE) +#set(CPACK_COMPONENTS_GROUPING) +set(CPACK_COMPONENTS_IGNORE_GROUPS 1) +#set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1) + +# we set shlibdeps to on +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +# except for the component "headers" that do not contain any binary. +# the packaging will just fail if this does not work +set(CPACK_DEBIAN_HEADERS_PACKAGE_SHLIBDEPS OFF) + +# Also libraries contains only a static library. +set(CPACK_DEBIAN_LIBRARIES_PACKAGE_SHLIBDEPS OFF) diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake new file mode 100644 index 0000000..79d8f0d --- /dev/null +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake @@ -0,0 +1,75 @@ +if(NOT CPackComponentsDEB_SOURCE_DIR) + message(FATAL_ERROR "CPackComponentsDEB_SOURCE_DIR not set") +endif() + +include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) + + + +# requirements + +# debian now produces lower case names +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/MyLib-*.deb") +set(expected_count 3) + + +set(actual_output) +run_cpack(actual_output + CPack_output + CPack_error + EXPECTED_FILE_MASK "${expected_file_mask}" + CONFIG_ARGS ${config_args} + CONFIG_VERBOSE ${config_verbose}) + +message(STATUS "expected_count='${expected_count}'") +message(STATUS "expected_file_mask='${expected_file_mask}'") +message(STATUS "actual_output_files='${actual_output}'") + +if(NOT actual_output) + message(FATAL_ERROR "error: expected_files do not exist: CPackComponentsDEB test fails. (CPack_output=${CPack_output}, CPack_error=${CPack_error}") +endif() + +list(LENGTH actual_output actual_count) +message(STATUS "actual_count='${actual_count}'") +if(NOT actual_count EQUAL expected_count) + message(FATAL_ERROR "error: expected_count=${expected_count} does not match actual_count=${actual_count}: CPackComponents test fails. (CPack_output=${CPack_output}, CPack_error=${CPack_error})") +endif() + + +# dpkg-deb checks for the summary of the packages +find_program(DPKGDEB_EXECUTABLE dpkg-deb) +if(DPKGDEB_EXECUTABLE) + set(dpkgdeb_output_errors_all) + foreach(_f IN LISTS actual_output) + + # extracts the metadata from the package + run_dpkgdeb(dpkg_output + FILENAME ${_f} + ) + + dpkgdeb_return_specific_metaentry(dpkg_package_name + DPKGDEB_OUTPUT "${dpkg_output}" + METAENTRY "Package:") + + message(STATUS "package='${dpkg_package_name}'") + + if("${dpkg_package_name}" STREQUAL "mylib-applications") + # pass + elseif("${dpkg_package_name}" STREQUAL "mylib-headers") + # pass + elseif("${dpkg_package_name}" STREQUAL "mylib-libraries") + # pass + else() + set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} + "dpkg-deb: ${_f}: component name not found: ${dpkg_package_name}\n") + endif() + + endforeach() + + + if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "") + message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}") + endif() +else() + message("dpkg-deb executable not found - skipping dpkg-deb test") +endif() diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index d5f1d22..16fb056 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -132,6 +132,7 @@ add_RunCMake_test(add_custom_command) add_RunCMake_test(add_custom_target) add_RunCMake_test(add_dependencies) add_RunCMake_test(build_command) +add_RunCMake_test(execute_process) add_RunCMake_test(export) add_RunCMake_test(cmake_minimum_required) add_RunCMake_test(continue) diff --git a/Tests/RunCMake/execute_process/MergeOutput-stdout.txt b/Tests/RunCMake/execute_process/MergeOutput-stdout.txt new file mode 100644 index 0000000..676f0ed --- /dev/null +++ b/Tests/RunCMake/execute_process/MergeOutput-stdout.txt @@ -0,0 +1,10 @@ +^-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr$ diff --git a/Tests/RunCMake/execute_process/MergeOutput.cmake b/Tests/RunCMake/execute_process/MergeOutput.cmake new file mode 100644 index 0000000..528ac90 --- /dev/null +++ b/Tests/RunCMake/execute_process/MergeOutput.cmake @@ -0,0 +1,4 @@ +foreach(i RANGE 1 5) + message(STATUS "Output on stdout") + message("Output on stderr") +endforeach() diff --git a/Tests/RunCMake/execute_process/MergeOutputFile-stderr.txt b/Tests/RunCMake/execute_process/MergeOutputFile-stderr.txt new file mode 100644 index 0000000..676f0ed --- /dev/null +++ b/Tests/RunCMake/execute_process/MergeOutputFile-stderr.txt @@ -0,0 +1,10 @@ +^-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr$ diff --git a/Tests/RunCMake/execute_process/MergeOutputFile.cmake b/Tests/RunCMake/execute_process/MergeOutputFile.cmake new file mode 100644 index 0000000..1a0d90e --- /dev/null +++ b/Tests/RunCMake/execute_process/MergeOutputFile.cmake @@ -0,0 +1,7 @@ +execute_process( + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/MergeOutput.cmake + OUTPUT_FILE out.txt + ERROR_FILE out.txt + ) +file(READ out.txt out) +message("${out}") diff --git a/Tests/RunCMake/execute_process/MergeOutputVars-stderr.txt b/Tests/RunCMake/execute_process/MergeOutputVars-stderr.txt new file mode 100644 index 0000000..676f0ed --- /dev/null +++ b/Tests/RunCMake/execute_process/MergeOutputVars-stderr.txt @@ -0,0 +1,10 @@ +^-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr +-- Output on stdout +Output on stderr$ diff --git a/Tests/RunCMake/execute_process/MergeOutputVars.cmake b/Tests/RunCMake/execute_process/MergeOutputVars.cmake new file mode 100644 index 0000000..3e7c69e --- /dev/null +++ b/Tests/RunCMake/execute_process/MergeOutputVars.cmake @@ -0,0 +1,6 @@ +execute_process( + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/MergeOutput.cmake + OUTPUT_VARIABLE out + ERROR_VARIABLE out + ) +message("${out}") diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake new file mode 100644 index 0000000..2080437 --- /dev/null +++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake @@ -0,0 +1,8 @@ +include(RunCMake) + +set(RunCMake_TEST_OUTPUT_MERGE 1) +run_cmake_command(MergeOutput ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/MergeOutput.cmake) +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) diff --git a/Tests/VSExcludeFromDefaultBuild/CMakeLists.txt b/Tests/VSExcludeFromDefaultBuild/CMakeLists.txt index d30414b..243fdf5 100644 --- a/Tests/VSExcludeFromDefaultBuild/CMakeLists.txt +++ b/Tests/VSExcludeFromDefaultBuild/CMakeLists.txt @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 2.8.9) project(VSExcludeFromDefaultBuild) +# CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD will enable the INSTALL target to be part of the default build +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) + # First step is to clear all .exe files in output so that possible past # failures of this test do not prevent it from succeeding. add_custom_target(ClearExes ALL @@ -13,6 +16,7 @@ add_custom_target(ClearExes ALL function(add_test_executable target) add_executable("${target}" ${ARGN}) add_dependencies("${target}" ClearExes) + install(TARGETS "${target}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/install" OPTIONAL) endfunction() add_test_executable(DefaultBuilt main.c) diff --git a/Tests/VSExcludeFromDefaultBuild/ClearExes.cmake b/Tests/VSExcludeFromDefaultBuild/ClearExes.cmake index ece30ad..99cf1a5 100644 --- a/Tests/VSExcludeFromDefaultBuild/ClearExes.cmake +++ b/Tests/VSExcludeFromDefaultBuild/ClearExes.cmake @@ -2,3 +2,7 @@ file(GLOB exeFiles "${dir}/*.exe") foreach(exeFile IN LISTS exeFiles) file(REMOVE "${exeFile}") endforeach() +file(GLOB exeFiles "${dir}/install/*.exe") +foreach(exeFile IN LISTS exeFiles) + file(REMOVE "${exeFile}") +endforeach() diff --git a/Tests/VSExcludeFromDefaultBuild/ResultTest.cmake b/Tests/VSExcludeFromDefaultBuild/ResultTest.cmake index 8fb00bf..f96e70b 100644 --- a/Tests/VSExcludeFromDefaultBuild/ResultTest.cmake +++ b/Tests/VSExcludeFromDefaultBuild/ResultTest.cmake @@ -7,6 +7,12 @@ macro(TestExists exeName) else() message(FATAL_ERROR "File ${exeFile} was expected ${ARGN} to exist!") endif() + set(exeFile "${dir}/${activeConfig}/install/${exeName}.exe") + if(${ARGN} EXISTS "${exeFile}") + message(STATUS "File ${exeFile} was correctly found ${ARGN} to exist.") + else() + message(FATAL_ERROR "File ${exeFile} was expected ${ARGN} to exist!") + endif() endmacro() TestExists(DefaultBuilt) |