New upstream version 3.28.2

ci/unstable
Timo Röhling 11 months ago
parent a9633ce77d
commit da192a20c6

@ -5,11 +5,11 @@ Get a global property of the CMake instance.
.. code-block:: cmake .. code-block:: cmake
get_cmake_property(<var> <property>) get_cmake_property(<variable> <property>)
Gets a global property from the CMake instance. The value of Gets a global property from the CMake instance. The value of
the ``<property>`` is stored in the variable ``<var>``. the ``<property>`` is stored in the specified ``<variable>``.
If the property is not found, ``<var>`` will be set to ``NOTFOUND``. If the property is not found, ``<variable>`` will be set to ``NOTFOUND``.
See the :manual:`cmake-properties(7)` manual for available properties. See the :manual:`cmake-properties(7)` manual for available properties.
In addition to global properties, this command (for historical reasons) In addition to global properties, this command (for historical reasons)

@ -9,14 +9,12 @@ Get a property for a source file.
[DIRECTORY <dir> | TARGET_DIRECTORY <target>] [DIRECTORY <dir> | TARGET_DIRECTORY <target>]
<property>) <property>)
Gets a property from a source file. The value of the property is Gets a property from a source file. The value of the property is stored in
stored in the specified ``<variable>``. If the source property is not found, the specified ``<variable>``. If the ``<file>`` is not a source file, or the
the behavior depends on whether it has been defined to be an ``INHERITED`` source property is not found, ``<variable>`` will be set to ``NOTFOUND``.
property or not (see :command:`define_property`). Non-inherited properties If the source property was defined to be an ``INHERITED`` property (see
will set ``variable`` to ``NOTFOUND``, whereas inherited properties will search :command:`define_property`), the search will include the relevant parent
the relevant parent scope as described for the :command:`define_property` scopes, as described for the :command:`define_property` command.
command and if still unable to find the property, ``variable`` will be set to
an empty string.
By default, the source file's property will be read from the current source By default, the source file's property will be read from the current source
directory's scope. directory's scope.

@ -5,16 +5,14 @@ Get a property from a target.
.. code-block:: cmake .. code-block:: cmake
get_target_property(<VAR> target property) get_target_property(<variable> <target> <property>)
Get a property from a target. The value of the property is stored in Get a property from a target. The value of the property is stored in the
the variable ``<VAR>``. If the target property is not found, the behavior specified ``<variable>``. If the target property is not found, ``<variable>``
depends on whether it has been defined to be an ``INHERITED`` property will be set to ``<variable>-NOTFOUND``. If the target property was defined to
or not (see :command:`define_property`). Non-inherited properties will be an ``INHERITED`` property (see :command:`define_property`), the search will
set ``<VAR>`` to ``<VAR>-NOTFOUND``, whereas inherited properties will search include the relevant parent scopes, as described for the
the relevant parent scope as described for the :command:`define_property` :command:`define_property` command.
command and if still unable to find the property, ``<VAR>`` will be set to
an empty string.
Use :command:`set_target_properties` to set target property values. Use :command:`set_target_properties` to set target property values.
Properties are usually used to control how a target is built, but some Properties are usually used to control how a target is built, but some

@ -5,16 +5,14 @@ Get a property of the test.
.. code-block:: cmake .. code-block:: cmake
get_test_property(test property [DIRECTORY <dir>] VAR) get_test_property(<test> <property> [DIRECTORY <dir>] <variable>)
Get a property from the test. The value of the property is stored in Get a property from the test. The value of the property is stored in
the variable ``VAR``. If the test property is not found, the behavior the specified ``<variable>``. If the ``<test>`` is not defined, or the
depends on whether it has been defined to be an ``INHERITED`` property test property is not found, ``<variable>`` will be set to ``NOTFOUND``.
or not (see :command:`define_property`). Non-inherited properties will If the test property was defined to be an ``INHERITED`` property (see
set ``VAR`` to "NOTFOUND", whereas inherited properties will search the :command:`define_property`), the search will include the relevant parent
relevant parent scope as described for the :command:`define_property` scopes, as described for the :command:`define_property` command.
command and if still unable to find the property, ``VAR`` will be set to
an empty string.
For a list of standard properties you can type For a list of standard properties you can type
:option:`cmake --help-property-list`. :option:`cmake --help-property-list`.

@ -5,9 +5,10 @@ Set properties of the current directory and subdirectories.
.. code-block:: cmake .. code-block:: cmake
set_directory_properties(PROPERTIES prop1 value1 [prop2 value2] ...) set_directory_properties(PROPERTIES <prop1> <value1> [<prop2> <value2>] ...)
Sets properties of the current directory and its subdirectories in key-value pairs. Sets properties of the current directory and its subdirectories in key-value
pairs.
See also the :command:`set_property(DIRECTORY)` command. See also the :command:`set_property(DIRECTORY)` command.

@ -5,9 +5,9 @@ Targets can have properties that affect how they are built.
.. code-block:: cmake .. code-block:: cmake
set_target_properties(target1 target2 ... set_target_properties(<targets> ...
PROPERTIES prop1 value1 PROPERTIES <prop1> <value1>
prop2 value2 ...) [<prop2> <value2>] ...)
Sets properties on targets. The syntax for the command is to list all Sets properties on targets. The syntax for the command is to list all
the targets you want to change, and then provide the values you want to the targets you want to change, and then provide the values you want to

@ -5,7 +5,10 @@ Set a property of the tests.
.. code-block:: cmake .. code-block:: cmake
set_tests_properties(test1 [test2...] PROPERTIES prop1 value1 prop2 value2) set_tests_properties(<tests>...
[DIRECTORY <dir>]
PROPERTIES <prop1> <value1>
[<prop2> <value2>]...)
Sets a property for the tests. If the test is not found, CMake Sets a property for the tests. If the test is not found, CMake
will report an error. will report an error.

@ -22,7 +22,7 @@ Synopsis
string(`JOIN`_ <glue> <out-var> [<input>...]) string(`JOIN`_ <glue> <out-var> [<input>...])
string(`TOLOWER`_ <string> <out-var>) string(`TOLOWER`_ <string> <out-var>)
string(`TOUPPER`_ <string> <out-var>) string(`TOUPPER`_ <string> <out-var>)
string(`LENGTH`_ <string> <out-var>) string(`LENGTH <LENGTH_>`_ <string> <out-var>)
string(`SUBSTRING`_ <string> <begin> <length> <out-var>) string(`SUBSTRING`_ <string> <begin> <length> <out-var>)
string(`STRIP`_ <string> <out-var>) string(`STRIP`_ <string> <out-var>)
string(`GENEX_STRIP`_ <string> <out-var>) string(`GENEX_STRIP`_ <string> <out-var>)

@ -30,6 +30,10 @@ following queries. The first query that provides a yes/no answer is used.
- Otherwise, the source file will be scanned if the compiler and generator - Otherwise, the source file will be scanned if the compiler and generator
support scanning. See policy :policy:`CMP0155`. support scanning. See policy :policy:`CMP0155`.
Note that any scanned source will be excluded from any unity build (see
:prop_tgt:`UNITY_BUILD`) because module-related statements can only happen at
one place within a C++ translation unit.
Compiler Support Compiler Support
================ ================

@ -224,7 +224,7 @@ For example::
They interpret the opening bracket as the start of an They interpret the opening bracket as the start of an
`Unquoted Argument`_. `Unquoted Argument`_.
.. _`Lua`: http://www.lua.org/ .. _`Lua`: https://www.lua.org/
.. _`Quoted Argument`: .. _`Quoted Argument`:

@ -583,7 +583,7 @@ generator is recommended. The :generator:`Unix Makefiles` or
:generator:`Ninja` generators can also be used, but they require the :generator:`Ninja` generators can also be used, but they require the
project to handle more areas like target CPU selection and code signing. project to handle more areas like target CPU selection and code signing.
Any of the three systems can be targeted by setting the Any of the Apple device platforms can be targeted by setting the
:variable:`CMAKE_SYSTEM_NAME` variable to a value from the table below. :variable:`CMAKE_SYSTEM_NAME` variable to a value from the table below.
By default, the latest Device SDK is chosen. As for all Apple platforms, By default, the latest Device SDK is chosen. As for all Apple platforms,
a different SDK (e.g. a simulator) can be selected by setting the a different SDK (e.g. a simulator) can be selected by setting the
@ -611,9 +611,11 @@ Variable :variable:`CMAKE_OSX_ARCHITECTURES` can be used to set architectures
for both device and simulator. Variable :variable:`CMAKE_OSX_DEPLOYMENT_TARGET` for both device and simulator. Variable :variable:`CMAKE_OSX_DEPLOYMENT_TARGET`
can be used to set an iOS/tvOS/visionOS/watchOS deployment target. can be used to set an iOS/tvOS/visionOS/watchOS deployment target.
Next configuration will install fat 5 architectures iOS library The next example installs five architectures in a universal binary for an iOS
and add the ``-miphoneos-version-min=9.3``/``-mios-simulator-version-min=9.3`` library. It adds the relevant ``-miphoneos-version-min=9.3`` or
flags to the compiler: ``-mios-simulator-version-min=9.3`` compiler flag where appropriate.
Note that the :variable:`CMAKE_IOS_INSTALL_COMBINED` variable used in the
example is now deprecated, so this approach is no longer recommended.
.. code-block:: console .. code-block:: console

@ -1592,17 +1592,20 @@ that running several of these tests at once does not exhaust the GPU's memory
pool. pool.
Please note that CTest has no concept of what a GPU is or how much memory it Please note that CTest has no concept of what a GPU is or how much memory it
has, nor does it have any way of communicating with a GPU to retrieve this has. It does not have any way of communicating with a GPU to retrieve this
information or perform any memory management. CTest simply keeps track of a information or perform any memory management, although the project can define
list of abstract resource types, each of which has a certain number of slots a test that provides details about the test machine (see
available for tests to use. Each test specifies the number of slots that it :ref:`ctest-resource-dynamically-generated-spec-file`).
requires from a certain resource, and CTest then schedules them in a way that
prevents the total number of slots in use from exceeding the listed capacity. CTest keeps track of a list of abstract resource types, each of which has a
When a test is executed, and slots from a resource are allocated to that test, certain number of slots available for tests to use. Each test specifies the
tests may assume that they have exclusive use of those slots for the duration number of slots that it requires from a certain resource, and CTest then
of the test's process. schedules them in a way that prevents the total number of slots in use from
exceeding the listed capacity. When a test is executed, and slots from a
The CTest resource allocation feature consists of two inputs: resource are allocated to that test, tests may assume that they have exclusive
use of those slots for the duration of the test's process.
The CTest resource allocation feature consists of at least two inputs:
* The :ref:`resource specification file <ctest-resource-specification-file>`, * The :ref:`resource specification file <ctest-resource-specification-file>`,
described below, which describes the resources available on the system. described below, which describes the resources available on the system.
@ -1643,15 +1646,20 @@ properties to indicate a skipped test.
Resource Specification File Resource Specification File
--------------------------- ---------------------------
The resource specification file is a JSON file which is passed to CTest, either The resource specification file is a JSON file which is passed to CTest in one
on the command line as :option:`ctest --resource-spec-file`, or as the of a number of ways. It can be specified on the command line with the
``RESOURCE_SPEC_FILE`` argument of :command:`ctest_test`. If a dashboard script :option:`ctest --resource-spec-file` option, it can be given using the
is used and ``RESOURCE_SPEC_FILE`` is not specified, the value of ``RESOURCE_SPEC_FILE`` argument of :command:`ctest_test`, or it can be
:variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script is used instead. generated dynamically as part of test execution (see
If :option:`--resource-spec-file <ctest --resource-spec-file>`, ``RESOURCE_SPEC_FILE``, :ref:`ctest-resource-dynamically-generated-spec-file`).
and :variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script are not specified,
the value of :variable:`CTEST_RESOURCE_SPEC_FILE` in the CMake build is used If a dashboard script is used and ``RESOURCE_SPEC_FILE`` is not specified, the
instead. If none of these are specified, no resource spec file is used. value of :variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script is used
instead. If :option:`--resource-spec-file <ctest --resource-spec-file>`,
``RESOURCE_SPEC_FILE``, and :variable:`CTEST_RESOURCE_SPEC_FILE` in the
dashboard script are not specified, the value of
:variable:`CTEST_RESOURCE_SPEC_FILE` in the CMake build is used instead.
If none of these are specified, no resource spec file is used.
The resource specification file must be a JSON object. All examples in this The resource specification file must be a JSON object. All examples in this
document assume the following resource specification file: document assume the following resource specification file:

@ -11,3 +11,7 @@ in the same way as it would with unity builds disabled.
This property helps with "ODR (One definition rule)" problems where combining This property helps with "ODR (One definition rule)" problems where combining
a particular source file with others might lead to build errors or other a particular source file with others might lead to build errors or other
unintended side effects. unintended side effects.
Note that sources which are scanned for C++ modules (see
:manual:`cmake-cxxmodules(7)`) are not eligible for unity build inclusion and
will automatically be excluded.

@ -64,6 +64,11 @@ a number of measures to help address such problems:
:prop_sf:`INCLUDE_DIRECTORIES` source property will not be combined :prop_sf:`INCLUDE_DIRECTORIES` source property will not be combined
into a unity source. into a unity source.
* Any source file which is scanned for C++ module sources via
:prop_tgt:`CXX_SCAN_FOR_MODULES`, :prop_sf:`CXX_SCAN_FOR_MODULES`, or
membership of a ``CXX_MODULES`` file set will not be combined into a unity
source. See :manual:`cmake-cxxmodules(7)` for details.
* Projects can prevent an individual source file from being combined into * Projects can prevent an individual source file from being combined into
a unity source by setting its :prop_sf:`SKIP_UNITY_BUILD_INCLUSION` a unity source by setting its :prop_sf:`SKIP_UNITY_BUILD_INCLUSION`
source property to true. This can be a more effective way to prevent source property to true. This can be a more effective way to prevent

@ -116,12 +116,6 @@ Modules
Additionally, the :command:`ExternalProject_Add_Step` command Additionally, the :command:`ExternalProject_Add_Step` command
has been updated to support the new ``JOB_SERVER_AWARE`` option. has been updated to support the new ``JOB_SERVER_AWARE`` option.
* The :module:`ExternalProject` module now declares ``BYPRODUCTS`` for the
downloaded file for generated ``download`` steps. Previously, if multiple
external projects downloaded to the same file, hash verification could fail.
Now, when using the :ref:`Ninja Generators`, this scenario is detected and
Ninja will raise an error stating that multiple rules generate the same file.
* The :module:`FetchContent` module's :command:`FetchContent_Declare` command * The :module:`FetchContent` module's :command:`FetchContent_Declare` command
gained an ``EXCLUDE_FROM_ALL`` option, which propagates through to the gained an ``EXCLUDE_FROM_ALL`` option, which propagates through to the
:command:`add_subdirectory` call made by :command:`add_subdirectory` call made by
@ -207,9 +201,9 @@ Updates
Changes made since CMake 3.28.0 include the following. Changes made since CMake 3.28.0 include the following.
3.28.1 3.28.1, 3.28.2
------ --------------
* This version made no changes to documented features or interfaces. * These versions made no changes to documented features or interfaces.
Some implementation updates were made to support ecosystem changes Some implementation updates were made to support ecosystem changes
and/or fix regressions. and/or fix regressions.

@ -41,6 +41,12 @@ or :command:`project` commands:
not be set without also setting not be set without also setting
:variable:`CMAKE_<LANG>_COMPILER` to a NVCC compiler. :variable:`CMAKE_<LANG>_COMPILER` to a NVCC compiler.
:variable:`CMAKE_<LANG>_PLATFORM <CMAKE_HIP_PLATFORM>`
This variable is set to the detected GPU platform when ``<lang>`` is ``HIP``.
If the variable is already set its value is always preserved. Only compatible values
will be considered for :variable:`CMAKE_<LANG>_COMPILER`.
For example: For example:
.. code-block:: cmake .. code-block:: cmake
@ -66,15 +72,23 @@ macro(check_language lang)
set(extra_compiler_variables) set(extra_compiler_variables)
if("${lang}" MATCHES "^(CUDA|HIP)$" AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") if("${lang}" MATCHES "^(CUDA|HIP)$" AND NOT CMAKE_GENERATOR MATCHES "Visual Studio")
set(extra_compiler_variables "set(CMAKE_CUDA_HOST_COMPILER \\\"\${CMAKE_CUDA_HOST_COMPILER}\\\")") set(extra_compiler_variables "set(CMAKE_${lang}_HOST_COMPILER \\\"\${CMAKE_${lang}_HOST_COMPILER}\\\")")
endif()
if("${lang}" STREQUAL "HIP")
list(APPEND extra_compiler_variables "set(CMAKE_${lang}_PLATFORM \\\"\${CMAKE_${lang}_PLATFORM}\\\")")
endif() endif()
list(TRANSFORM extra_compiler_variables PREPEND "\"")
list(TRANSFORM extra_compiler_variables APPEND "\\n\"")
list(JOIN extra_compiler_variables "\n " extra_compiler_variables)
set(_cl_content set(_cl_content
"cmake_minimum_required(VERSION ${CMAKE_VERSION}) "cmake_minimum_required(VERSION ${CMAKE_VERSION})
project(Check${lang} ${lang}) project(Check${lang} ${lang})
file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\" file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\"
\"set(CMAKE_${lang}_COMPILER \\\"\${CMAKE_${lang}_COMPILER}\\\")\\n\" \"set(CMAKE_${lang}_COMPILER \\\"\${CMAKE_${lang}_COMPILER}\\\")\\n\"
\"${extra_compiler_variables}\\n\" ${extra_compiler_variables}
)" )"
) )
@ -95,6 +109,11 @@ file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\"
else() else()
set(_D_CMAKE_TOOLCHAIN_FILE "") set(_D_CMAKE_TOOLCHAIN_FILE "")
endif() endif()
if(CMAKE_${lang}_PLATFORM)
set(_D_CMAKE_LANG_PLATFORM "-DCMAKE_${lang}_PLATFORM:STRING=${CMAKE_${lang}_PLATFORM}")
else()
set(_D_CMAKE_LANG_PLATFORM "")
endif()
execute_process( execute_process(
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang}
COMMAND ${CMAKE_COMMAND} . -G ${CMAKE_GENERATOR} COMMAND ${CMAKE_COMMAND} . -G ${CMAKE_GENERATOR}
@ -103,6 +122,7 @@ file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\"
${_D_CMAKE_GENERATOR_INSTANCE} ${_D_CMAKE_GENERATOR_INSTANCE}
${_D_CMAKE_MAKE_PROGRAM} ${_D_CMAKE_MAKE_PROGRAM}
${_D_CMAKE_TOOLCHAIN_FILE} ${_D_CMAKE_TOOLCHAIN_FILE}
${_D_CMAKE_LANG_PLATFORM}
OUTPUT_VARIABLE _cl_output OUTPUT_VARIABLE _cl_output
ERROR_VARIABLE _cl_output ERROR_VARIABLE _cl_output
RESULT_VARIABLE _cl_result RESULT_VARIABLE _cl_result
@ -130,6 +150,10 @@ file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\"
mark_as_advanced(CMAKE_${lang}_HOST_COMPILER) mark_as_advanced(CMAKE_${lang}_HOST_COMPILER)
endif() endif()
if(CMAKE_${lang}_PLATFORM)
set(CMAKE_${lang}_PLATFORM "${CMAKE_${lang}_PLATFORM}" CACHE STRING "${lang} platform")
mark_as_advanced(CMAKE_${lang}_PLATFORM)
endif()
endif() endif()
endmacro() endmacro()

@ -2825,7 +2825,6 @@ function(_ep_add_download_command name)
set(comment) set(comment)
set(work_dir) set(work_dir)
set(extra_repo_info) set(extra_repo_info)
set(byproduct_file)
if(cmd_set) if(cmd_set)
set(work_dir ${download_dir}) set(work_dir ${download_dir})
@ -3106,16 +3105,14 @@ hash=${hash}
get_filename_component(fname "${fname}" NAME) get_filename_component(fname "${fname}" NAME)
else() else()
# Fall back to a default file name. The actual file name does not # Fall back to a default file name. The actual file name does not
# matter as long as it doesn't conflict with other projects because # matter because it is used only internally and our extraction tool
# it is used only internally and our extraction tool inspects the # inspects the file content directly. If it turns out the wrong URL
# file content directly. If it turns out the wrong URL was given # was given that will be revealed during the build which is an easier
# that will be revealed during the build which is an easier place for # place for users to diagnose than an error here anyway.
# users to diagnose than an error here anyway. set(fname "archive.tar")
set(fname "${name}-archive.tar")
endif() endif()
string(REPLACE ";" "-" fname "${fname}") string(REPLACE ";" "-" fname "${fname}")
set(file ${download_dir}/${fname}) set(file ${download_dir}/${fname})
set(byproduct_file "${download_dir}/${fname}")
get_property(timeout TARGET ${name} PROPERTY _EP_TIMEOUT) get_property(timeout TARGET ${name} PROPERTY _EP_TIMEOUT)
get_property(inactivity_timeout get_property(inactivity_timeout
TARGET ${name} TARGET ${name}
@ -3292,7 +3289,6 @@ hash=${hash}
COMMAND ${__cmdQuoted} COMMAND ${__cmdQuoted}
WORKING_DIRECTORY \${work_dir} WORKING_DIRECTORY \${work_dir}
DEPENDS \${depends} DEPENDS \${depends}
BYPRODUCTS \${byproduct_file}
DEPENDEES mkdir DEPENDEES mkdir
${log} ${log}
${uses_terminal} ${uses_terminal}

@ -17,7 +17,7 @@ packages and/or feature for a build tree such as::
PNG, A PNG image library., <http://www.libpng.org/pub/png/> PNG, A PNG image library., <http://www.libpng.org/pub/png/>
* Enables saving screenshots * Enables saving screenshots
-- The following OPTIONAL packages have not been found: -- The following OPTIONAL packages have not been found:
Lua51, The Lua scripting language., <http://www.lua.org> Lua51, The Lua scripting language., <https://www.lua.org>
* Enables macros in MyWordProcessor * Enables macros in MyWordProcessor
Foo, Foo provides cool stuff. Foo, Foo provides cool stuff.

@ -1394,7 +1394,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret)
set(_Boost_THREAD_DEPENDENCIES chrono atomic) set(_Boost_THREAD_DEPENDENCIES chrono atomic)
set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono atomic) set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono atomic)
set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.84.0 AND NOT Boost_NO_WARN_NEW_VERSIONS) if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.85.0 AND NOT Boost_NO_WARN_NEW_VERSIONS)
message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets") message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets")
endif() endif()
endif() endif()
@ -1669,6 +1669,7 @@ else()
# _Boost_COMPONENT_HEADERS. See the instructions at the top of # _Boost_COMPONENT_HEADERS. See the instructions at the top of
# _Boost_COMPONENT_DEPENDENCIES. # _Boost_COMPONENT_DEPENDENCIES.
set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS}
"1.84.0" "1.84"
"1.83.0" "1.83" "1.82.0" "1.82" "1.81.0" "1.81" "1.80.0" "1.80" "1.79.0" "1.79" "1.83.0" "1.83" "1.82.0" "1.82" "1.81.0" "1.81" "1.80.0" "1.80" "1.79.0" "1.79"
"1.78.0" "1.78" "1.77.0" "1.77" "1.76.0" "1.76" "1.75.0" "1.75" "1.74.0" "1.74" "1.78.0" "1.78" "1.77.0" "1.77" "1.76.0" "1.76" "1.75.0" "1.75" "1.74.0" "1.74"
"1.73.0" "1.73" "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69" "1.73.0" "1.73" "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69"

@ -960,7 +960,7 @@ endif()
# the version of the CUDA toolchain # the version of the CUDA toolchain
# Create a separate variable so this directory can be selectively added to math targets. # Create a separate variable so this directory can be selectively added to math targets.
find_path(CUDAToolkit_CUBLAS_INCLUDE_DIR cublas_v2.h PATHS find_path(CUDAToolkit_CUBLAS_INCLUDE_DIR cublas_v2.h PATHS
"${CUDAToolkit_INCLUDE_DIRECTORIES}" ${CUDAToolkit_INCLUDE_DIRECTORIES}
NO_DEFAULT_PATH) NO_DEFAULT_PATH)
if(NOT CUDAToolkit_CUBLAS_INCLUDE_DIR) if(NOT CUDAToolkit_CUBLAS_INCLUDE_DIR)
@ -973,7 +973,7 @@ if(NOT CUDAToolkit_CUBLAS_INCLUDE_DIR)
cmake_path(NORMAL_PATH CUDAToolkit_MATH_INCLUDE_DIR) cmake_path(NORMAL_PATH CUDAToolkit_MATH_INCLUDE_DIR)
find_path(CUDAToolkit_CUBLAS_INCLUDE_DIR cublas_v2.h PATHS find_path(CUDAToolkit_CUBLAS_INCLUDE_DIR cublas_v2.h PATHS
"${CUDAToolkit_INCLUDE_DIRECTORIES}" ${CUDAToolkit_INCLUDE_DIRECTORIES}
) )
if(CUDAToolkit_CUBLAS_INCLUDE_DIR) if(CUDAToolkit_CUBLAS_INCLUDE_DIR)
list(APPEND CUDAToolkit_INCLUDE_DIRECTORIES "${CUDAToolkit_CUBLAS_INCLUDE_DIR}") list(APPEND CUDAToolkit_INCLUDE_DIRECTORIES "${CUDAToolkit_CUBLAS_INCLUDE_DIR}")
@ -1246,7 +1246,7 @@ if(CUDAToolkit_FOUND)
find_path(CUDAToolkit_CUPTI_INCLUDE_DIR cupti.h PATHS find_path(CUDAToolkit_CUPTI_INCLUDE_DIR cupti.h PATHS
"${CUDAToolkit_ROOT_DIR}/extras/CUPTI/include" "${CUDAToolkit_ROOT_DIR}/extras/CUPTI/include"
"${CUDAToolkit_INCLUDE_DIRS}" ${CUDAToolkit_INCLUDE_DIRS}
PATH_SUFFIXES "../extras/CUPTI/include" PATH_SUFFIXES "../extras/CUPTI/include"
"../../../extras/CUPTI/include" "../../../extras/CUPTI/include"
NO_DEFAULT_PATH) NO_DEFAULT_PATH)

@ -65,104 +65,6 @@ directory of a Freetype installation.
# I'm going to attempt to cut out the middleman and hope # I'm going to attempt to cut out the middleman and hope
# everything still works. # everything still works.
set(_Freetype_args)
if (Freetype_FIND_VERSION)
list(APPEND _Freetype_args
"${Freetype_FIND_VERSION}")
if (Freetype_FIND_VERSION_EXACT)
list(APPEND _Freetype_args
EXACT)
endif ()
endif ()
set(_Freetype_component_req)
set(_Freetype_component_opt)
foreach (_Freetype_component IN LISTS Freetype_FIND_COMPONENTS)
if (Freetype_FIND_REQUIRE_${_Freetype_component})
list(APPEND _Freetype_component_req
"${_Freetype_component}")
else ()
list(APPEND _Freetype_component_opt
"${_Freetype_component}")
endif ()
endforeach ()
unset(_Freetype_component)
if (_Freetype_component_req)
list(APPEND _Freetype_args
COMPONENTS "${_Freetype_component_req}")
endif ()
unset(_Freetype_component_req)
if (_Freetype_component_opt)
list(APPEND _Freetype_args
OPTIONAL_COMPONENTS "${_Freetype_component_opt}")
endif ()
unset(_Freetype_component_opt)
# Always find with QUIET to avoid noise when it is not found.
find_package(freetype CONFIG QUIET ${_Freetype_args})
unset(_Freetype_args)
if (freetype_FOUND)
if (NOT TARGET Freetype::Freetype)
add_library(Freetype::Freetype IMPORTED INTERFACE)
set_target_properties(Freetype::Freetype PROPERTIES
INTERFACE_LINK_LIBRARIES freetype)
endif ()
get_property(FREETYPE_INCLUDE_DIRS TARGET freetype PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
get_property(FREETYPE_LIBRARIES TARGET freetype PROPERTY INTERFACE_LINK_LIBRARIES)
get_property(_Freetype_location TARGET freetype PROPERTY IMPORTED_IMPLIB)
if (NOT _Freetype_location)
get_property(_Freetype_location_release TARGET freetype PROPERTY IMPORTED_IMPLIB_RELEASE)
if (NOT _Freetype_location_release)
get_property(_Freetype_location_release TARGET freetype PROPERTY IMPORTED_IMPLIB_RELWITHDEBINFO)
endif ()
get_property(_Freetype_location_debug TARGET freetype PROPERTY IMPORTED_IMPLIB_DEBUG)
if (_Freetype_location_release AND _Freetype_location_debug)
set(_Freetype_location
optimized "${_Freetype_location_release}"
debug "${_Freetype_location_debug}")
elseif (_Freetype_location_release)
set(_Freetype_location "${_Freetype_location_release}")
elseif (_Freetype_location_debug)
set(_Freetype_location "${_Freetype_location_debug}")
else ()
get_property(_Freetype_location_release TARGET freetype PROPERTY LOCATION_RELEASE)
if (NOT _Freetype_location_release)
get_property(_Freetype_location_release TARGET freetype PROPERTY LOCATION_RELWITHDEBINFO)
endif ()
get_property(_Freetype_location_debug TARGET freetype PROPERTY LOCATION_DEBUG)
if (_Freetype_location_release AND _Freetype_location_debug)
set(_Freetype_location
optimized "${_Freetype_location_release}"
debug "${_Freetype_location_debug}")
elseif (_Freetype_location_release)
set(_Freetype_location "${_Freetype_location_release}")
elseif (_Freetype_location_debug)
set(_Freetype_location "${_Freetype_location_debug}")
else ()
get_property(_Freetype_location TARGET freetype PROPERTY LOCATION)
endif ()
endif ()
unset(_Freetype_location_release)
unset(_Freetype_location_debug)
endif ()
list(INSERT FREETYPE_LIBRARIES 0
"${_Freetype_location}")
unset(_Freetype_location)
set(Freetype_FOUND 1)
set(FREETYPE_FOUND 1)
set(FREETYPE_VERSION_STRING "${freetype_VERSION}")
foreach (_Freetype_component IN LISTS Freetype_FIND_COMPONENTS)
set(Freetype_${_Freetype_component}_FOUND "${freetype_${_Freetype_component}_FOUND}")
endforeach ()
unset(_Freetype_component)
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
find_package_handle_standard_args(Freetype
HANDLE_COMPONENTS
VERSION_VAR FREETYPE_VERSION_STRING
)
return ()
endif ()
set(FREETYPE_FIND_ARGS set(FREETYPE_FIND_ARGS
HINTS HINTS
ENV FREETYPE_DIR ENV FREETYPE_DIR

@ -189,17 +189,13 @@ Cached variables
the location of the root of the Matlab installation found. If this value the location of the root of the Matlab installation found. If this value
is changed by the user, the result variables are recomputed. is changed by the user, the result variables are recomputed.
Provided macros Provided commands
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
:command:`matlab_get_version_from_release_name` :command:`matlab_get_version_from_release_name`
returns the version from the release name returns the version from the Matlab release name
:command:`matlab_get_release_name_from_version` :command:`matlab_get_release_name_from_version`
returns the release name from the Matlab version returns the release name from the Matlab version
Provided functions
^^^^^^^^^^^^^^^^^^
:command:`matlab_add_mex` :command:`matlab_add_mex`
adds a target compiling a MEX file. adds a target compiling a MEX file.
:command:`matlab_add_unit_test` :command:`matlab_add_unit_test`
@ -350,6 +346,11 @@ file(MAKE_DIRECTORY "${_matlab_temporary_folder}")
* Output: ``version`` is the version of Matlab (e.g. 23.2.0) * Output: ``version`` is the version of Matlab (e.g. 23.2.0)
Returns the version of Matlab from a release name Returns the version of Matlab from a release name
.. note::
This command provides correct versions mappings for Matlab but not MCR.
#]=======================================================================] #]=======================================================================]
macro(matlab_get_version_from_release_name release_name version_name) macro(matlab_get_version_from_release_name release_name version_name)
@ -377,33 +378,27 @@ endmacro()
* Output: ``release_name`` is the release name (R2023b) * Output: ``release_name`` is the release name (R2023b)
Returns the release name from the version of Matlab Returns the release name from the version of Matlab
.. note::
This command provides correct version mappings for Matlab but not MCR.
#]=======================================================================] #]=======================================================================]
macro(matlab_get_release_name_from_version version release_name) function(matlab_get_release_name_from_version version release_name)
# only the major.minor version is used # only the major.minor version is used
string(REGEX MATCH "([0-9]+\\.[0-9]+)" _match ${version}) string(REGEX REPLACE "^([0-9]+\\.[0-9]+).*" "\\1" version "${version}")
if(CMAKE_MATCH_1)
set(short_version ${CMAKE_MATCH_1})
else()
set(short_version ${version})
endif()
set(${release_name} "")
foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
string(REGEX MATCHALL "(.+)=${short_version}" _matched ${_var}) if(_var MATCHES "(.+)=${version}")
if(NOT _matched STREQUAL "") set(${release_name} ${CMAKE_MATCH_1} PARENT_SCOPE)
set(${release_name} ${CMAKE_MATCH_1}) return()
break()
endif() endif()
endforeach() endforeach()
unset(_var) message(WARNING "[MATLAB] The version ${version} is not registered")
unset(_matched)
if(${release_name} STREQUAL "")
message(WARNING "[MATLAB] The version ${short_version} is not registered")
endif()
endmacro() endfunction()
# extracts all the supported release names (R2022b...) of Matlab # extracts all the supported release names (R2022b...) of Matlab
@ -453,9 +448,10 @@ endmacro()
are installed. The found versions are returned in `matlab_versions`. are installed. The found versions are returned in `matlab_versions`.
Set `win64` to `TRUE` if the 64 bit version of Matlab should be looked for Set `win64` to `TRUE` if the 64 bit version of Matlab should be looked for
The returned list contains all versions under The returned list contains all versions under
``HKLM\\SOFTWARE\\Mathworks\\MATLAB`` and ``HKLM\\SOFTWARE\\Mathworks\\MATLAB``,
``HKLM\\SOFTWARE\\Mathworks\\MATLAB Runtime`` or an empty list in case an ``HKLM\\SOFTWARE\\Mathworks\\MATLAB Runtime`` and
error occurred (or nothing found). ``HKLM\\SOFTWARE\\Mathworks\\MATLAB Compiler Runtime`` or an empty list in
case an error occurred (or nothing found).
.. note:: .. note::
@ -466,7 +462,7 @@ endmacro()
function(matlab_extract_all_installed_versions_from_registry win64 matlab_versions) function(matlab_extract_all_installed_versions_from_registry win64 matlab_versions)
if(NOT CMAKE_HOST_WIN32) if(NOT CMAKE_HOST_WIN32)
message(FATAL_ERROR "[MATLAB] This macro can only be called by a Windows host") message(FATAL_ERROR "[MATLAB] This function can only be called by a Windows host")
endif() endif()
if(${win64} AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "64") if(${win64} AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "64")
@ -485,25 +481,23 @@ function(matlab_extract_all_installed_versions_from_registry win64 matlab_versio
) )
if(_reg) if(_reg)
string(REGEX MATCHALL "([0-9]+(\\.[0-9]+)+)" _versions_regex "${_reg}")
string(REGEX MATCHALL "([0-9]+\\.[0-9]+)" _versions_regex "${_reg}") foreach(_match IN LISTS _versions_regex)
if(_match MATCHES "([0-9]+(\\.[0-9]+)+)")
foreach(match IN LISTS _versions_regex)
string(REGEX MATCH "([0-9]+\\.[0-9]+)" current_match "${match}")
if(NOT CMAKE_MATCH_1)
continue()
endif()
cmake_host_system_information(RESULT _reg cmake_host_system_information(RESULT _reg
QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}/${CMAKE_MATCH_1}" QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}/${CMAKE_MATCH_1}"
VALUE "MATLABROOT" VALUE "MATLABROOT"
VIEW ${_view}
) )
_Matlab_VersionInfoXML("${_reg}" _matlab_version_tmp) _Matlab_VersionInfoXML("${_reg}" _matlab_version_tmp)
if(NOT "${_matlab_version_tmp}" STREQUAL "unknown") if("${_matlab_version_tmp}" STREQUAL "unknown")
list(APPEND matlabs_from_registry ${_match})
else()
list(APPEND matlabs_from_registry ${_matlab_version_tmp}) list(APPEND matlabs_from_registry ${_matlab_version_tmp})
endif() endif()
endif()
endforeach() endforeach()
endif() endif()
@ -558,42 +552,59 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_
# extract_matlab_versions_from_registry_brute_force or # extract_matlab_versions_from_registry_brute_force or
# matlab_extract_all_installed_versions_from_registry. # matlab_extract_all_installed_versions_from_registry.
# Mostly the major.minor version is used in Mathworks Windows Registry keys.
# If the patch is not zero, major.minor.patch is used.
list(TRANSFORM matlab_versions REPLACE "^([0-9]+\\.[0-9]+(\\.[1-9][0-9]*)?).*" "\\1")
set(_matlab_roots_list ) set(_matlab_roots_list )
# check for Matlab installations # check for Matlab installations
foreach(_matlab_current_version IN LISTS matlab_versions) foreach(_matlab_current_version IN LISTS matlab_versions)
get_filename_component( cmake_host_system_information(RESULT current_MATLAB_ROOT
current_MATLAB_ROOT QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/MATLAB/${_matlab_current_version}"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${_matlab_current_version};MATLABROOT]" VALUE "MATLABROOT"
ABSOLUTE) )
cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT)
if(IS_DIRECTORY "${current_MATLAB_ROOT}") if(IS_DIRECTORY "${current_MATLAB_ROOT}")
_Matlab_VersionInfoXML("${current_MATLAB_ROOT}" _matlab_version_tmp)
if("${_matlab_version_tmp}" STREQUAL "unknown")
list(APPEND _matlab_roots_list "MATLAB" ${_matlab_current_version} ${current_MATLAB_ROOT}) list(APPEND _matlab_roots_list "MATLAB" ${_matlab_current_version} ${current_MATLAB_ROOT})
else()
list(APPEND _matlab_roots_list "MATLAB" ${_matlab_version_tmp} ${current_MATLAB_ROOT})
endif()
endif() endif()
endforeach() endforeach()
# Check for MCR installations # Check for MCR installations
foreach(_matlab_current_version IN LISTS matlab_versions) foreach(_matlab_current_version IN LISTS matlab_versions)
get_filename_component( cmake_host_system_information(RESULT current_MATLAB_ROOT
current_MATLAB_ROOT QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/MATLAB Runtime/${_matlab_current_version}"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB Runtime\\${_matlab_current_version};MATLABROOT]" VALUE "MATLABROOT"
ABSOLUTE) )
cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT)
# remove the dot # remove the dot
string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
if(IS_DIRECTORY "${current_MATLAB_ROOT}") if(IS_DIRECTORY "${current_MATLAB_ROOT}")
_Matlab_VersionInfoXML("${current_MATLAB_ROOT}" _matlab_version_tmp)
if("${_matlab_version_tmp}" STREQUAL "unknown")
list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}") list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
else()
list(APPEND _matlab_roots_list "MCR" ${_matlab_version_tmp} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
endif()
endif() endif()
endforeach() endforeach()
# Check for old MCR installations # Check for old MCR installations
foreach(_matlab_current_version IN LISTS matlab_versions) foreach(_matlab_current_version IN LISTS matlab_versions)
get_filename_component( cmake_host_system_information(RESULT current_MATLAB_ROOT
current_MATLAB_ROOT QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/MATLAB Compiler Runtime/${_matlab_current_version}"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB Compiler Runtime\\${_matlab_current_version};MATLABROOT]" VALUE "MATLABROOT"
ABSOLUTE) )
cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT)
# remove the dot # remove the dot
string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
@ -1409,20 +1420,11 @@ function(_Matlab_VersionInfoXML matlab_root _version)
set(_ver "unknown") set(_ver "unknown")
set(_XMLfile ${matlab_root}/VersionInfo.xml) set(_XMLfile ${matlab_root}/VersionInfo.xml)
if(NOT EXISTS ${_XMLfile}) if(EXISTS ${_XMLfile})
return()
endif()
file(READ ${_XMLfile} versioninfo_string) file(READ ${_XMLfile} versioninfo_string)
if(versioninfo_string)
# parses "<version>23.2.0.2365128</version>" # parses "<version>23.2.0.2365128</version>"
string(REGEX MATCH "<version>([0-9]+(\\.[0-9]+)+)</version>" if(versioninfo_string MATCHES "<version>([0-9]+(\\.[0-9]+)+)</version>")
version_reg_match
${versioninfo_string}
)
if(CMAKE_MATCH_1)
set(_ver "${CMAKE_MATCH_1}") set(_ver "${CMAKE_MATCH_1}")
endif() endif()
endif() endif()
@ -1634,7 +1636,17 @@ set(Matlab_VERSION_STRING "NOTFOUND")
set(Matlab_Or_MCR "UNKNOWN") set(Matlab_Or_MCR "UNKNOWN")
if(_numbers_of_matlab_roots GREATER 0) if(_numbers_of_matlab_roots GREATER 0)
if(Matlab_FIND_VERSION_EXACT) if(Matlab_FIND_VERSION_EXACT)
list(FIND _matlab_possible_roots ${Matlab_FIND_VERSION} _list_index) set(_list_index -1)
foreach(_matlab_root_index RANGE 1 ${_numbers_of_matlab_roots} 3)
list(GET _matlab_possible_roots ${_matlab_root_index} _matlab_root_version)
# only the major.minor version is used
string(REGEX REPLACE "^([0-9]+\\.[0-9]+).*" "\\1" _matlab_root_version "${_matlab_root_version}")
if(_matlab_root_version VERSION_EQUAL Matlab_FIND_VERSION)
set(_list_index ${_matlab_root_index})
break()
endif()
endforeach()
if(_list_index LESS 0) if(_list_index LESS 0)
set(_list_index 1) set(_list_index 1)
endif() endif()

@ -55,24 +55,26 @@ function(cmake_cuda_architectures_all lang lang_var_)
endif() endif()
if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.4) if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.4)
if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA") if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA"
OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0)
)
list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 87) list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 87)
endif() endif()
endif() endif()
if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.8) if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.8)
if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA") if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA"
OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0)
)
list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 89 90) list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 89 90)
list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 90) list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 90)
endif() endif()
endif() endif()
if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 12.0) if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 12.0)
if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA")
list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 35 37) list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 35 37)
list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 35) list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 35)
endif() endif()
endif()
# only generate jit code for the newest arch for all/all-major # only generate jit code for the newest arch for all/all-major
list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL _latest_arch) list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL _latest_arch)

@ -41,20 +41,19 @@ macro(PKGCONFIG _package _include_DIR _link_DIR _link_FLAGS _cflags)
if(NOT _return_VALUE) if(NOT _return_VALUE)
execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --variable=includedir execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --variable=includedir
OUTPUT_VARIABLE ${_include_DIR} ) OUTPUT_VARIABLE ${_include_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE )
string(REGEX REPLACE "[\r\n]" " " ${_include_DIR} "${${_include_DIR}}") string(REGEX REPLACE "[\r\n]" " " ${_include_DIR} "${${_include_DIR}}")
execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --variable=libdir execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --variable=libdir
OUTPUT_VARIABLE ${_link_DIR} ) OUTPUT_VARIABLE ${_link_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE )
string(REGEX REPLACE "[\r\n]" " " ${_link_DIR} "${${_link_DIR}}") string(REGEX REPLACE "[\r\n]" " " ${_link_DIR} "${${_link_DIR}}")
execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --libs execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --libs
OUTPUT_VARIABLE ${_link_FLAGS} ) OUTPUT_VARIABLE ${_link_FLAGS} OUTPUT_STRIP_TRAILING_WHITESPACE )
string(REGEX REPLACE "[\r\n]" " " ${_link_FLAGS} "${${_link_FLAGS}}") string(REGEX REPLACE "[\r\n]" " " ${_link_FLAGS} "${${_link_FLAGS}}")
execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --cflags execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --cflags
OUTPUT_VARIABLE ${_cflags} ) OUTPUT_VARIABLE ${_cflags} OUTPUT_STRIP_TRAILING_WHITESPACE )
string(REGEX REPLACE "[\r\n]" " " ${_cflags} "${${_cflags}}") string(REGEX REPLACE "[\r\n]" " " ${_cflags} "${${_cflags}}")
else() else()

@ -1,7 +1,7 @@
# CMake version number components. # CMake version number components.
set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MAJOR 3)
set(CMake_VERSION_MINOR 28) set(CMake_VERSION_MINOR 28)
set(CMake_VERSION_PATCH 1) set(CMake_VERSION_PATCH 2)
#set(CMake_VERSION_RC 0) #set(CMake_VERSION_RC 0)
set(CMake_VERSION_IS_DIRTY 0) set(CMake_VERSION_IS_DIRTY 0)
@ -21,7 +21,7 @@ endif()
if(NOT CMake_VERSION_NO_GIT) if(NOT CMake_VERSION_NO_GIT)
# If this source was exported by 'git archive', use its commit info. # If this source was exported by 'git archive', use its commit info.
set(git_info [==[1eed682d7c CMake 3.28.1]==]) set(git_info [==[1f25aa1a0a CMake 3.28.2]==])
# Otherwise, try to identify the current development source version. # Otherwise, try to identify the current development source version.
if(NOT git_info MATCHES "^([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]?[0-9a-f]?)[0-9a-f]* " if(NOT git_info MATCHES "^([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]?[0-9a-f]?)[0-9a-f]* "

@ -7,8 +7,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <fcntl.h>
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cm_sys_stat.h" #include "cm_sys_stat.h"

@ -135,14 +135,14 @@ private:
std::string cmCTestBZR::LoadInfo() std::string cmCTestBZR::LoadInfo()
{ {
// Run "bzr info" to get the repository info from the work tree. // Run "bzr info" to get the repository info from the work tree.
std::string bzr = this->CommandLineTool; const char* bzr = this->CommandLineTool.c_str();
std::vector<std::string> bzr_info = { bzr, "info" }; const char* bzr_info[] = { bzr, "info", nullptr };
InfoParser iout(this, "info-out> "); InfoParser iout(this, "info-out> ");
OutputLogger ierr(this->Log, "info-err> "); OutputLogger ierr(this->Log, "info-err> ");
this->RunChild(bzr_info, &iout, &ierr); this->RunChild(bzr_info, &iout, &ierr);
// Run "bzr revno" to get the repository revision number from the work tree. // Run "bzr revno" to get the repository revision number from the work tree.
std::vector<std::string> bzr_revno = { bzr, "revno" }; const char* bzr_revno[] = { bzr, "revno", nullptr };
std::string rev; std::string rev;
RevnoParser rout(this, "revno-out> ", rev); RevnoParser rout(this, "revno-out> ", rev);
OutputLogger rerr(this->Log, "revno-err> "); OutputLogger rerr(this->Log, "revno-err> ");
@ -372,18 +372,22 @@ bool cmCTestBZR::UpdateImpl()
// TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
// Use "bzr pull" to update the working tree. // Use "bzr pull" to update the working tree.
std::vector<std::string> bzr_update; std::vector<char const*> bzr_update;
bzr_update.push_back(this->CommandLineTool); bzr_update.push_back(this->CommandLineTool.c_str());
bzr_update.push_back("pull"); bzr_update.push_back("pull");
cm::append(bzr_update, args); for (std::string const& arg : args) {
bzr_update.push_back(arg.c_str());
}
bzr_update.push_back(this->URL.c_str());
bzr_update.push_back(this->URL); bzr_update.push_back(nullptr);
// For some reason bzr uses stderr to display the update status. // For some reason bzr uses stderr to display the update status.
OutputLogger out(this->Log, "pull-out> "); OutputLogger out(this->Log, "pull-out> ");
UpdateParser err(this, "pull-err> "); UpdateParser err(this, "pull-err> ");
return this->RunUpdateCommand(bzr_update, &out, &err); return this->RunUpdateCommand(bzr_update.data(), &out, &err);
} }
bool cmCTestBZR::LoadRevisions() bool cmCTestBZR::LoadRevisions()
@ -404,9 +408,10 @@ bool cmCTestBZR::LoadRevisions()
} }
// Run "bzr log" to get all global revisions of interest. // Run "bzr log" to get all global revisions of interest.
std::string bzr = this->CommandLineTool; const char* bzr = this->CommandLineTool.c_str();
std::vector<std::string> bzr_log = { bzr, "log", "-v", "-r", const char* bzr_log[] = {
revs, "--xml", this->URL }; bzr, "log", "-v", "-r", revs.c_str(), "--xml", this->URL.c_str(), nullptr
};
{ {
LogParser out(this, "log-out> "); LogParser out(this, "log-out> ");
OutputLogger err(this->Log, "log-err> "); OutputLogger err(this->Log, "log-err> ");
@ -462,8 +467,8 @@ private:
bool cmCTestBZR::LoadModifications() bool cmCTestBZR::LoadModifications()
{ {
// Run "bzr status" which reports local modifications. // Run "bzr status" which reports local modifications.
std::string bzr = this->CommandLineTool; const char* bzr = this->CommandLineTool.c_str();
std::vector<std::string> bzr_status = { bzr, "status", "-SV" }; const char* bzr_status[] = { bzr, "status", "-SV", nullptr };
StatusParser out(this, "status-out> "); StatusParser out(this, "status-out> ");
OutputLogger err(this->Log, "status-err> "); OutputLogger err(this->Log, "status-err> ");
this->RunChild(bzr_status, &out, &err); this->RunChild(bzr_status, &out, &err);

@ -7,6 +7,8 @@
#include <cstring> #include <cstring>
#include <ratio> #include <ratio>
#include "cmsys/Process.h"
#include "cmBuildOptions.h" #include "cmBuildOptions.h"
#include "cmCTest.h" #include "cmCTest.h"
#include "cmCTestTestHandler.h" #include "cmCTestTestHandler.h"
@ -306,11 +308,12 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
return 1; return 1;
} }
std::vector<std::string> testCommand; std::vector<const char*> testCommand;
testCommand.push_back(fullPath); testCommand.push_back(fullPath.c_str());
for (std::string const& testCommandArg : this->TestCommandArgs) { for (std::string const& testCommandArg : this->TestCommandArgs) {
testCommand.push_back(testCommandArg); testCommand.push_back(testCommandArg.c_str());
} }
testCommand.push_back(nullptr);
std::string outs; std::string outs;
int retval = 0; int retval = 0;
// run the test from the this->BuildRunDir if set // run the test from the this->BuildRunDir if set
@ -346,10 +349,10 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
} }
} }
bool runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr, int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr,
remainingTime, nullptr); remainingTime, nullptr);
if (!runTestRes || retval != 0) { if (runTestRes != cmsysProcess_State_Exited || retval != 0) {
out << "Test command failed: " << testCommand[0] << "\n"; out << "Test command failed: " << testCommand[0] << "\n";
retval = 1; retval = 1;
} }

@ -3,17 +3,15 @@
#include "cmCTestBuildHandler.h" #include "cmCTestBuildHandler.h"
#include <cstdlib> #include <cstdlib>
#include <memory>
#include <ratio> #include <ratio>
#include <set> #include <set>
#include <utility> #include <utility>
#include <cmext/algorithm> #include <cmext/algorithm>
#include <cm3p/uv.h>
#include "cmsys/Directory.hxx" #include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmCTest.h" #include "cmCTest.h"
#include "cmCTestLaunchReporter.h" #include "cmCTestLaunchReporter.h"
@ -26,9 +24,6 @@
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmStringReplaceHelper.h" #include "cmStringReplaceHelper.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVHandlePtr.h"
#include "cmUVProcessChain.h"
#include "cmUVStream.h"
#include "cmValue.h" #include "cmValue.h"
#include "cmXMLWriter.h" #include "cmXMLWriter.h"
@ -425,7 +420,7 @@ int cmCTestBuildHandler::ProcessHandler()
cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr); cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr);
this->ColorRemover = &colorRemover; this->ColorRemover = &colorRemover;
int retVal = 0; int retVal = 0;
bool res = true; int res = cmsysProcess_State_Exited;
if (!this->CTest->GetShowOnly()) { if (!this->CTest->GetShowOnly()) {
res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0, res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0,
ofs); ofs);
@ -480,7 +475,7 @@ int cmCTestBuildHandler::ProcessHandler()
} }
this->GenerateXMLFooter(xml, elapsed_build_time); this->GenerateXMLFooter(xml, elapsed_build_time);
if (!res || retVal || this->TotalErrors > 0) { if (res != cmsysProcess_State_Exited || retVal || this->TotalErrors > 0) {
cmCTestLog(this->CTest, ERROR_MESSAGE, cmCTestLog(this->CTest, ERROR_MESSAGE,
"Error(s) when building project" << std::endl); "Error(s) when building project" << std::endl);
} }
@ -769,7 +764,7 @@ void cmCTestBuildHandler::LaunchHelper::WriteScrapeMatchers(
} }
} }
bool cmCTestBuildHandler::RunMakeCommand(const std::string& command, int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
int* retVal, const char* dir, int* retVal, const char* dir,
int timeout, std::ostream& ofs, int timeout, std::ostream& ofs,
Encoding encoding) Encoding encoding)
@ -781,9 +776,19 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
return false; return false;
} }
std::vector<const char*> argv;
argv.reserve(args.size() + 1);
for (std::string const& arg : args) {
argv.push_back(arg.c_str());
}
argv.push_back(nullptr);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Run command:", this->Quiet); "Run command:", this->Quiet);
for (auto const& arg : args) { for (char const* arg : argv) {
if (!arg) {
break;
}
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" \"" << arg << "\"", this->Quiet); " \"" << arg << "\"", this->Quiet);
} }
@ -795,20 +800,21 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
static_cast<void>(launchHelper); static_cast<void>(launchHelper);
// Now create process object // Now create process object
cmUVProcessChainBuilder builder; cmsysProcess* cp = cmsysProcess_New();
builder.AddCommand(args) cmsysProcess_SetCommand(cp, argv.data());
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) cmsysProcess_SetWorkingDirectory(cp, dir);
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
if (dir) { cmsysProcess_SetTimeout(cp, timeout);
builder.SetWorkingDirectory(dir); cmsysProcess_Execute(cp);
}
auto chain = builder.Start();
// Initialize tick's // Initialize tick's
std::string::size_type tick = 0; std::string::size_type tick = 0;
static constexpr std::string::size_type tick_len = 1024; const std::string::size_type tick_len = 1024;
char* data;
int length;
cmProcessOutput processOutput(encoding); cmProcessOutput processOutput(encoding);
std::string strdata;
cmCTestOptionalLog( cmCTestOptionalLog(
this->CTest, HANDLER_PROGRESS_OUTPUT, this->CTest, HANDLER_PROGRESS_OUTPUT,
" Each symbol represents " " Each symbol represents "
@ -830,65 +836,39 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
this->WarningQuotaReached = false; this->WarningQuotaReached = false;
this->ErrorQuotaReached = false; this->ErrorQuotaReached = false;
cm::uv_timer_ptr timer;
bool timedOut = false;
timer.init(chain.GetLoop(), &timedOut);
if (timeout > 0) {
timer.start(
[](uv_timer_t* t) {
auto* timedOutPtr = static_cast<bool*>(t->data);
*timedOutPtr = true;
},
timeout * 1000, 0);
}
// For every chunk of data // For every chunk of data
cm::uv_pipe_ptr outputStream; int res;
bool outFinished = false; while ((res = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
cm::uv_pipe_ptr errorStream; // Replace '\0' with '\n', since '\0' does not really make sense. This is
bool errFinished = false; // for Visual Studio output
auto startRead = [this, &chain, &processOutput, &tick, for (int cc = 0; cc < length; ++cc) {
&ofs](cm::uv_pipe_ptr& pipe, int stream, if (data[cc] == 0) {
t_BuildProcessingQueueType& queue, bool& finished, data[cc] = '\n';
int id) -> std::unique_ptr<cmUVStreamReadHandle> {
pipe.init(chain.GetLoop(), 0);
uv_pipe_open(pipe, stream);
return cmUVStreamRead(
pipe,
[this, &processOutput, &queue, id, &tick, &ofs](std::vector<char> data) {
// Replace '\0' with '\n', since '\0' does not really make sense. This
// is for Visual Studio output
for (auto& c : data) {
if (c == 0) {
c = '\n';
} }
} }
// Process the chunk of data // Process the chunk of data
std::string strdata; if (res == cmsysProcess_Pipe_STDERR) {
processOutput.DecodeText(data.data(), data.size(), strdata, id); processOutput.DecodeText(data, length, strdata, 1);
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
ofs, &queue); &this->BuildProcessingErrorQueue);
}, } else {
[this, &processOutput, &queue, id, &tick, &ofs, &finished]() { processOutput.DecodeText(data, length, strdata, 2);
std::string strdata; this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
processOutput.DecodeText(std::string(), strdata, id); &this->BuildProcessingQueue);
}
}
processOutput.DecodeText(std::string(), strdata, 1);
if (!strdata.empty()) { if (!strdata.empty()) {
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
ofs, &queue); &this->BuildProcessingErrorQueue);
} }
finished = true; processOutput.DecodeText(std::string(), strdata, 2);
}); if (!strdata.empty()) {
}; this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
auto outputHandle = startRead(outputStream, chain.OutputStream(), &this->BuildProcessingQueue);
this->BuildProcessingQueue, outFinished, 1);
auto errorHandle =
startRead(errorStream, chain.ErrorStream(),
this->BuildProcessingErrorQueue, errFinished, 2);
while (!timedOut && !(outFinished && errFinished && chain.Finished())) {
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
} }
this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs, this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
&this->BuildProcessingQueue); &this->BuildProcessingQueue);
this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs, this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
@ -899,13 +879,13 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
<< std::endl, << std::endl,
this->Quiet); this->Quiet);
if (chain.Finished()) { // Properly handle output of the build command
auto const& status = chain.GetStatus(0); cmsysProcess_WaitForExit(cp, nullptr);
auto exception = status.GetException(); int result = cmsysProcess_GetState(cp);
switch (exception.first) {
case cmUVProcessChain::ExceptionCode::None: if (result == cmsysProcess_State_Exited) {
if (retVal) { if (retVal) {
*retVal = static_cast<int>(status.ExitStatus); *retVal = cmsysProcess_GetExitValue(cp);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Command exited with the value: " << *retVal "Command exited with the value: " << *retVal
<< std::endl, << std::endl,
@ -933,9 +913,8 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
reporter.RealArgs = args; reporter.RealArgs = args;
reporter.ComputeFileNames(); reporter.ComputeFileNames();
reporter.ExitCode = *retVal; reporter.ExitCode = *retVal;
reporter.Status = status; reporter.Process = cp;
// Use temporary BuildLog file to populate this error for // Use temporary BuildLog file to populate this error for CDash.
// CDash.
ofs.flush(); ofs.flush();
reporter.LogOut = this->LogFileNames["Build"]; reporter.LogOut = this->LogFileNames["Build"];
reporter.LogOut += ".tmp"; reporter.LogOut += ".tmp";
@ -946,7 +925,7 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
errorwarning.LineNumber = 0; errorwarning.LineNumber = 0;
errorwarning.LogLine = 1; errorwarning.LogLine = 1;
errorwarning.Text = cmStrCat( errorwarning.Text = cmStrCat(
"*** WARNING non-zero return value in ctest from: ", args[0]); "*** WARNING non-zero return value in ctest from: ", argv[0]);
errorwarning.PreContext.clear(); errorwarning.PreContext.clear();
errorwarning.PostContext.clear(); errorwarning.PostContext.clear();
errorwarning.Error = false; errorwarning.Error = false;
@ -955,37 +934,35 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
} }
} }
} }
break; } else if (result == cmsysProcess_State_Exception) {
case cmUVProcessChain::ExceptionCode::Spawn: { if (retVal) {
*retVal = cmsysProcess_GetExitException(cp);
cmCTestOptionalLog(this->CTest, WARNING,
"There was an exception: " << *retVal << std::endl,
this->Quiet);
}
} else if (result == cmsysProcess_State_Expired) {
cmCTestOptionalLog(this->CTest, WARNING,
"There was a timeout" << std::endl, this->Quiet);
} else if (result == cmsysProcess_State_Error) {
// If there was an error running command, report that on the dashboard. // If there was an error running command, report that on the dashboard.
cmCTestBuildErrorWarning errorwarning; cmCTestBuildErrorWarning errorwarning;
errorwarning.LineNumber = 0; errorwarning.LineNumber = 0;
errorwarning.LogLine = 1; errorwarning.LogLine = 1;
errorwarning.Text = errorwarning.Text =
cmStrCat("*** ERROR executing: ", exception.second); cmStrCat("*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
errorwarning.PreContext.clear(); errorwarning.PreContext.clear();
errorwarning.PostContext.clear(); errorwarning.PostContext.clear();
errorwarning.Error = true; errorwarning.Error = true;
this->ErrorsAndWarnings.push_back(std::move(errorwarning)); this->ErrorsAndWarnings.push_back(std::move(errorwarning));
this->TotalErrors++; this->TotalErrors++;
cmCTestLog(this->CTest, ERROR_MESSAGE, cmCTestLog(this->CTest, ERROR_MESSAGE,
"There was an error: " << exception.second << std::endl); "There was an error: " << cmsysProcess_GetErrorString(cp)
} break; << std::endl);
default:
if (retVal) {
*retVal = status.TermSignal;
cmCTestOptionalLog(
this->CTest, WARNING,
"There was an exception: " << *retVal << std::endl, this->Quiet);
}
break;
}
} else {
cmCTestOptionalLog(this->CTest, WARNING,
"There was a timeout" << std::endl, this->Quiet);
} }
return true; cmsysProcess_Delete(cp);
return result;
} }
// ###################################################################### // ######################################################################

@ -53,7 +53,7 @@ private:
//! Run command specialized for make and configure. Returns process status //! Run command specialized for make and configure. Returns process status
// and retVal is return value or exception. // and retVal is return value or exception.
bool RunMakeCommand(const std::string& command, int* retVal, const char* dir, int RunMakeCommand(const std::string& command, int* retVal, const char* dir,
int timeout, std::ostream& ofs, int timeout, std::ostream& ofs,
Encoding encoding = cmProcessOutput::Auto); Encoding encoding = cmProcessOutput::Auto);

@ -5,7 +5,6 @@
#include <utility> #include <utility>
#include <cm/string_view> #include <cm/string_view>
#include <cmext/algorithm>
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
@ -90,15 +89,18 @@ bool cmCTestCVS::UpdateImpl()
} }
// Run "cvs update" to update the work tree. // Run "cvs update" to update the work tree.
std::vector<std::string> cvs_update; std::vector<char const*> cvs_update;
cvs_update.push_back(this->CommandLineTool); cvs_update.push_back(this->CommandLineTool.c_str());
cvs_update.push_back("-z3"); cvs_update.push_back("-z3");
cvs_update.push_back("update"); cvs_update.push_back("update");
cm::append(cvs_update, args); for (std::string const& arg : args) {
cvs_update.push_back(arg.c_str());
}
cvs_update.push_back(nullptr);
UpdateParser out(this, "up-out> "); UpdateParser out(this, "up-out> ");
UpdateParser err(this, "up-err> "); UpdateParser err(this, "up-err> ");
return this->RunUpdateCommand(cvs_update, &out, &err); return this->RunUpdateCommand(cvs_update.data(), &out, &err);
} }
class cmCTestCVS::LogParser : public cmCTestVC::LineParser class cmCTestCVS::LogParser : public cmCTestVC::LineParser
@ -219,8 +221,10 @@ void cmCTestCVS::LoadRevisions(std::string const& file, const char* branchFlag,
cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush); cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
// Run "cvs log" to get revisions of this file on this branch. // Run "cvs log" to get revisions of this file on this branch.
std::string cvs = this->CommandLineTool; const char* cvs = this->CommandLineTool.c_str();
std::vector<std::string> cvs_log = { cvs, "log", "-N", branchFlag, file }; const char* cvs_log[] = {
cvs, "log", "-N", branchFlag, file.c_str(), nullptr
};
LogParser out(this, "log-out> ", revisions); LogParser out(this, "log-out> ", revisions);
OutputLogger err(this->Log, "log-err> "); OutputLogger err(this->Log, "log-err> ");

@ -45,7 +45,7 @@ int cmCTestConfigureHandler::ProcessHandler()
auto elapsed_time_start = std::chrono::steady_clock::now(); auto elapsed_time_start = std::chrono::steady_clock::now();
std::string output; std::string output;
int retVal = 0; int retVal = 0;
bool res = false; int res = 0;
if (!this->CTest->GetShowOnly()) { if (!this->CTest->GetShowOnly()) {
cmGeneratedFileStream os; cmGeneratedFileStream os;
if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) { if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) {

@ -9,7 +9,6 @@
#include <cstring> #include <cstring>
#include <iomanip> #include <iomanip>
#include <iterator> #include <iterator>
#include <memory>
#include <ratio> #include <ratio>
#include <sstream> #include <sstream>
#include <type_traits> #include <type_traits>
@ -19,10 +18,9 @@
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx" #include "cmsys/Glob.hxx"
#include "cmsys/Process.h"
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cm_fileno.hxx"
#include "cmCTest.h" #include "cmCTest.h"
#include "cmDuration.h" #include "cmDuration.h"
#include "cmGeneratedFileStream.h" #include "cmGeneratedFileStream.h"
@ -35,7 +33,6 @@
#include "cmParsePHPCoverage.h" #include "cmParsePHPCoverage.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVProcessChain.h"
#include "cmWorkingDirectory.h" #include "cmWorkingDirectory.h"
#include "cmXMLWriter.h" #include "cmXMLWriter.h"
@ -43,6 +40,85 @@ class cmMakefile;
#define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0)) #define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0))
class cmCTestRunProcess
{
public:
cmCTestRunProcess()
{
this->Process = cmsysProcess_New();
this->PipeState = -1;
this->TimeOut = cmDuration(-1);
}
~cmCTestRunProcess()
{
if (this->PipeState != -1 && this->PipeState != cmsysProcess_Pipe_None &&
this->PipeState != cmsysProcess_Pipe_Timeout) {
this->WaitForExit();
}
cmsysProcess_Delete(this->Process);
}
cmCTestRunProcess(const cmCTestRunProcess&) = delete;
cmCTestRunProcess& operator=(const cmCTestRunProcess&) = delete;
void SetCommand(const char* command)
{
this->CommandLineStrings.clear();
this->CommandLineStrings.emplace_back(command);
}
void AddArgument(const char* arg)
{
if (arg) {
this->CommandLineStrings.emplace_back(arg);
}
}
void SetWorkingDirectory(const char* dir) { this->WorkingDirectory = dir; }
void SetTimeout(cmDuration t) { this->TimeOut = t; }
bool StartProcess()
{
std::vector<const char*> args;
args.reserve(this->CommandLineStrings.size());
for (std::string const& cl : this->CommandLineStrings) {
args.push_back(cl.c_str());
}
args.push_back(nullptr); // null terminate
cmsysProcess_SetCommand(this->Process, args.data());
if (!this->WorkingDirectory.empty()) {
cmsysProcess_SetWorkingDirectory(this->Process,
this->WorkingDirectory.c_str());
}
cmsysProcess_SetOption(this->Process, cmsysProcess_Option_HideWindow, 1);
if (this->TimeOut >= cmDuration::zero()) {
cmsysProcess_SetTimeout(this->Process, this->TimeOut.count());
}
cmsysProcess_Execute(this->Process);
this->PipeState = cmsysProcess_GetState(this->Process);
// if the process is running or exited return true
return this->PipeState == cmsysProcess_State_Executing ||
this->PipeState == cmsysProcess_State_Exited;
}
void SetStdoutFile(const char* fname)
{
cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDOUT, fname);
}
void SetStderrFile(const char* fname)
{
cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDERR, fname);
}
int WaitForExit(double* timeout = nullptr)
{
this->PipeState = cmsysProcess_WaitForExit(this->Process, timeout);
return this->PipeState;
}
int GetProcessState() const { return this->PipeState; }
private:
int PipeState;
cmsysProcess* Process;
std::vector<std::string> CommandLineStrings;
std::string WorkingDirectory;
cmDuration TimeOut;
};
cmCTestCoverageHandler::cmCTestCoverageHandler() = default; cmCTestCoverageHandler::cmCTestCoverageHandler() = default;
void cmCTestCoverageHandler::Initialize() void cmCTestCoverageHandler::Initialize()
@ -1864,35 +1940,34 @@ int cmCTestCoverageHandler::RunBullseyeCommand(
cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n"); cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
return 0; return 0;
} }
std::vector<std::string> args{ cmd };
if (arg) { if (arg) {
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Run : " << program << " " << arg << "\n", this->Quiet); "Run : " << program << " " << arg << "\n", this->Quiet);
args.emplace_back(arg);
} else { } else {
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Run : " << program << "\n", this->Quiet); "Run : " << program << "\n", this->Quiet);
} }
// create a process object and start it // create a process object and start it
cmUVProcessChainBuilder builder; cmCTestRunProcess runCoverageSrc;
runCoverageSrc.SetCommand(program.c_str());
runCoverageSrc.AddArgument(arg);
std::string stdoutFile = std::string stdoutFile =
cmStrCat(cont->BinaryDir, "/Testing/Temporary/", cmStrCat(cont->BinaryDir, "/Testing/Temporary/",
this->GetCTestInstance()->GetCurrentTag(), '-', cmd); this->GetCTestInstance()->GetCurrentTag(), '-', cmd);
std::string stderrFile = stdoutFile; std::string stderrFile = stdoutFile;
stdoutFile += ".stdout"; stdoutFile += ".stdout";
stderrFile += ".stderr"; stderrFile += ".stderr";
std::unique_ptr<FILE, int (*)(FILE*)> stdoutHandle( runCoverageSrc.SetStdoutFile(stdoutFile.c_str());
cmsys::SystemTools::Fopen(stdoutFile, "w"), fclose); runCoverageSrc.SetStderrFile(stderrFile.c_str());
std::unique_ptr<FILE, int (*)(FILE*)> stderrHandle( if (!runCoverageSrc.StartProcess()) {
cmsys::SystemTools::Fopen(stderrFile, "w"), fclose); cmCTestLog(this->CTest, ERROR_MESSAGE,
builder.AddCommand(args) "Could not run : " << program << " " << arg << "\n"
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, << "kwsys process state : "
cm_fileno(stdoutHandle.get())) << runCoverageSrc.GetProcessState());
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, return 0;
cm_fileno(stderrHandle.get())); }
// since we set the output file names wait for it to end // since we set the output file names wait for it to end
auto chain = builder.Start(); runCoverageSrc.WaitForExit();
chain.Wait();
outputFile = stdoutFile; outputFile = stdoutFile;
return 1; return 1;
} }

@ -9,9 +9,8 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <cmext/algorithm>
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmCTest.h" #include "cmCTest.h"
#include "cmCTestVC.h" #include "cmCTestVC.h"
@ -19,7 +18,6 @@
#include "cmProcessOutput.h" #include "cmProcessOutput.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVProcessChain.h"
#include "cmValue.h" #include "cmValue.h"
static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major, static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major,
@ -60,9 +58,9 @@ private:
std::string cmCTestGIT::GetWorkingRevision() std::string cmCTestGIT::GetWorkingRevision()
{ {
// Run plumbing "git rev-list" to get work tree revision. // Run plumbing "git rev-list" to get work tree revision.
std::string git = this->CommandLineTool; const char* git = this->CommandLineTool.c_str();
std::vector<std::string> git_rev_list = { git, "rev-list", "-n", const char* git_rev_list[] = { git, "rev-list", "-n", "1",
"1", "HEAD", "--" }; "HEAD", "--", nullptr };
std::string rev; std::string rev;
OneLineParser out(this, "rl-out> ", rev); OneLineParser out(this, "rl-out> ", rev);
OutputLogger err(this->Log, "rl-err> "); OutputLogger err(this->Log, "rl-err> ");
@ -94,13 +92,13 @@ std::string cmCTestGIT::FindGitDir()
std::string git_dir; std::string git_dir;
// Run "git rev-parse --git-dir" to locate the real .git directory. // Run "git rev-parse --git-dir" to locate the real .git directory.
std::string git = this->CommandLineTool; const char* git = this->CommandLineTool.c_str();
std::vector<std::string> git_rev_parse = { git, "rev-parse", "--git-dir" }; char const* git_rev_parse[] = { git, "rev-parse", "--git-dir", nullptr };
std::string git_dir_line; std::string git_dir_line;
OneLineParser rev_parse_out(this, "rev-parse-out> ", git_dir_line); OneLineParser rev_parse_out(this, "rev-parse-out> ", git_dir_line);
OutputLogger rev_parse_err(this->Log, "rev-parse-err> "); OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
std::string{}, cmProcessOutput::UTF8)) { cmProcessOutput::UTF8)) {
git_dir = git_dir_line; git_dir = git_dir_line;
} }
if (git_dir.empty()) { if (git_dir.empty()) {
@ -119,10 +117,11 @@ std::string cmCTestGIT::FindGitDir()
std::string cygpath_exe = std::string cygpath_exe =
cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe"); cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe");
if (cmSystemTools::FileExists(cygpath_exe)) { if (cmSystemTools::FileExists(cygpath_exe)) {
std::vector<std::string> cygpath = { cygpath_exe, "-w", git_dir }; char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(),
0 };
OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line); OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line);
OutputLogger cygpath_err(this->Log, "cygpath-err> "); OutputLogger cygpath_err(this->Log, "cygpath-err> ");
if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, std::string{}, if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, nullptr,
cmProcessOutput::UTF8)) { cmProcessOutput::UTF8)) {
git_dir = git_dir_line; git_dir = git_dir_line;
} }
@ -137,12 +136,12 @@ std::string cmCTestGIT::FindTopDir()
std::string top_dir = this->SourceDirectory; std::string top_dir = this->SourceDirectory;
// Run "git rev-parse --show-cdup" to locate the top of the tree. // Run "git rev-parse --show-cdup" to locate the top of the tree.
std::string git = this->CommandLineTool; const char* git = this->CommandLineTool.c_str();
std::vector<std::string> git_rev_parse = { git, "rev-parse", "--show-cdup" }; char const* git_rev_parse[] = { git, "rev-parse", "--show-cdup", nullptr };
std::string cdup; std::string cdup;
OneLineParser rev_parse_out(this, "rev-parse-out> ", cdup); OneLineParser rev_parse_out(this, "rev-parse-out> ", cdup);
OutputLogger rev_parse_err(this->Log, "rev-parse-err> "); OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, "", if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
cmProcessOutput::UTF8) && cmProcessOutput::UTF8) &&
!cdup.empty()) { !cdup.empty()) {
top_dir += "/"; top_dir += "/";
@ -154,10 +153,10 @@ std::string cmCTestGIT::FindTopDir()
bool cmCTestGIT::UpdateByFetchAndReset() bool cmCTestGIT::UpdateByFetchAndReset()
{ {
std::string git = this->CommandLineTool; const char* git = this->CommandLineTool.c_str();
// Use "git fetch" to get remote commits. // Use "git fetch" to get remote commits.
std::vector<std::string> git_fetch; std::vector<char const*> git_fetch;
git_fetch.push_back(git); git_fetch.push_back(git);
git_fetch.push_back("fetch"); git_fetch.push_back("fetch");
@ -167,12 +166,17 @@ bool cmCTestGIT::UpdateByFetchAndReset()
opts = this->CTest->GetCTestConfiguration("GITUpdateOptions"); opts = this->CTest->GetCTestConfiguration("GITUpdateOptions");
} }
std::vector<std::string> args = cmSystemTools::ParseArguments(opts); std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
cm::append(git_fetch, args); for (std::string const& arg : args) {
git_fetch.push_back(arg.c_str());
}
// Sentinel argument.
git_fetch.push_back(nullptr);
// Fetch upstream refs. // Fetch upstream refs.
OutputLogger fetch_out(this->Log, "fetch-out> "); OutputLogger fetch_out(this->Log, "fetch-out> ");
OutputLogger fetch_err(this->Log, "fetch-err> "); OutputLogger fetch_err(this->Log, "fetch-err> ");
if (!this->RunUpdateCommand(git_fetch, &fetch_out, &fetch_err)) { if (!this->RunUpdateCommand(git_fetch.data(), &fetch_out, &fetch_err)) {
return false; return false;
} }
@ -203,22 +207,25 @@ bool cmCTestGIT::UpdateByFetchAndReset()
} }
// Reset the local branch to point at that tracked from upstream. // Reset the local branch to point at that tracked from upstream.
std::vector<std::string> git_reset = { git, "reset", "--hard", sha1 }; char const* git_reset[] = { git, "reset", "--hard", sha1.c_str(), nullptr };
OutputLogger reset_out(this->Log, "reset-out> "); OutputLogger reset_out(this->Log, "reset-out> ");
OutputLogger reset_err(this->Log, "reset-err> "); OutputLogger reset_err(this->Log, "reset-err> ");
return this->RunChild(git_reset, &reset_out, &reset_err); return this->RunChild(&git_reset[0], &reset_out, &reset_err);
} }
bool cmCTestGIT::UpdateByCustom(std::string const& custom) bool cmCTestGIT::UpdateByCustom(std::string const& custom)
{ {
cmList git_custom_command{ custom, cmList::EmptyElements::Yes }; cmList git_custom_command{ custom, cmList::EmptyElements::Yes };
std::vector<std::string> git_custom; std::vector<char const*> git_custom;
git_custom.reserve(git_custom_command.size()); git_custom.reserve(git_custom_command.size() + 1);
cm::append(git_custom, git_custom_command); for (std::string const& i : git_custom_command) {
git_custom.push_back(i.c_str());
}
git_custom.push_back(nullptr);
OutputLogger custom_out(this->Log, "custom-out> "); OutputLogger custom_out(this->Log, "custom-out> ");
OutputLogger custom_err(this->Log, "custom-err> "); OutputLogger custom_err(this->Log, "custom-err> ");
return this->RunUpdateCommand(git_custom, &custom_out, &custom_err); return this->RunUpdateCommand(git_custom.data(), &custom_out, &custom_err);
} }
bool cmCTestGIT::UpdateInternal() bool cmCTestGIT::UpdateInternal()
@ -237,14 +244,13 @@ bool cmCTestGIT::UpdateImpl()
} }
std::string top_dir = this->FindTopDir(); std::string top_dir = this->FindTopDir();
std::string git = this->CommandLineTool; const char* git = this->CommandLineTool.c_str();
std::string recursive = "--recursive"; const char* recursive = "--recursive";
std::string sync_recursive = "--recursive"; const char* sync_recursive = "--recursive";
// Git < 1.6.5 did not support submodule --recursive // Git < 1.6.5 did not support submodule --recursive
bool support_recursive = true;
if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) { if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) {
support_recursive = false; recursive = nullptr;
// No need to require >= 1.6.5 if there are no submodules. // No need to require >= 1.6.5 if there are no submodules.
if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) { if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
this->Log << "Git < 1.6.5 cannot update submodules recursively\n"; this->Log << "Git < 1.6.5 cannot update submodules recursively\n";
@ -252,9 +258,8 @@ bool cmCTestGIT::UpdateImpl()
} }
// Git < 1.8.1 did not support sync --recursive // Git < 1.8.1 did not support sync --recursive
bool support_sync_recursive = true;
if (this->GetGitVersion() < cmCTestGITVersion(1, 8, 1, 0)) { if (this->GetGitVersion() < cmCTestGITVersion(1, 8, 1, 0)) {
support_sync_recursive = false; sync_recursive = nullptr;
// No need to require >= 1.8.1 if there are no submodules. // No need to require >= 1.8.1 if there are no submodules.
if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) { if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
this->Log << "Git < 1.8.1 cannot synchronize submodules recursively\n"; this->Log << "Git < 1.8.1 cannot synchronize submodules recursively\n";
@ -269,39 +274,35 @@ bool cmCTestGIT::UpdateImpl()
std::string init_submodules = std::string init_submodules =
this->CTest->GetCTestConfiguration("GITInitSubmodules"); this->CTest->GetCTestConfiguration("GITInitSubmodules");
if (cmIsOn(init_submodules)) { if (cmIsOn(init_submodules)) {
std::vector<std::string> git_submodule_init = { git, "submodule", "init" }; char const* git_submodule_init[] = { git, "submodule", "init", nullptr };
ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err, ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err,
top_dir); top_dir.c_str());
if (!ret) { if (!ret) {
return false; return false;
} }
} }
std::vector<std::string> git_submodule_sync = { git, "submodule", "sync" }; char const* git_submodule_sync[] = { git, "submodule", "sync",
if (support_sync_recursive) { sync_recursive, nullptr };
git_submodule_sync.push_back(sync_recursive);
}
ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err, ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err,
top_dir); top_dir.c_str());
if (!ret) { if (!ret) {
return false; return false;
} }
std::vector<std::string> git_submodule = { git, "submodule", "update" }; char const* git_submodule[] = { git, "submodule", "update", recursive,
if (support_recursive) { nullptr };
git_submodule.push_back(recursive);
}
return this->RunChild(git_submodule, &submodule_out, &submodule_err, return this->RunChild(git_submodule, &submodule_out, &submodule_err,
top_dir); top_dir.c_str());
} }
unsigned int cmCTestGIT::GetGitVersion() unsigned int cmCTestGIT::GetGitVersion()
{ {
if (!this->CurrentGitVersion) { if (!this->CurrentGitVersion) {
std::string git = this->CommandLineTool; const char* git = this->CommandLineTool.c_str();
std::vector<std::string> git_version = { git, "--version" }; char const* git_version[] = { git, "--version", nullptr };
std::string version; std::string version;
OneLineParser version_out(this, "version-out> ", version); OneLineParser version_out(this, "version-out> ", version);
OutputLogger version_err(this->Log, "version-err> "); OutputLogger version_err(this->Log, "version-err> ");
@ -604,49 +605,50 @@ bool cmCTestGIT::LoadRevisions()
{ {
// Use 'git rev-list ... | git diff-tree ...' to get revisions. // Use 'git rev-list ... | git diff-tree ...' to get revisions.
std::string range = this->OldRevision + ".." + this->NewRevision; std::string range = this->OldRevision + ".." + this->NewRevision;
std::string git = this->CommandLineTool; const char* git = this->CommandLineTool.c_str();
std::vector<std::string> git_rev_list = { git, "rev-list", "--reverse", const char* git_rev_list[] = { git, "rev-list", "--reverse",
range, "--" }; range.c_str(), "--", nullptr };
std::vector<std::string> git_diff_tree = { const char* git_diff_tree[] = {
git, "diff-tree", "--stdin", "--always", git, "diff-tree", "--stdin", "--always", "-z",
"-z", "-r", "--pretty=raw", "--encoding=utf-8" "-r", "--pretty=raw", "--encoding=utf-8", nullptr
}; };
this->Log << cmCTestGIT::ComputeCommandLine(git_rev_list) << " | " this->Log << cmCTestGIT::ComputeCommandLine(git_rev_list) << " | "
<< cmCTestGIT::ComputeCommandLine(git_diff_tree) << "\n"; << cmCTestGIT::ComputeCommandLine(git_diff_tree) << "\n";
cmUVProcessChainBuilder builder; cmsysProcess* cp = cmsysProcess_New();
builder.AddCommand(git_rev_list) cmsysProcess_AddCommand(cp, git_rev_list);
.AddCommand(git_diff_tree) cmsysProcess_AddCommand(cp, git_diff_tree);
.SetWorkingDirectory(this->SourceDirectory); cmsysProcess_SetWorkingDirectory(cp, this->SourceDirectory.c_str());
CommitParser out(this, "dt-out> "); CommitParser out(this, "dt-out> ");
OutputLogger err(this->Log, "dt-err> "); OutputLogger err(this->Log, "dt-err> ");
cmCTestGIT::RunProcess(builder, &out, &err, cmProcessOutput::UTF8); cmCTestGIT::RunProcess(cp, &out, &err, cmProcessOutput::UTF8);
// Send one extra zero-byte to terminate the last record. // Send one extra zero-byte to terminate the last record.
out.Process("", 1); out.Process("", 1);
cmsysProcess_Delete(cp);
return true; return true;
} }
bool cmCTestGIT::LoadModifications() bool cmCTestGIT::LoadModifications()
{ {
std::string git = this->CommandLineTool; const char* git = this->CommandLineTool.c_str();
// Use 'git update-index' to refresh the index w.r.t. the work tree. // Use 'git update-index' to refresh the index w.r.t. the work tree.
std::vector<std::string> git_update_index = { git, "update-index", const char* git_update_index[] = { git, "update-index", "--refresh",
"--refresh" }; nullptr };
OutputLogger ui_out(this->Log, "ui-out> "); OutputLogger ui_out(this->Log, "ui-out> ");
OutputLogger ui_err(this->Log, "ui-err> "); OutputLogger ui_err(this->Log, "ui-err> ");
this->RunChild(git_update_index, &ui_out, &ui_err, "", this->RunChild(git_update_index, &ui_out, &ui_err, nullptr,
cmProcessOutput::UTF8); cmProcessOutput::UTF8);
// Use 'git diff-index' to get modified files. // Use 'git diff-index' to get modified files.
std::vector<std::string> git_diff_index = { git, "diff-index", "-z", "HEAD", const char* git_diff_index[] = { git, "diff-index", "-z",
"--" }; "HEAD", "--", nullptr };
DiffParser out(this, "di-out> "); DiffParser out(this, "di-out> ");
OutputLogger err(this->Log, "di-err> "); OutputLogger err(this->Log, "di-err> ");
this->RunChild(git_diff_index, &out, &err, "", cmProcessOutput::UTF8); this->RunChild(git_diff_index, &out, &err, nullptr, cmProcessOutput::UTF8);
for (Change const& c : out.Changes) { for (Change const& c : out.Changes) {
this->DoModification(PathModified, c.Path); this->DoModification(PathModified, c.Path);

@ -95,8 +95,8 @@ private:
std::string cmCTestHG::GetWorkingRevision() std::string cmCTestHG::GetWorkingRevision()
{ {
// Run plumbing "hg identify" to get work tree revision. // Run plumbing "hg identify" to get work tree revision.
std::string hg = this->CommandLineTool; const char* hg = this->CommandLineTool.c_str();
std::vector<std::string> hg_identify = { hg, "identify", "-i" }; const char* hg_identify[] = { hg, "identify", "-i", nullptr };
std::string rev; std::string rev;
IdentifyParser out(this, "rev-out> ", rev); IdentifyParser out(this, "rev-out> ", rev);
OutputLogger err(this->Log, "rev-err> "); OutputLogger err(this->Log, "rev-err> ");
@ -127,16 +127,16 @@ bool cmCTestHG::UpdateImpl()
{ {
// Use "hg pull" followed by "hg update" to update the working tree. // Use "hg pull" followed by "hg update" to update the working tree.
{ {
std::string hg = this->CommandLineTool; const char* hg = this->CommandLineTool.c_str();
std::vector<std::string> hg_pull = { hg, "pull", "-v" }; const char* hg_pull[] = { hg, "pull", "-v", nullptr };
OutputLogger out(this->Log, "pull-out> "); OutputLogger out(this->Log, "pull-out> ");
OutputLogger err(this->Log, "pull-err> "); OutputLogger err(this->Log, "pull-err> ");
this->RunChild(hg_pull, &out, &err); this->RunChild(&hg_pull[0], &out, &err);
} }
// TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
std::vector<std::string> hg_update; std::vector<char const*> hg_update;
hg_update.push_back(this->CommandLineTool.c_str()); hg_update.push_back(this->CommandLineTool.c_str());
hg_update.push_back("update"); hg_update.push_back("update");
hg_update.push_back("-v"); hg_update.push_back("-v");
@ -147,11 +147,16 @@ bool cmCTestHG::UpdateImpl()
opts = this->CTest->GetCTestConfiguration("HGUpdateOptions"); opts = this->CTest->GetCTestConfiguration("HGUpdateOptions");
} }
std::vector<std::string> args = cmSystemTools::ParseArguments(opts); std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
cm::append(hg_update, args); for (std::string const& arg : args) {
hg_update.push_back(arg.c_str());
}
// Sentinel argument.
hg_update.push_back(nullptr);
OutputLogger out(this->Log, "update-out> "); OutputLogger out(this->Log, "update-out> ");
OutputLogger err(this->Log, "update-err> "); OutputLogger err(this->Log, "update-err> ");
return this->RunUpdateCommand(hg_update, &out, &err); return this->RunUpdateCommand(hg_update.data(), &out, &err);
} }
class cmCTestHG::LogParser class cmCTestHG::LogParser
@ -272,8 +277,8 @@ bool cmCTestHG::LoadRevisions()
// the project has spaces in the path. Also, they may not have // the project has spaces in the path. Also, they may not have
// proper XML escapes. // proper XML escapes.
std::string range = this->OldRevision + ":" + this->NewRevision; std::string range = this->OldRevision + ":" + this->NewRevision;
std::string hg = this->CommandLineTool; const char* hg = this->CommandLineTool.c_str();
std::string hgXMLTemplate = "<logentry\n" const char* hgXMLTemplate = "<logentry\n"
" revision=\"{node|short}\">\n" " revision=\"{node|short}\">\n"
" <author>{author|person}</author>\n" " <author>{author|person}</author>\n"
" <email>{author|email}</email>\n" " <email>{author|email}</email>\n"
@ -283,8 +288,10 @@ bool cmCTestHG::LoadRevisions()
" <file_adds>{file_adds}</file_adds>\n" " <file_adds>{file_adds}</file_adds>\n"
" <file_dels>{file_dels}</file_dels>\n" " <file_dels>{file_dels}</file_dels>\n"
"</logentry>\n"; "</logentry>\n";
std::vector<std::string> hg_log = { hg, "log", "--removed", "-r", const char* hg_log[] = {
range, "--template", hgXMLTemplate }; hg, "log", "--removed", "-r", range.c_str(),
"--template", hgXMLTemplate, nullptr
};
LogParser out(this, "log-out> "); LogParser out(this, "log-out> ");
out.Process("<?xml version=\"1.0\"?>\n" out.Process("<?xml version=\"1.0\"?>\n"
@ -298,8 +305,8 @@ bool cmCTestHG::LoadRevisions()
bool cmCTestHG::LoadModifications() bool cmCTestHG::LoadModifications()
{ {
// Use 'hg status' to get modified files. // Use 'hg status' to get modified files.
std::string hg = this->CommandLineTool; const char* hg = this->CommandLineTool.c_str();
std::vector<std::string> hg_status = { hg, "status" }; const char* hg_status[] = { hg, "status", nullptr };
StatusParser out(this, "status-out> "); StatusParser out(this, "status-out> ");
OutputLogger err(this->Log, "status-err> "); OutputLogger err(this->Log, "status-err> ");
this->RunChild(hg_status, &out, &err); this->RunChild(hg_status, &out, &err);

@ -2,19 +2,13 @@
file Copyright.txt or https://cmake.org/licensing for details. */ file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCTestLaunch.h" #include "cmCTestLaunch.h"
#include <cstdio>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <memory>
#include <utility>
#include <cm3p/uv.h>
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cm_fileno.hxx"
#include "cmCTestLaunchReporter.h" #include "cmCTestLaunchReporter.h"
#include "cmGlobalGenerator.h" #include "cmGlobalGenerator.h"
#include "cmMakefile.h" #include "cmMakefile.h"
@ -23,9 +17,6 @@
#include "cmStateSnapshot.h" #include "cmStateSnapshot.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVHandlePtr.h"
#include "cmUVProcessChain.h"
#include "cmUVStream.h"
#include "cmake.h" #include "cmake.h"
#ifdef _WIN32 #ifdef _WIN32
@ -37,6 +28,8 @@
cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
{ {
this->Process = nullptr;
if (!this->ParseArguments(argc, argv)) { if (!this->ParseArguments(argc, argv)) {
return; return;
} }
@ -47,9 +40,13 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
this->ScrapeRulesLoaded = false; this->ScrapeRulesLoaded = false;
this->HaveOut = false; this->HaveOut = false;
this->HaveErr = false; this->HaveErr = false;
this->Process = cmsysProcess_New();
} }
cmCTestLaunch::~cmCTestLaunch() = default; cmCTestLaunch::~cmCTestLaunch()
{
cmsysProcess_Delete(this->Process);
}
bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv)
{ {
@ -116,12 +113,15 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv)
// Extract the real command line. // Extract the real command line.
if (arg0) { if (arg0) {
for (int i = 0; i < argc - arg0; ++i) { this->RealArgC = argc - arg0;
this->RealArgV.emplace_back((argv + arg0)[i]); this->RealArgV = argv + arg0;
this->HandleRealArg((argv + arg0)[i]); for (int i = 0; i < this->RealArgC; ++i) {
this->HandleRealArg(this->RealArgV[i]);
} }
return true; return true;
} }
this->RealArgC = 0;
this->RealArgV = nullptr;
std::cerr << "No launch/command separator ('--') found!\n"; std::cerr << "No launch/command separator ('--') found!\n";
return false; return false;
} }
@ -151,22 +151,17 @@ void cmCTestLaunch::RunChild()
} }
// Prepare to run the real command. // Prepare to run the real command.
cmUVProcessChainBuilder builder; cmsysProcess* cp = this->Process;
builder.AddCommand(this->RealArgV); cmsysProcess_SetCommand(cp, this->RealArgV);
cmsys::ofstream fout; cmsys::ofstream fout;
cmsys::ofstream ferr; cmsys::ofstream ferr;
if (this->Reporter.Passthru) { if (this->Reporter.Passthru) {
// In passthru mode we just share the output pipes. // In passthru mode we just share the output pipes.
builder cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
cm_fileno(stdout))
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
cm_fileno(stderr));
} else { } else {
// In full mode we record the child output pipes to log files. // In full mode we record the child output pipes to log files.
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
fout.open(this->Reporter.LogOut.c_str(), std::ios::out | std::ios::binary); fout.open(this->Reporter.LogOut.c_str(), std::ios::out | std::ios::binary);
ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary); ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary);
} }
@ -179,65 +174,51 @@ void cmCTestLaunch::RunChild()
#endif #endif
// Run the real command. // Run the real command.
auto chain = builder.Start(); cmsysProcess_Execute(cp);
// Record child stdout and stderr if necessary. // Record child stdout and stderr if necessary.
cm::uv_pipe_ptr outPipe;
cm::uv_pipe_ptr errPipe;
bool outFinished = true;
bool errFinished = true;
cmProcessOutput processOutput;
std::unique_ptr<cmUVStreamReadHandle> outputHandle;
std::unique_ptr<cmUVStreamReadHandle> errorHandle;
if (!this->Reporter.Passthru) { if (!this->Reporter.Passthru) {
auto beginRead = [&chain, &processOutput]( char* data = nullptr;
cm::uv_pipe_ptr& pipe, int stream, std::ostream& out, int length = 0;
cmsys::ofstream& file, bool& haveData, bool& finished, cmProcessOutput processOutput;
int id) -> std::unique_ptr<cmUVStreamReadHandle> {
pipe.init(chain.GetLoop(), 0);
uv_pipe_open(pipe, stream);
finished = false;
return cmUVStreamRead(
pipe,
[&processOutput, &out, &file, id, &haveData](std::vector<char> data) {
std::string strdata;
processOutput.DecodeText(data.data(), data.size(), strdata, id);
file.write(strdata.c_str(), strdata.size());
out.write(strdata.c_str(), strdata.size());
haveData = true;
},
[&processOutput, &out, &file, &finished, id]() {
std::string strdata; std::string strdata;
processOutput.DecodeText(std::string(), strdata, id); while (int p = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
if (p == cmsysProcess_Pipe_STDOUT) {
processOutput.DecodeText(data, length, strdata, 1);
fout.write(strdata.c_str(), strdata.size());
std::cout.write(strdata.c_str(), strdata.size());
this->HaveOut = true;
} else if (p == cmsysProcess_Pipe_STDERR) {
processOutput.DecodeText(data, length, strdata, 2);
ferr.write(strdata.c_str(), strdata.size());
std::cerr.write(strdata.c_str(), strdata.size());
this->HaveErr = true;
}
}
processOutput.DecodeText(std::string(), strdata, 1);
if (!strdata.empty()) { if (!strdata.empty()) {
file.write(strdata.c_str(), strdata.size()); fout.write(strdata.c_str(), strdata.size());
out.write(strdata.c_str(), strdata.size()); std::cout.write(strdata.c_str(), strdata.size());
}
processOutput.DecodeText(std::string(), strdata, 2);
if (!strdata.empty()) {
ferr.write(strdata.c_str(), strdata.size());
std::cerr.write(strdata.c_str(), strdata.size());
} }
finished = true;
});
};
outputHandle = beginRead(outPipe, chain.OutputStream(), std::cout, fout,
this->HaveOut, outFinished, 1);
errorHandle = beginRead(errPipe, chain.ErrorStream(), std::cerr, ferr,
this->HaveErr, errFinished, 2);
} }
// Wait for the real command to finish. // Wait for the real command to finish.
while (!(chain.Finished() && outFinished && errFinished)) { cmsysProcess_WaitForExit(cp, nullptr);
uv_run(&chain.GetLoop(), UV_RUN_ONCE); this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp);
}
this->Reporter.Status = chain.GetStatus(0);
if (this->Reporter.Status.GetException().first ==
cmUVProcessChain::ExceptionCode::Spawn) {
this->Reporter.ExitCode = 1;
} else {
this->Reporter.ExitCode =
static_cast<int>(this->Reporter.Status.ExitStatus);
}
} }
int cmCTestLaunch::Run() int cmCTestLaunch::Run()
{ {
if (!this->Process) {
std::cerr << "Could not allocate cmsysProcess instance!\n";
return -1;
}
this->RunChild(); this->RunChild();
if (this->CheckResults()) { if (this->CheckResults()) {
@ -245,6 +226,7 @@ int cmCTestLaunch::Run()
} }
this->LoadConfig(); this->LoadConfig();
this->Reporter.Process = this->Process;
this->Reporter.WriteXML(); this->Reporter.WriteXML();
return this->Reporter.ExitCode; return this->Reporter.ExitCode;

@ -43,12 +43,15 @@ private:
bool ParseArguments(int argc, const char* const* argv); bool ParseArguments(int argc, const char* const* argv);
// The real command line appearing after launcher arguments. // The real command line appearing after launcher arguments.
std::vector<std::string> RealArgV; int RealArgC;
const char* const* RealArgV;
// The real command line after response file expansion. // The real command line after response file expansion.
std::vector<std::string> RealArgs; std::vector<std::string> RealArgs;
void HandleRealArg(const char* arg); void HandleRealArg(const char* arg);
struct cmsysProcess_s* Process;
// Whether or not any data have been written to stdout or stderr. // Whether or not any data have been written to stdout or stderr.
bool HaveOut; bool HaveOut;
bool HaveErr; bool HaveErr;

@ -2,9 +2,8 @@
file Copyright.txt or https://cmake.org/licensing for details. */ file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCTestLaunchReporter.h" #include "cmCTestLaunchReporter.h"
#include <utility>
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cmCryptoHash.h" #include "cmCryptoHash.h"
@ -23,7 +22,6 @@
cmCTestLaunchReporter::cmCTestLaunchReporter() cmCTestLaunchReporter::cmCTestLaunchReporter()
{ {
this->Passthru = true; this->Passthru = true;
this->Status.Finished = true;
this->ExitCode = 1; this->ExitCode = 1;
this->CWD = cmSystemTools::GetCurrentWorkingDirectory(); this->CWD = cmSystemTools::GetCurrentWorkingDirectory();
@ -233,23 +231,35 @@ void cmCTestLaunchReporter::WriteXMLResult(cmXMLElement& e2)
// ExitCondition // ExitCondition
cmXMLElement e4(e3, "ExitCondition"); cmXMLElement e4(e3, "ExitCondition");
if (this->Status.Finished) { cmsysProcess* cp = this->Process;
auto exception = this->Status.GetException(); switch (cmsysProcess_GetState(cp)) {
switch (exception.first) { case cmsysProcess_State_Starting:
case cmUVProcessChain::ExceptionCode::None: e4.Content("No process has been executed");
e4.Content(this->ExitCode);
break; break;
case cmUVProcessChain::ExceptionCode::Spawn: case cmsysProcess_State_Executing:
e4.Content("Error administrating child process: "); e4.Content("The process is still executing");
e4.Content(exception.second);
break; break;
default: case cmsysProcess_State_Disowned:
e4.Content("Terminated abnormally: "); e4.Content("Disowned");
e4.Content(exception.second);
break; break;
} case cmsysProcess_State_Killed:
} else { e4.Content("Killed by parent");
break;
case cmsysProcess_State_Expired:
e4.Content("Killed when timeout expired"); e4.Content("Killed when timeout expired");
break;
case cmsysProcess_State_Exited:
e4.Content(this->ExitCode);
break;
case cmsysProcess_State_Exception:
e4.Content("Terminated abnormally: ");
e4.Content(cmsysProcess_GetExceptionString(cp));
break;
case cmsysProcess_State_Error:
e4.Content("Error administrating child process: ");
e4.Content(cmsysProcess_GetErrorString(cp));
break;
} }
} }

@ -10,8 +10,6 @@
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cmUVProcessChain.h"
class cmXMLElement; class cmXMLElement;
/** \class cmCTestLaunchReporter /** \class cmCTestLaunchReporter
@ -50,7 +48,7 @@ public:
void ComputeFileNames(); void ComputeFileNames();
bool Passthru; bool Passthru;
cmUVProcessChain::Status Status; struct cmsysProcess_s* Process;
int ExitCode; int ExitCode;
// Temporary log files for stdout and stderr of real command. // Temporary log files for stdout and stderr of real command.

@ -149,16 +149,17 @@ cmCTestP4::User cmCTestP4::GetUserData(const std::string& username)
auto it = this->Users.find(username); auto it = this->Users.find(username);
if (it == this->Users.end()) { if (it == this->Users.end()) {
std::vector<std::string> p4_users; std::vector<char const*> p4_users;
this->SetP4Options(p4_users); this->SetP4Options(p4_users);
p4_users.push_back("users"); p4_users.push_back("users");
p4_users.push_back("-m"); p4_users.push_back("-m");
p4_users.push_back("1"); p4_users.push_back("1");
p4_users.push_back(username); p4_users.push_back(username.c_str());
p4_users.push_back(nullptr);
UserParser out(this, "users-out> "); UserParser out(this, "users-out> ");
OutputLogger err(this->Log, "users-err> "); OutputLogger err(this->Log, "users-err> ");
this->RunChild(p4_users, &out, &err); this->RunChild(p4_users.data(), &out, &err);
// The user should now be added to the map. Search again. // The user should now be added to the map. Search again.
it = this->Users.find(username); it = this->Users.find(username);
@ -302,10 +303,10 @@ private:
} }
}; };
void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions) void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions)
{ {
if (this->P4Options.empty()) { if (this->P4Options.empty()) {
std::string p4 = this->CommandLineTool; const char* p4 = this->CommandLineTool.c_str();
this->P4Options.emplace_back(p4); this->P4Options.emplace_back(p4);
// The CTEST_P4_CLIENT variable sets the P4 client used when issuing // The CTEST_P4_CLIENT variable sets the P4 client used when issuing
@ -327,12 +328,15 @@ void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions)
cm::append(this->P4Options, cmSystemTools::ParseArguments(opts)); cm::append(this->P4Options, cmSystemTools::ParseArguments(opts));
} }
CommandOptions = this->P4Options; CommandOptions.clear();
for (std::string const& o : this->P4Options) {
CommandOptions.push_back(o.c_str());
}
} }
std::string cmCTestP4::GetWorkingRevision() std::string cmCTestP4::GetWorkingRevision()
{ {
std::vector<std::string> p4_identify; std::vector<char const*> p4_identify;
this->SetP4Options(p4_identify); this->SetP4Options(p4_identify);
p4_identify.push_back("changes"); p4_identify.push_back("changes");
@ -341,13 +345,14 @@ std::string cmCTestP4::GetWorkingRevision()
p4_identify.push_back("-t"); p4_identify.push_back("-t");
std::string source = this->SourceDirectory + "/...#have"; std::string source = this->SourceDirectory + "/...#have";
p4_identify.push_back(source); p4_identify.push_back(source.c_str());
p4_identify.push_back(nullptr);
std::string rev; std::string rev;
IdentifyParser out(this, "p4_changes-out> ", rev); IdentifyParser out(this, "p4_changes-out> ", rev);
OutputLogger err(this->Log, "p4_changes-err> "); OutputLogger err(this->Log, "p4_changes-err> ");
bool result = this->RunChild(p4_identify, &out, &err); bool result = this->RunChild(p4_identify.data(), &out, &err);
// If there was a problem contacting the server return "<unknown>" // If there was a problem contacting the server return "<unknown>"
if (!result) { if (!result) {
@ -383,7 +388,7 @@ bool cmCTestP4::NoteNewRevision()
bool cmCTestP4::LoadRevisions() bool cmCTestP4::LoadRevisions()
{ {
std::vector<std::string> p4_changes; std::vector<char const*> p4_changes;
this->SetP4Options(p4_changes); this->SetP4Options(p4_changes);
// Use 'p4 changes ...@old,new' to get a list of changelists // Use 'p4 changes ...@old,new' to get a list of changelists
@ -404,36 +409,38 @@ bool cmCTestP4::LoadRevisions()
.append(this->NewRevision); .append(this->NewRevision);
p4_changes.push_back("changes"); p4_changes.push_back("changes");
p4_changes.push_back(range); p4_changes.push_back(range.c_str());
p4_changes.push_back(nullptr);
ChangesParser out(this, "p4_changes-out> "); ChangesParser out(this, "p4_changes-out> ");
OutputLogger err(this->Log, "p4_changes-err> "); OutputLogger err(this->Log, "p4_changes-err> ");
this->ChangeLists.clear(); this->ChangeLists.clear();
this->RunChild(p4_changes, &out, &err); this->RunChild(p4_changes.data(), &out, &err);
if (this->ChangeLists.empty()) { if (this->ChangeLists.empty()) {
return true; return true;
} }
// p4 describe -s ...@1111111,2222222 // p4 describe -s ...@1111111,2222222
std::vector<std::string> p4_describe; std::vector<char const*> p4_describe;
for (std::string const& i : cmReverseRange(this->ChangeLists)) { for (std::string const& i : cmReverseRange(this->ChangeLists)) {
this->SetP4Options(p4_describe); this->SetP4Options(p4_describe);
p4_describe.push_back("describe"); p4_describe.push_back("describe");
p4_describe.push_back("-s"); p4_describe.push_back("-s");
p4_describe.push_back(i); p4_describe.push_back(i.c_str());
p4_describe.push_back(nullptr);
DescribeParser outDescribe(this, "p4_describe-out> "); DescribeParser outDescribe(this, "p4_describe-out> ");
OutputLogger errDescribe(this->Log, "p4_describe-err> "); OutputLogger errDescribe(this->Log, "p4_describe-err> ");
this->RunChild(p4_describe, &outDescribe, &errDescribe); this->RunChild(p4_describe.data(), &outDescribe, &errDescribe);
} }
return true; return true;
} }
bool cmCTestP4::LoadModifications() bool cmCTestP4::LoadModifications()
{ {
std::vector<std::string> p4_diff; std::vector<char const*> p4_diff;
this->SetP4Options(p4_diff); this->SetP4Options(p4_diff);
p4_diff.push_back("diff"); p4_diff.push_back("diff");
@ -441,11 +448,12 @@ bool cmCTestP4::LoadModifications()
// Ideally we would use -Od but not all clients support it // Ideally we would use -Od but not all clients support it
p4_diff.push_back("-dn"); p4_diff.push_back("-dn");
std::string source = this->SourceDirectory + "/..."; std::string source = this->SourceDirectory + "/...";
p4_diff.push_back(source); p4_diff.push_back(source.c_str());
p4_diff.push_back(nullptr);
DiffParser out(this, "p4_diff-out> "); DiffParser out(this, "p4_diff-out> ");
OutputLogger err(this->Log, "p4_diff-err> "); OutputLogger err(this->Log, "p4_diff-err> ");
this->RunChild(p4_diff, &out, &err); this->RunChild(p4_diff.data(), &out, &err);
return true; return true;
} }
@ -453,14 +461,17 @@ bool cmCTestP4::UpdateCustom(const std::string& custom)
{ {
cmList p4_custom_command{ custom, cmList::EmptyElements::Yes }; cmList p4_custom_command{ custom, cmList::EmptyElements::Yes };
std::vector<std::string> p4_custom; std::vector<char const*> p4_custom;
p4_custom.reserve(p4_custom_command.size()); p4_custom.reserve(p4_custom_command.size() + 1);
cm::append(p4_custom, p4_custom_command); for (std::string const& i : p4_custom_command) {
p4_custom.push_back(i.c_str());
}
p4_custom.push_back(nullptr);
OutputLogger custom_out(this->Log, "p4_customsync-out> "); OutputLogger custom_out(this->Log, "p4_customsync-out> ");
OutputLogger custom_err(this->Log, "p4_customsync-err> "); OutputLogger custom_err(this->Log, "p4_customsync-err> ");
return this->RunUpdateCommand(p4_custom, &custom_out, &custom_err); return this->RunUpdateCommand(p4_custom.data(), &custom_out, &custom_err);
} }
bool cmCTestP4::UpdateImpl() bool cmCTestP4::UpdateImpl()
@ -477,7 +488,7 @@ bool cmCTestP4::UpdateImpl()
return false; return false;
} }
std::vector<std::string> p4_sync; std::vector<char const*> p4_sync;
this->SetP4Options(p4_sync); this->SetP4Options(p4_sync);
p4_sync.push_back("sync"); p4_sync.push_back("sync");
@ -488,7 +499,9 @@ bool cmCTestP4::UpdateImpl()
opts = this->CTest->GetCTestConfiguration("P4UpdateOptions"); opts = this->CTest->GetCTestConfiguration("P4UpdateOptions");
} }
std::vector<std::string> args = cmSystemTools::ParseArguments(opts); std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
cm::append(p4_sync, args); for (std::string const& arg : args) {
p4_sync.push_back(arg.c_str());
}
std::string source = this->SourceDirectory + "/..."; std::string source = this->SourceDirectory + "/...";
@ -502,10 +515,11 @@ bool cmCTestP4::UpdateImpl()
source.append("@\"").append(date).append("\""); source.append("@\"").append(date).append("\"");
} }
p4_sync.push_back(source); p4_sync.push_back(source.c_str());
p4_sync.push_back(nullptr);
OutputLogger out(this->Log, "p4_sync-out> "); OutputLogger out(this->Log, "p4_sync-out> ");
OutputLogger err(this->Log, "p4_sync-err> "); OutputLogger err(this->Log, "p4_sync-err> ");
return this->RunUpdateCommand(p4_sync, &out, &err); return this->RunUpdateCommand(p4_sync.data(), &out, &err);
} }

@ -39,7 +39,7 @@ private:
std::vector<std::string> P4Options; std::vector<std::string> P4Options;
User GetUserData(const std::string& username); User GetUserData(const std::string& username);
void SetP4Options(std::vector<std::string>& options); void SetP4Options(std::vector<char const*>& options);
std::string GetWorkingRevision(); std::string GetWorkingRevision();
bool NoteOldRevision() override; bool NoteOldRevision() override;

@ -33,7 +33,7 @@ cmCTestSVN::~cmCTestSVN() = default;
void cmCTestSVN::CleanupImpl() void cmCTestSVN::CleanupImpl()
{ {
std::vector<std::string> svn_cleanup; std::vector<const char*> svn_cleanup;
svn_cleanup.push_back("cleanup"); svn_cleanup.push_back("cleanup");
OutputLogger out(this->Log, "cleanup-out> "); OutputLogger out(this->Log, "cleanup-out> ");
OutputLogger err(this->Log, "cleanup-err> "); OutputLogger err(this->Log, "cleanup-err> ");
@ -88,9 +88,9 @@ static bool cmCTestSVNPathStarts(std::string const& p1, std::string const& p2)
std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo) std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo)
{ {
// Run "svn info" to get the repository info from the work tree. // Run "svn info" to get the repository info from the work tree.
std::vector<std::string> svn_info; std::vector<const char*> svn_info;
svn_info.push_back("info"); svn_info.push_back("info");
svn_info.push_back(svninfo.LocalPath); svn_info.push_back(svninfo.LocalPath.c_str());
std::string rev; std::string rev;
InfoParser out(this, "info-out> ", rev, svninfo); InfoParser out(this, "info-out> ", rev, svninfo);
OutputLogger err(this->Log, "info-err> "); OutputLogger err(this->Log, "info-err> ");
@ -251,24 +251,26 @@ bool cmCTestSVN::UpdateImpl()
args.push_back("-r{" + this->GetNightlyTime() + " +0000}"); args.push_back("-r{" + this->GetNightlyTime() + " +0000}");
} }
std::vector<std::string> svn_update; std::vector<char const*> svn_update;
svn_update.push_back("update"); svn_update.push_back("update");
cm::append(svn_update, args); for (std::string const& arg : args) {
svn_update.push_back(arg.c_str());
}
UpdateParser out(this, "up-out> "); UpdateParser out(this, "up-out> ");
OutputLogger err(this->Log, "up-err> "); OutputLogger err(this->Log, "up-err> ");
return this->RunSVNCommand(svn_update, &out, &err); return this->RunSVNCommand(svn_update, &out, &err);
} }
bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters, bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters,
OutputParser* out, OutputParser* err) OutputParser* out, OutputParser* err)
{ {
if (parameters.empty()) { if (parameters.empty()) {
return false; return false;
} }
std::vector<std::string> args; std::vector<char const*> args;
args.push_back(this->CommandLineTool); args.push_back(this->CommandLineTool.c_str());
cm::append(args, parameters); cm::append(args, parameters);
args.push_back("--non-interactive"); args.push_back("--non-interactive");
@ -276,12 +278,16 @@ bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters,
std::vector<std::string> parsedUserOptions = std::vector<std::string> parsedUserOptions =
cmSystemTools::ParseArguments(userOptions); cmSystemTools::ParseArguments(userOptions);
cm::append(args, parsedUserOptions); for (std::string const& opt : parsedUserOptions) {
args.push_back(opt.c_str());
}
args.push_back(nullptr);
if (parameters[0] == "update") { if (strcmp(parameters[0], "update") == 0) {
return this->RunUpdateCommand(args, out, err); return this->RunUpdateCommand(args.data(), out, err);
} }
return this->RunChild(args, out, err); return this->RunChild(args.data(), out, err);
} }
class cmCTestSVN::LogParser class cmCTestSVN::LogParser
@ -387,7 +393,7 @@ bool cmCTestSVN::LoadRevisions(SVNInfo& svninfo)
} }
// Run "svn log" to get all global revisions of interest. // Run "svn log" to get all global revisions of interest.
std::vector<std::string> svn_log; std::vector<const char*> svn_log;
svn_log.push_back("log"); svn_log.push_back("log");
svn_log.push_back("--xml"); svn_log.push_back("--xml");
svn_log.push_back("-v"); svn_log.push_back("-v");
@ -466,7 +472,7 @@ private:
bool cmCTestSVN::LoadModifications() bool cmCTestSVN::LoadModifications()
{ {
// Run "svn status" which reports local modifications. // Run "svn status" which reports local modifications.
std::vector<std::string> svn_status; std::vector<const char*> svn_status;
svn_status.push_back("status"); svn_status.push_back("status");
StatusParser out(this, "status-out> "); StatusParser out(this, "status-out> ");
OutputLogger err(this->Log, "status-err> "); OutputLogger err(this->Log, "status-err> ");
@ -528,7 +534,7 @@ bool cmCTestSVN::LoadRepositories()
this->RootInfo = &(this->Repositories.back()); this->RootInfo = &(this->Repositories.back());
// Run "svn status" to get the list of external repositories // Run "svn status" to get the list of external repositories
std::vector<std::string> svn_status; std::vector<const char*> svn_status;
svn_status.push_back("status"); svn_status.push_back("status");
ExternalParser out(this, "external-out> "); ExternalParser out(this, "external-out> ");
OutputLogger err(this->Log, "external-err> "); OutputLogger err(this->Log, "external-err> ");

@ -33,7 +33,7 @@ private:
bool NoteNewRevision() override; bool NoteNewRevision() override;
bool UpdateImpl() override; bool UpdateImpl() override;
bool RunSVNCommand(std::vector<std::string> const& parameters, bool RunSVNCommand(std::vector<char const*> const& parameters,
OutputParser* out, OutputParser* err); OutputParser* out, OutputParser* err);
// Information about an SVN repository (root repository or external) // Information about an SVN repository (root repository or external)

@ -11,9 +11,8 @@
#include <cm/memory> #include <cm/memory>
#include <cm3p/uv.h>
#include "cmsys/Directory.hxx" #include "cmsys/Directory.hxx"
#include "cmsys/Process.h"
#include "cmCTest.h" #include "cmCTest.h"
#include "cmCTestBuildCommand.h" #include "cmCTestBuildCommand.h"
@ -41,8 +40,6 @@
#include "cmStateSnapshot.h" #include "cmStateSnapshot.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVHandlePtr.h"
#include "cmUVProcessChain.h"
#include "cmValue.h" #include "cmValue.h"
#include "cmake.h" #include "cmake.h"
@ -151,65 +148,66 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg)
// now pass through all the other arguments // now pass through all the other arguments
std::vector<std::string>& initArgs = std::vector<std::string>& initArgs =
this->CTest->GetInitialCommandLineArguments(); this->CTest->GetInitialCommandLineArguments();
//*** need to make sure this does not have the current script ***
for (size_t i = 1; i < initArgs.size(); ++i) {
argv.push_back(initArgs[i].c_str());
}
argv.push_back(nullptr);
// Now create process object // Now create process object
cmUVProcessChainBuilder builder; cmsysProcess* cp = cmsysProcess_New();
builder.AddCommand(initArgs) cmsysProcess_SetCommand(cp, argv.data());
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) // cmsysProcess_SetWorkingDirectory(cp, dir);
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
auto process = builder.Start(); // cmsysProcess_SetTimeout(cp, timeout);
cm::uv_pipe_ptr outPipe; cmsysProcess_Execute(cp);
outPipe.init(process.GetLoop(), 0);
uv_pipe_open(outPipe, process.OutputStream());
cm::uv_pipe_ptr errPipe;
errPipe.init(process.GetLoop(), 0);
uv_pipe_open(errPipe, process.ErrorStream());
std::vector<char> out; std::vector<char> out;
std::vector<char> err; std::vector<char> err;
std::string line; std::string line;
auto pipe = int pipe =
cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line, cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err);
std::chrono::seconds(100), out, err); while (pipe != cmsysProcess_Pipe_None) {
while (pipe != cmSystemTools::WaitForLineResult::None) {
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Output: " << line << "\n"); "Output: " << line << "\n");
if (pipe == cmSystemTools::WaitForLineResult::STDERR) { if (pipe == cmsysProcess_Pipe_STDERR) {
cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n"); cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
} else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) { } else if (pipe == cmsysProcess_Pipe_STDOUT) {
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n"); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n");
} }
pipe = pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out,
cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line, err);
std::chrono::seconds(100), out, err);
} }
// Properly handle output of the build command // Properly handle output of the build command
process.Wait(); cmsysProcess_WaitForExit(cp, nullptr);
auto const& status = process.GetStatus(0); int result = cmsysProcess_GetState(cp);
auto result = status.GetException();
int retVal = 0; int retVal = 0;
bool failed = false; bool failed = false;
switch (result.first) { if (result == cmsysProcess_State_Exited) {
case cmUVProcessChain::ExceptionCode::None: retVal = cmsysProcess_GetExitValue(cp);
retVal = static_cast<int>(status.ExitStatus); } else if (result == cmsysProcess_State_Exception) {
break; retVal = cmsysProcess_GetExitException(cp);
case cmUVProcessChain::ExceptionCode::Spawn: cmCTestLog(this->CTest, ERROR_MESSAGE,
"\tThere was an exception: "
<< cmsysProcess_GetExceptionString(cp) << " " << retVal
<< std::endl);
failed = true;
} else if (result == cmsysProcess_State_Expired) {
cmCTestLog(this->CTest, ERROR_MESSAGE, cmCTestLog(this->CTest, ERROR_MESSAGE,
"\tError executing ctest: " << result.second << std::endl); "\tThere was a timeout" << std::endl);
failed = true; failed = true;
break; } else if (result == cmsysProcess_State_Error) {
default:
retVal = status.TermSignal;
cmCTestLog(this->CTest, ERROR_MESSAGE, cmCTestLog(this->CTest, ERROR_MESSAGE,
"\tThere was an exception: " << result.second << " " << retVal "\tError executing ctest: " << cmsysProcess_GetErrorString(cp)
<< std::endl); << std::endl);
failed = true; failed = true;
} }
cmsysProcess_Delete(cp);
if (failed) { if (failed) {
std::ostringstream message; std::ostringstream message;
message << "Error running command: ["; message << "Error running command: [";
message << static_cast<int>(result.first) << "] "; message << result << "] ";
for (const char* arg : argv) { for (const char* arg : argv) {
if (arg) { if (arg) {
message << arg << " "; message << arg << " ";

@ -7,9 +7,10 @@
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include "cmsys/Process.h"
#include "cmCTest.h" #include "cmCTest.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVProcessChain.h"
#include "cmValue.h" #include "cmValue.h"
#include "cmXMLWriter.h" #include "cmXMLWriter.h"
@ -54,12 +55,18 @@ bool cmCTestVC::InitialCheckout(const std::string& command)
// Construct the initial checkout command line. // Construct the initial checkout command line.
std::vector<std::string> args = cmSystemTools::ParseArguments(command); std::vector<std::string> args = cmSystemTools::ParseArguments(command);
std::vector<char const*> vc_co;
vc_co.reserve(args.size() + 1);
for (std::string const& arg : args) {
vc_co.push_back(arg.c_str());
}
vc_co.push_back(nullptr);
// Run the initial checkout command and log its output. // Run the initial checkout command and log its output.
this->Log << "--- Begin Initial Checkout ---\n"; this->Log << "--- Begin Initial Checkout ---\n";
OutputLogger out(this->Log, "co-out> "); OutputLogger out(this->Log, "co-out> ");
OutputLogger err(this->Log, "co-err> "); OutputLogger err(this->Log, "co-err> ");
bool result = this->RunChild(args, &out, &err, parent); bool result = this->RunChild(vc_co.data(), &out, &err, parent.c_str());
this->Log << "--- End Initial Checkout ---\n"; this->Log << "--- End Initial Checkout ---\n";
if (!result) { if (!result) {
cmCTestLog(this->CTest, ERROR_MESSAGE, cmCTestLog(this->CTest, ERROR_MESSAGE,
@ -68,35 +75,35 @@ bool cmCTestVC::InitialCheckout(const std::string& command)
return result; return result;
} }
bool cmCTestVC::RunChild(const std::vector<std::string>& cmd, bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out,
OutputParser* out, OutputParser* err, OutputParser* err, const char* workDir,
std::string workDir, Encoding encoding) Encoding encoding)
{ {
this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n"; this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n";
cmUVProcessChainBuilder builder; cmsysProcess* cp = cmsysProcess_New();
if (workDir.empty()) { cmsysProcess_SetCommand(cp, cmd);
workDir = this->SourceDirectory; workDir = workDir ? workDir : this->SourceDirectory.c_str();
} cmsysProcess_SetWorkingDirectory(cp, workDir);
builder.AddCommand(cmd).SetWorkingDirectory(workDir); cmCTestVC::RunProcess(cp, out, err, encoding);
auto status = cmCTestVC::RunProcess(builder, out, err, encoding); int result = cmsysProcess_GetExitValue(cp);
return status.front().SpawnResult == 0 && status.front().ExitStatus == 0; cmsysProcess_Delete(cp);
return result == 0;
} }
std::string cmCTestVC::ComputeCommandLine(const std::vector<std::string>& cmd) std::string cmCTestVC::ComputeCommandLine(char const* const* cmd)
{ {
std::ostringstream line; std::ostringstream line;
const char* sep = ""; const char* sep = "";
for (auto const& arg : cmd) { for (const char* const* arg = cmd; *arg; ++arg) {
line << sep << "\"" << arg << "\""; line << sep << "\"" << *arg << "\"";
sep = " "; sep = " ";
} }
return line.str(); return line.str();
} }
bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd, bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out,
OutputParser* out, OutputParser* err, OutputParser* err, Encoding encoding)
Encoding encoding)
{ {
// Report the command line. // Report the command line.
this->UpdateCommandLine = this->ComputeCommandLine(cmd); this->UpdateCommandLine = this->ComputeCommandLine(cmd);
@ -106,7 +113,7 @@ bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd,
} }
// Run the command. // Run the command.
return this->RunChild(cmd, out, err, "", encoding); return this->RunChild(cmd, out, err, nullptr, encoding);
} }
std::string cmCTestVC::GetNightlyTime() std::string cmCTestVC::GetNightlyTime()

@ -6,7 +6,6 @@
#include <iosfwd> #include <iosfwd>
#include <string> #include <string>
#include <vector>
#include "cmProcessOutput.h" #include "cmProcessOutput.h"
#include "cmProcessTools.h" #include "cmProcessTools.h"
@ -109,15 +108,15 @@ protected:
}; };
/** Convert a list of arguments to a human-readable command line. */ /** Convert a list of arguments to a human-readable command line. */
static std::string ComputeCommandLine(const std::vector<std::string>& cmd); static std::string ComputeCommandLine(char const* const* cmd);
/** Run a command line and send output to given parsers. */ /** Run a command line and send output to given parsers. */
bool RunChild(const std::vector<std::string>& cmd, OutputParser* out, bool RunChild(char const* const* cmd, OutputParser* out, OutputParser* err,
OutputParser* err, std::string workDir = {}, const char* workDir = nullptr,
Encoding encoding = cmProcessOutput::Auto); Encoding encoding = cmProcessOutput::Auto);
/** Run VC update command line and send output to given parsers. */ /** Run VC update command line and send output to given parsers. */
bool RunUpdateCommand(const std::vector<std::string>& cmd, OutputParser* out, bool RunUpdateCommand(char const* const* cmd, OutputParser* out,
OutputParser* err = nullptr, OutputParser* err = nullptr,
Encoding encoding = cmProcessOutput::Auto); Encoding encoding = cmProcessOutput::Auto);

@ -667,10 +667,6 @@ Modify cmCTestResourceGroupsLexer.cxx:
#include <cstddef> #include <cstddef>
#ifndef _WIN32
# include <termios.h>
#endif
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
#define INITIAL 0 #define INITIAL 0

@ -26,10 +26,6 @@ Modify cmCTestResourceGroupsLexer.cxx:
#include <cstddef> #include <cstddef>
#ifndef _WIN32
# include <termios.h>
#endif
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
%} %}

@ -5,7 +5,6 @@
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <chrono> #include <chrono>
#include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@ -25,13 +24,13 @@
#include <cmext/string_view> #include <cmext/string_view>
#include <cm3p/curl/curl.h> #include <cm3p/curl/curl.h>
#include <cm3p/uv.h>
#include <cm3p/zlib.h> #include <cm3p/zlib.h>
#include "cmsys/Base64.h" #include "cmsys/Base64.h"
#include "cmsys/Directory.hxx" #include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx" #include "cmsys/Glob.hxx"
#include "cmsys/Process.h"
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cmsys/SystemInformation.hxx" #include "cmsys/SystemInformation.hxx"
#if defined(_WIN32) #if defined(_WIN32)
@ -65,9 +64,6 @@
#include "cmStateTypes.h" #include "cmStateTypes.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVHandlePtr.h"
#include "cmUVProcessChain.h"
#include "cmUVStream.h"
#include "cmValue.h" #include "cmValue.h"
#include "cmVersion.h" #include "cmVersion.h"
#include "cmVersionConfig.h" #include "cmVersionConfig.h"
@ -1077,7 +1073,7 @@ int cmCTest::GetTestModelFromString(const std::string& str)
// ###################################################################### // ######################################################################
// ###################################################################### // ######################################################################
bool cmCTest::RunMakeCommand(const std::string& command, std::string& output, int cmCTest::RunMakeCommand(const std::string& command, std::string& output,
int* retVal, const char* dir, cmDuration timeout, int* retVal, const char* dir, cmDuration timeout,
std::ostream& ofs, Encoding encoding) std::ostream& ofs, Encoding encoding)
{ {
@ -1088,41 +1084,47 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
return false; return false;
} }
std::vector<const char*> argv;
argv.reserve(args.size() + 1);
for (std::string const& a : args) {
argv.push_back(a.c_str());
}
argv.push_back(nullptr);
output.clear(); output.clear();
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:"); cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:");
for (auto const& arg : args) { for (char const* arg : argv) {
if (!arg) {
break;
}
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\""); cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"");
} }
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl); cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl);
// Now create process object // Now create process object
cmUVProcessChainBuilder builder; cmsysProcess* cp = cmsysProcess_New();
builder.AddCommand(args).SetMergedBuiltinStreams(); cmsysProcess_SetCommand(cp, argv.data());
if (dir) { cmsysProcess_SetWorkingDirectory(cp, dir);
builder.SetWorkingDirectory(dir); cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
} cmsysProcess_SetTimeout(cp, timeout.count());
auto chain = builder.Start(); cmsysProcess_Execute(cp);
cm::uv_pipe_ptr outputStream;
outputStream.init(chain.GetLoop(), 0);
uv_pipe_open(outputStream, chain.OutputStream());
// Initialize tick's // Initialize tick's
std::string::size_type tick = 0; std::string::size_type tick = 0;
std::string::size_type tick_len = 1024; std::string::size_type tick_len = 1024;
std::string::size_type tick_line_len = 50; std::string::size_type tick_line_len = 50;
char* data;
int length;
cmProcessOutput processOutput(encoding); cmProcessOutput processOutput(encoding);
std::string strdata;
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
" Each . represents " << tick_len " Each . represents " << tick_len
<< " bytes of output\n" << " bytes of output\n"
" " " "
<< std::flush); << std::flush);
auto outputHandle = cmUVStreamRead( while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
outputStream, processOutput.DecodeText(data, length, strdata);
[this, &processOutput, &output, &tick, &tick_len, &tick_line_len,
&ofs](std::vector<char> data) {
std::string strdata;
processOutput.DecodeText(data.data(), data.size(), strdata);
for (char& cc : strdata) { for (char& cc : strdata) {
if (cc == 0) { if (cc == 0) {
cc = '\n'; cc = '\n';
@ -1143,9 +1145,7 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
if (ofs) { if (ofs) {
ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
} }
}, }
[this, &processOutput, &output, &ofs]() {
std::string strdata;
processOutput.DecodeText(std::string(), strdata); processOutput.DecodeText(std::string(), strdata);
if (!strdata.empty()) { if (!strdata.empty()) {
output.append(strdata); output.append(strdata);
@ -1155,40 +1155,36 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
} }
} }
});
bool finished = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0));
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
" Size of output: " << int(double(output.size()) / 1024.0) << "K" " Size of output: " << int(double(output.size()) / 1024.0) << "K"
<< std::endl); << std::endl);
if (finished) { cmsysProcess_WaitForExit(cp, nullptr);
auto const& status = chain.GetStatus(0);
auto exception = status.GetException(); int result = cmsysProcess_GetState(cp);
switch (exception.first) {
case cmUVProcessChain::ExceptionCode::None: if (result == cmsysProcess_State_Exited) {
*retVal = static_cast<int>(status.ExitStatus); *retVal = cmsysProcess_GetExitValue(cp);
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
"Command exited with the value: " << *retVal << std::endl); "Command exited with the value: " << *retVal << std::endl);
break; } else if (result == cmsysProcess_State_Exception) {
case cmUVProcessChain::ExceptionCode::Spawn: *retVal = cmsysProcess_GetExitException(cp);
output += "\n*** ERROR executing: ";
output += exception.second;
output += "\n***The build process failed.";
cmCTestLog(this, ERROR_MESSAGE,
"There was an error: " << exception.second << std::endl);
break;
default:
*retVal = static_cast<int>(exception.first);
cmCTestLog(this, WARNING, cmCTestLog(this, WARNING,
"There was an exception: " << *retVal << std::endl); "There was an exception: " << *retVal << std::endl);
break; } else if (result == cmsysProcess_State_Expired) {
}
} else {
cmCTestLog(this, WARNING, "There was a timeout" << std::endl); cmCTestLog(this, WARNING, "There was a timeout" << std::endl);
} else if (result == cmsysProcess_State_Error) {
output += "\n*** ERROR executing: ";
output += cmsysProcess_GetErrorString(cp);
output += "\n***The build process failed.";
cmCTestLog(this, ERROR_MESSAGE,
"There was an error: " << cmsysProcess_GetErrorString(cp)
<< std::endl);
} }
return true; cmsysProcess_Delete(cp);
return result;
} }
// ###################################################################### // ######################################################################
@ -1196,9 +1192,8 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
// ###################################################################### // ######################################################################
// ###################################################################### // ######################################################################
bool cmCTest::RunTest(const std::vector<std::string>& argv, int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
std::string* output, int* retVal, std::ostream* log, int* retVal, std::ostream* log, cmDuration testTimeOut,
cmDuration testTimeOut,
std::vector<std::string>* environment, Encoding encoding) std::vector<std::string>* environment, Encoding encoding)
{ {
bool modifyEnv = (environment && !environment->empty()); bool modifyEnv = (environment && !environment->empty());
@ -1238,17 +1233,20 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
inst.SetStreams(&oss, &oss); inst.SetStreams(&oss, &oss);
std::vector<std::string> args; std::vector<std::string> args;
for (auto const& i : argv) { for (char const* i : argv) {
if (i) {
// make sure we pass the timeout in for any build and test // make sure we pass the timeout in for any build and test
// invocations. Since --build-generator is required this is a // invocations. Since --build-generator is required this is a
// good place to check for it, and to add the arguments in // good place to check for it, and to add the arguments in
if (i == "--build-generator" && timeout != cmCTest::MaxDuration() && if (strcmp(i, "--build-generator") == 0 &&
timeout != cmCTest::MaxDuration() &&
timeout > cmDuration::zero()) { timeout > cmDuration::zero()) {
args.emplace_back("--test-timeout"); args.emplace_back("--test-timeout");
args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout))); args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
} }
args.emplace_back(i); args.emplace_back(i);
} }
}
if (log) { if (log) {
*log << "* Run internal CTest" << std::endl; *log << "* Run internal CTest" << std::endl;
} }
@ -1273,7 +1271,7 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
<< std::endl); << std::endl);
} }
return true; return cmsysProcess_State_Exited;
} }
std::vector<char> tempOutput; std::vector<char> tempOutput;
if (output) { if (output) {
@ -1286,32 +1284,31 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
cmSystemTools::AppendEnv(*environment); cmSystemTools::AppendEnv(*environment);
} }
cmUVProcessChainBuilder builder; cmsysProcess* cp = cmsysProcess_New();
builder.AddCommand(argv).SetMergedBuiltinStreams(); cmsysProcess_SetCommand(cp, argv.data());
cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl); cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl);
auto chain = builder.Start(); if (cmSystemTools::GetRunCommandHideConsole()) {
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
}
cmsysProcess_SetTimeout(cp, timeout.count());
cmsysProcess_Execute(cp);
char* data;
int length;
cmProcessOutput processOutput(encoding); cmProcessOutput processOutput(encoding);
cm::uv_pipe_ptr outputStream;
outputStream.init(chain.GetLoop(), 0);
uv_pipe_open(outputStream, chain.OutputStream());
auto outputHandle = cmUVStreamRead(
outputStream,
[this, &processOutput, &output, &tempOutput,
&log](std::vector<char> data) {
std::string strdata; std::string strdata;
processOutput.DecodeText(data.data(), data.size(), strdata); while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
processOutput.DecodeText(data, length, strdata);
if (output) { if (output) {
cm::append(tempOutput, data.data(), data.data() + data.size()); cm::append(tempOutput, data, data + length);
} }
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
cmCTestLogWrite(strdata.c_str(), strdata.size())); cmCTestLogWrite(strdata.c_str(), strdata.size()));
if (log) { if (log) {
log->write(strdata.c_str(), strdata.size()); log->write(strdata.c_str(), strdata.size());
} }
}, }
[this, &processOutput, &log]() {
std::string strdata;
processOutput.DecodeText(std::string(), strdata); processOutput.DecodeText(std::string(), strdata);
if (!strdata.empty()) { if (!strdata.empty()) {
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
@ -1320,9 +1317,8 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
log->write(strdata.c_str(), strdata.size()); log->write(strdata.c_str(), strdata.size());
} }
} }
});
bool complete = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0)); cmsysProcess_WaitForExit(cp, nullptr);
processOutput.DecodeText(tempOutput, tempOutput); processOutput.DecodeText(tempOutput, tempOutput);
if (output && tempOutput.begin() != tempOutput.end()) { if (output && tempOutput.begin() != tempOutput.end()) {
output->append(tempOutput.data(), tempOutput.size()); output->append(tempOutput.data(), tempOutput.size());
@ -1330,41 +1326,33 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
"-- Process completed" << std::endl); "-- Process completed" << std::endl);
bool result = false; int result = cmsysProcess_GetState(cp);
if (complete) { if (result == cmsysProcess_State_Exited) {
auto const& status = chain.GetStatus(0); *retVal = cmsysProcess_GetExitValue(cp);
auto exception = status.GetException();
switch (exception.first) {
case cmUVProcessChain::ExceptionCode::None:
*retVal = static_cast<int>(status.ExitStatus);
if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) { if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
this->OutputTestErrors(tempOutput); this->OutputTestErrors(tempOutput);
} }
result = true; } else if (result == cmsysProcess_State_Exception) {
break; if (this->Impl->OutputTestOutputOnTestFailure) {
case cmUVProcessChain::ExceptionCode::Spawn: { this->OutputTestErrors(tempOutput);
std::string outerr = }
cmStrCat("\n*** ERROR executing: ", exception.second); *retVal = cmsysProcess_GetExitException(cp);
std::string outerr = cmStrCat("\n*** Exception executing: ",
cmsysProcess_GetExceptionString(cp));
if (output) { if (output) {
*output += outerr; *output += outerr;
} }
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
} break; } else if (result == cmsysProcess_State_Error) {
default: {
if (this->Impl->OutputTestOutputOnTestFailure) {
this->OutputTestErrors(tempOutput);
}
*retVal = status.TermSignal;
std::string outerr = std::string outerr =
cmStrCat("\n*** Exception executing: ", exception.second); cmStrCat("\n*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
if (output) { if (output) {
*output += outerr; *output += outerr;
} }
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
} break;
}
} }
cmsysProcess_Delete(cp);
return result; return result;
} }
@ -3482,70 +3470,49 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args,
stdOut->clear(); stdOut->clear();
stdErr->clear(); stdErr->clear();
cmUVProcessChainBuilder builder; cmsysProcess* cp = cmsysProcess_New();
builder.AddCommand(args) cmsysProcess_SetCommand(cp, argv.data());
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) cmsysProcess_SetWorkingDirectory(cp, dir);
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); if (cmSystemTools::GetRunCommandHideConsole()) {
if (dir) { cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
builder.SetWorkingDirectory(dir);
}
auto chain = builder.Start();
cm::uv_timer_ptr timer;
bool timedOut = false;
if (timeout.count()) {
timer.init(chain.GetLoop(), &timedOut);
timer.start(
[](uv_timer_t* t) {
auto* timedOutPtr = static_cast<bool*>(t->data);
*timedOutPtr = true;
},
static_cast<uint64_t>(timeout.count() * 1000.0), 0);
} }
cmsysProcess_SetTimeout(cp, timeout.count());
cmsysProcess_Execute(cp);
std::vector<char> tempOutput; std::vector<char> tempOutput;
bool outFinished = false;
cm::uv_pipe_ptr outStream;
std::vector<char> tempError; std::vector<char> tempError;
bool errFinished = false; char* data;
cm::uv_pipe_ptr errStream; int length;
cmProcessOutput processOutput(encoding); cmProcessOutput processOutput(encoding);
auto startRead = [this, &chain, &processOutput](
cm::uv_pipe_ptr& pipe, int stream,
std::vector<char>& temp,
bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
pipe.init(chain.GetLoop(), 0);
uv_pipe_open(pipe, stream);
return cmUVStreamRead(
pipe,
[this, &temp, &processOutput](std::vector<char> data) {
cm::append(temp, data);
if (this->Impl->ExtraVerbose) {
std::string strdata; std::string strdata;
processOutput.DecodeText(data.data(), data.size(), strdata); int res;
bool done = false;
while (!done) {
res = cmsysProcess_WaitForData(cp, &data, &length, nullptr);
switch (res) {
case cmsysProcess_Pipe_STDOUT:
cm::append(tempOutput, data, data + length);
break;
case cmsysProcess_Pipe_STDERR:
cm::append(tempError, data, data + length);
break;
default:
done = true;
}
if ((res == cmsysProcess_Pipe_STDOUT || res == cmsysProcess_Pipe_STDERR) &&
this->Impl->ExtraVerbose) {
processOutput.DecodeText(data, length, strdata);
cmSystemTools::Stdout(strdata); cmSystemTools::Stdout(strdata);
} }
},
[&finished]() { finished = true; });
};
auto outputHandle =
startRead(outStream, chain.OutputStream(), tempOutput, outFinished);
auto errorHandle =
startRead(errStream, chain.ErrorStream(), tempError, errFinished);
while (!timedOut && !(outFinished && errFinished)) {
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
} }
if (this->Impl->ExtraVerbose) { if (this->Impl->ExtraVerbose) {
std::string strdata;
processOutput.DecodeText(std::string(), strdata); processOutput.DecodeText(std::string(), strdata);
if (!strdata.empty()) { if (!strdata.empty()) {
cmSystemTools::Stdout(strdata); cmSystemTools::Stdout(strdata);
} }
} }
while (!timedOut && !chain.Finished()) { cmsysProcess_WaitForExit(cp, nullptr);
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
}
if (!tempOutput.empty()) { if (!tempOutput.empty()) {
processOutput.DecodeText(tempOutput, tempOutput); processOutput.DecodeText(tempOutput, tempOutput);
stdOut->append(tempOutput.data(), tempOutput.size()); stdOut->append(tempOutput.data(), tempOutput.size());
@ -3556,32 +3523,32 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args,
} }
bool result = true; bool result = true;
if (timedOut) { if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
const char* error_str = "Process terminated due to timeout\n";
cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
stdErr->append(error_str, strlen(error_str));
result = false;
} else {
auto const& status = chain.GetStatus(0);
auto exception = status.GetException();
switch (exception.first) {
case cmUVProcessChain::ExceptionCode::None:
if (retVal) { if (retVal) {
*retVal = static_cast<int>(status.ExitStatus); *retVal = cmsysProcess_GetExitValue(cp);
} else { } else {
if (status.ExitStatus != 0) { if (cmsysProcess_GetExitValue(cp) != 0) {
result = false; result = false;
} }
} }
break; } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
default: { const char* exception_str = cmsysProcess_GetExceptionString(cp);
cmCTestLog(this, ERROR_MESSAGE, exception.second << std::endl); cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl);
stdErr->append(exception.second); stdErr->append(exception_str, strlen(exception_str));
result = false;
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
const char* error_str = cmsysProcess_GetErrorString(cp);
cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
stdErr->append(error_str, strlen(error_str));
result = false;
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
const char* error_str = "Process terminated due to timeout\n";
cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
stdErr->append(error_str, strlen(error_str));
result = false; result = false;
} break;
}
} }
cmsysProcess_Delete(cp);
return result; return result;
} }

@ -254,7 +254,7 @@ public:
* Run command specialized for make and configure. Returns process status * Run command specialized for make and configure. Returns process status
* and retVal is return value or exception. * and retVal is return value or exception.
*/ */
bool RunMakeCommand(const std::string& command, std::string& output, int RunMakeCommand(const std::string& command, std::string& output,
int* retVal, const char* dir, cmDuration timeout, int* retVal, const char* dir, cmDuration timeout,
std::ostream& ofs, std::ostream& ofs,
Encoding encoding = cmProcessOutput::Auto); Encoding encoding = cmProcessOutput::Auto);
@ -303,8 +303,8 @@ public:
* environment variables prior to running the test. After running the test, * environment variables prior to running the test. After running the test,
* environment variables are restored to their previous values. * environment variables are restored to their previous values.
*/ */
bool RunTest(const std::vector<std::string>& args, std::string* output, int RunTest(std::vector<const char*> args, std::string* output, int* retVal,
int* retVal, std::ostream* logfile, cmDuration testTimeOut, std::ostream* logfile, cmDuration testTimeOut,
std::vector<std::string>* environment, std::vector<std::string>* environment,
Encoding encoding = cmProcessOutput::Auto); Encoding encoding = cmProcessOutput::Auto);

@ -260,7 +260,7 @@ cmComputeLinkInformation::cmComputeLinkInformation(
, Config(config) , Config(config)
{ {
// Check whether to recognize OpenBSD-style library versioned names. // Check whether to recognize OpenBSD-style library versioned names.
this->OpenBSD = this->Makefile->GetState()->GetGlobalPropertyAsBool( this->IsOpenBSD = this->Makefile->GetState()->GetGlobalPropertyAsBool(
"FIND_LIBRARY_USE_OPENBSD_VERSIONING"); "FIND_LIBRARY_USE_OPENBSD_VERSIONING");
// Allocate internals. // Allocate internals.
@ -553,7 +553,8 @@ bool cmComputeLinkInformation::Compute()
this->Target->GetType() == cmStateEnums::MODULE_LIBRARY || this->Target->GetType() == cmStateEnums::MODULE_LIBRARY ||
this->Target->GetType() == cmStateEnums::STATIC_LIBRARY || this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
(this->Target->CanCompileSources() && (this->Target->CanCompileSources() &&
(this->Target->HaveCxx20ModuleSources() || (this->Target->HaveCxxModuleSupport(this->Config) ==
cmGeneratorTarget::Cxx20SupportLevel::Supported ||
this->Target->HaveFortranSources())))) { this->Target->HaveFortranSources())))) {
return false; return false;
} }
@ -1574,7 +1575,7 @@ std::string cmComputeLinkInformation::CreateExtensionRegex(
libext += ')'; libext += ')';
// Add an optional OpenBSD-style version or major.minor.version component. // Add an optional OpenBSD-style version or major.minor.version component.
if (this->OpenBSD || type == LinkShared) { if (this->IsOpenBSD || type == LinkShared) {
libext += "(\\.[0-9]+)*"; libext += "(\\.[0-9]+)*";
} }

@ -254,7 +254,7 @@ private:
std::unique_ptr<cmOrderDirectories> OrderRuntimeSearchPath; std::unique_ptr<cmOrderDirectories> OrderRuntimeSearchPath;
bool OldLinkDirMode; bool OldLinkDirMode;
bool OpenBSD; bool IsOpenBSD;
bool LinkDependsNoShared; bool LinkDependsNoShared;
bool RuntimeUseChrpath; bool RuntimeUseChrpath;
bool NoSONameUsesPath; bool NoSONameUsesPath;

@ -493,7 +493,8 @@ bool cmDyndepCollation::WriteDyndepMetadata(
if (!has_provides) { if (!has_provides) {
cmSystemTools::Error( cmSystemTools::Error(
cmStrCat("Output ", object.PrimaryOutput, cmStrCat("Output ", object.PrimaryOutput,
" is of type `CXX_MODULES` but does not provide a module")); " is of type `CXX_MODULES` but does not provide a module "
"interface unit or partition"));
result = false; result = false;
continue; continue;
} }

@ -2,8 +2,8 @@
file Copyright.txt or https://cmake.org/licensing for details. */ file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmExecuteProcessCommand.h" #include "cmExecuteProcessCommand.h"
#include <algorithm>
#include <cctype> /* isspace */ #include <cctype> /* isspace */
#include <cstdint>
#include <cstdio> #include <cstdio>
#include <iostream> #include <iostream>
#include <map> #include <map>
@ -16,9 +16,7 @@
#include <cmext/algorithm> #include <cmext/algorithm>
#include <cmext/string_view> #include <cmext/string_view>
#include <cm3p/uv.h> #include "cmsys/Process.h"
#include "cm_fileno.hxx"
#include "cmArgumentParser.h" #include "cmArgumentParser.h"
#include "cmExecutionStatus.h" #include "cmExecutionStatus.h"
@ -28,9 +26,6 @@
#include "cmProcessOutput.h" #include "cmProcessOutput.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVHandlePtr.h"
#include "cmUVProcessChain.h"
#include "cmUVStream.h"
namespace { namespace {
bool cmExecuteProcessCommandIsWhitespace(char c) bool cmExecuteProcessCommandIsWhitespace(char c)
@ -41,7 +36,7 @@ bool cmExecuteProcessCommandIsWhitespace(char c)
void cmExecuteProcessCommandFixText(std::vector<char>& output, void cmExecuteProcessCommandFixText(std::vector<char>& output,
bool strip_trailing_whitespace); bool strip_trailing_whitespace);
void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
std::size_t length); int length);
} }
// cmExecuteProcessCommand // cmExecuteProcessCommand
@ -166,69 +161,57 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
} }
} }
// Create a process instance. // Create a process instance.
cmUVProcessChainBuilder builder; std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr(
cmsysProcess_New(), cmsysProcess_Delete);
cmsysProcess* cp = cp_ptr.get();
// Set the command sequence. // Set the command sequence.
for (std::vector<std::string> const& cmd : arguments.Commands) { for (std::vector<std::string> const& cmd : arguments.Commands) {
builder.AddCommand(cmd); std::vector<const char*> argv(cmd.size() + 1);
std::transform(cmd.begin(), cmd.end(), argv.begin(),
[](std::string const& s) { return s.c_str(); });
argv.back() = nullptr;
cmsysProcess_AddCommand(cp, argv.data());
} }
// Set the process working directory. // Set the process working directory.
if (!arguments.WorkingDirectory.empty()) { if (!arguments.WorkingDirectory.empty()) {
builder.SetWorkingDirectory(arguments.WorkingDirectory); cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str());
} }
// Check the output variables. // Always hide the process window.
std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose); cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
if (!inputFilename.empty()) {
inputFile.reset(cmsys::SystemTools::Fopen(inputFilename, "rb"));
if (inputFile) {
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
cm_fileno(inputFile.get()));
}
} else {
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
cm_fileno(stdin));
}
std::unique_ptr<FILE, int (*)(FILE*)> outputFile(nullptr, fclose); // Check the output variables.
if (!outputFilename.empty()) { bool merge_output = false;
outputFile.reset(cmsys::SystemTools::Fopen(outputFilename, "wb")); if (!arguments.InputFile.empty()) {
if (outputFile) { cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN,
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, arguments.InputFile.c_str());
cm_fileno(outputFile.get())); }
} if (!arguments.OutputFile.empty()) {
} else { cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT,
if (arguments.OutputVariable == arguments.ErrorVariable && arguments.OutputFile.c_str());
!arguments.ErrorVariable.empty()) { }
builder.SetMergedBuiltinStreams(); if (!arguments.ErrorFile.empty()) {
if (arguments.ErrorFile == arguments.OutputFile) {
merge_output = true;
} else { } else {
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR,
arguments.ErrorFile.c_str());
} }
} }
if (!arguments.OutputVariable.empty() &&
std::unique_ptr<FILE, int (*)(FILE*)> errorFile(nullptr, fclose); arguments.OutputVariable == arguments.ErrorVariable) {
if (!errorFilename.empty()) { merge_output = true;
if (errorFilename == outputFilename) {
if (outputFile) {
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
cm_fileno(outputFile.get()));
}
} else {
errorFile.reset(cmsys::SystemTools::Fopen(errorFilename, "wb"));
if (errorFile) {
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
cm_fileno(errorFile.get()));
}
} }
} else if (arguments.ErrorVariable.empty() || if (merge_output) {
(!arguments.ErrorVariable.empty() && cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
arguments.OutputVariable != arguments.ErrorVariable)) {
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
} }
// Set the timeout if any. // Set the timeout if any.
int64_t timeoutMillis = static_cast<int64_t>(timeout * 1000.0); if (timeout >= 0) {
cmsysProcess_SetTimeout(cp, timeout);
}
bool echo_stdout = false; bool echo_stdout = false;
bool echo_stderr = false; bool echo_stderr = false;
@ -276,86 +259,36 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
} }
} }
// Start the process. // Start the process.
auto chain = builder.Start(); cmsysProcess_Execute(cp);
bool timedOut = false;
cm::uv_timer_ptr timer;
if (timeoutMillis >= 0) {
timer.init(chain.GetLoop(), &timedOut);
timer.start(
[](uv_timer_t* handle) {
auto* timeoutPtr = static_cast<bool*>(handle->data);
*timeoutPtr = true;
},
timeoutMillis, 0);
}
// Read the process output. // Read the process output.
struct ReadData std::vector<char> tempOutput;
{ std::vector<char> tempError;
bool Finished = false; int length;
std::vector<char> Output; char* data;
cm::uv_pipe_ptr Stream; int p;
};
ReadData outputData;
ReadData errorData;
cmProcessOutput processOutput( cmProcessOutput processOutput(
cmProcessOutput::FindEncoding(arguments.Encoding)); cmProcessOutput::FindEncoding(arguments.Encoding));
std::string strdata; std::string strdata;
while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
std::unique_ptr<cmUVStreamReadHandle> outputHandle; // Put the output in the right place.
if (chain.OutputStream() >= 0) { if (p == cmsysProcess_Pipe_STDOUT && !arguments.OutputQuiet) {
outputData.Stream.init(chain.GetLoop(), 0); if (arguments.OutputVariable.empty() || arguments.EchoOutputVariable) {
uv_pipe_open(outputData.Stream, chain.OutputStream()); processOutput.DecodeText(data, length, strdata, 1);
outputHandle = cmUVStreamRead(
outputData.Stream,
[&arguments, &processOutput, &outputData,
&strdata](std::vector<char> data) {
if (!arguments.OutputQuiet) {
if (arguments.OutputVariable.empty() ||
arguments.EchoOutputVariable) {
processOutput.DecodeText(data.data(), data.size(), strdata, 1);
cmSystemTools::Stdout(strdata); cmSystemTools::Stdout(strdata);
} }
if (!arguments.OutputVariable.empty()) { if (!arguments.OutputVariable.empty()) {
cmExecuteProcessCommandAppend(outputData.Output, data.data(), cmExecuteProcessCommandAppend(tempOutput, data, length);
data.size());
} }
} } else if (p == cmsysProcess_Pipe_STDERR && !arguments.ErrorQuiet) {
},
[&outputData]() { outputData.Finished = true; });
} else {
outputData.Finished = true;
}
std::unique_ptr<cmUVStreamReadHandle> errorHandle;
if (chain.ErrorStream() >= 0 &&
chain.ErrorStream() != chain.OutputStream()) {
errorData.Stream.init(chain.GetLoop(), 0);
uv_pipe_open(errorData.Stream, chain.ErrorStream());
errorHandle = cmUVStreamRead(
errorData.Stream,
[&arguments, &processOutput, &errorData,
&strdata](std::vector<char> data) {
if (!arguments.ErrorQuiet) {
if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) { if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
processOutput.DecodeText(data.data(), data.size(), strdata, 2); processOutput.DecodeText(data, length, strdata, 2);
cmSystemTools::Stderr(strdata); cmSystemTools::Stderr(strdata);
} }
if (!arguments.ErrorVariable.empty()) { if (!arguments.ErrorVariable.empty()) {
cmExecuteProcessCommandAppend(errorData.Output, data.data(), cmExecuteProcessCommandAppend(tempError, data, length);
data.size());
} }
} }
},
[&errorData]() { errorData.Finished = true; });
} else {
errorData.Finished = true;
}
while (chain.Valid() && !timedOut &&
!(chain.Finished() && outputData.Finished && errorData.Finished)) {
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
} }
if (!arguments.OutputQuiet && if (!arguments.OutputQuiet &&
(arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) { (arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) {
@ -372,93 +305,126 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
} }
} }
// All output has been read. // All output has been read. Wait for the process to exit.
processOutput.DecodeText(outputData.Output, outputData.Output); cmsysProcess_WaitForExit(cp, nullptr);
processOutput.DecodeText(errorData.Output, errorData.Output); processOutput.DecodeText(tempOutput, tempOutput);
processOutput.DecodeText(tempError, tempError);
// Fix the text in the output strings. // Fix the text in the output strings.
cmExecuteProcessCommandFixText(outputData.Output, cmExecuteProcessCommandFixText(tempOutput,
arguments.OutputStripTrailingWhitespace); arguments.OutputStripTrailingWhitespace);
cmExecuteProcessCommandFixText(errorData.Output, cmExecuteProcessCommandFixText(tempError,
arguments.ErrorStripTrailingWhitespace); arguments.ErrorStripTrailingWhitespace);
// Store the output obtained. // Store the output obtained.
if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) { if (!arguments.OutputVariable.empty() && !tempOutput.empty()) {
status.GetMakefile().AddDefinition(arguments.OutputVariable, status.GetMakefile().AddDefinition(arguments.OutputVariable,
outputData.Output.data()); tempOutput.data());
} }
if (arguments.ErrorVariable != arguments.OutputVariable && if (!merge_output && !arguments.ErrorVariable.empty() &&
!arguments.ErrorVariable.empty() && !errorData.Output.empty()) { !tempError.empty()) {
status.GetMakefile().AddDefinition(arguments.ErrorVariable, status.GetMakefile().AddDefinition(arguments.ErrorVariable,
errorData.Output.data()); tempError.data());
} }
// Store the result of running the process. // Store the result of running the process.
if (!arguments.ResultVariable.empty()) { if (!arguments.ResultVariable.empty()) {
if (timedOut) { switch (cmsysProcess_GetState(cp)) {
status.GetMakefile().AddDefinition(arguments.ResultVariable, case cmsysProcess_State_Exited: {
"Process terminated due to timeout"); int v = cmsysProcess_GetExitValue(cp);
} else { char buf[16];
auto const* lastStatus = chain.GetStatus().back(); snprintf(buf, sizeof(buf), "%d", v);
auto exception = lastStatus->GetException(); status.GetMakefile().AddDefinition(arguments.ResultVariable, buf);
if (exception.first == cmUVProcessChain::ExceptionCode::None) { } break;
case cmsysProcess_State_Exception:
status.GetMakefile().AddDefinition( status.GetMakefile().AddDefinition(
arguments.ResultVariable, arguments.ResultVariable, cmsysProcess_GetExceptionString(cp));
std::to_string(static_cast<int>(lastStatus->ExitStatus))); break;
} else { case cmsysProcess_State_Error:
status.GetMakefile().AddDefinition(arguments.ResultVariable, status.GetMakefile().AddDefinition(arguments.ResultVariable,
exception.second); cmsysProcess_GetErrorString(cp));
} break;
case cmsysProcess_State_Expired:
status.GetMakefile().AddDefinition(
arguments.ResultVariable, "Process terminated due to timeout");
break;
} }
} }
// Store the result of running the processes. // Store the result of running the processes.
if (!arguments.ResultsVariable.empty()) { if (!arguments.ResultsVariable.empty()) {
if (timedOut) { switch (cmsysProcess_GetState(cp)) {
status.GetMakefile().AddDefinition(arguments.ResultsVariable, case cmsysProcess_State_Exited: {
"Process terminated due to timeout");
} else {
std::vector<std::string> res; std::vector<std::string> res;
for (auto const* processStatus : chain.GetStatus()) { for (size_t i = 0; i < arguments.Commands.size(); ++i) {
auto exception = processStatus->GetException(); switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) {
if (exception.first == cmUVProcessChain::ExceptionCode::None) { case kwsysProcess_StateByIndex_Exited: {
res.emplace_back( int exitCode =
std::to_string(static_cast<int>(processStatus->ExitStatus))); cmsysProcess_GetExitValueByIndex(cp, static_cast<int>(i));
} else { char buf[16];
res.emplace_back(exception.second); snprintf(buf, sizeof(buf), "%d", exitCode);
res.emplace_back(buf);
} break;
case kwsysProcess_StateByIndex_Exception:
res.emplace_back(cmsysProcess_GetExceptionStringByIndex(
cp, static_cast<int>(i)));
break;
case kwsysProcess_StateByIndex_Error:
default:
res.emplace_back("Error getting the child return code");
break;
} }
} }
status.GetMakefile().AddDefinition(arguments.ResultsVariable, status.GetMakefile().AddDefinition(arguments.ResultsVariable,
cmList::to_string(res)); cmList::to_string(res));
} break;
case cmsysProcess_State_Exception:
status.GetMakefile().AddDefinition(
arguments.ResultsVariable, cmsysProcess_GetExceptionString(cp));
break;
case cmsysProcess_State_Error:
status.GetMakefile().AddDefinition(arguments.ResultsVariable,
cmsysProcess_GetErrorString(cp));
break;
case cmsysProcess_State_Expired:
status.GetMakefile().AddDefinition(
arguments.ResultsVariable, "Process terminated due to timeout");
break;
} }
} }
auto queryProcessStatusByIndex = [&chain](std::size_t index) -> std::string { auto queryProcessStatusByIndex = [&cp](int index) -> std::string {
auto const& processStatus = chain.GetStatus(index); std::string processStatus;
auto exception = processStatus.GetException(); switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(index))) {
if (exception.first == cmUVProcessChain::ExceptionCode::None) { case kwsysProcess_StateByIndex_Exited: {
if (processStatus.ExitStatus) { int exitCode = cmsysProcess_GetExitValueByIndex(cp, index);
return cmStrCat("Child return code: ", processStatus.ExitStatus); if (exitCode) {
processStatus = "Child return code: " + std::to_string(exitCode);
}
} break;
case kwsysProcess_StateByIndex_Exception: {
processStatus = cmStrCat(
"Abnormal exit with child return code: ",
cmsysProcess_GetExceptionStringByIndex(cp, static_cast<int>(index)));
break;
} }
return ""; case kwsysProcess_StateByIndex_Error:
default:
processStatus = "Error getting the child return code";
break;
} }
return cmStrCat("Abnormal exit with child return code: ", return processStatus;
exception.second);
}; };
if (arguments.CommandErrorIsFatal == "ANY"_s) { if (arguments.CommandErrorIsFatal == "ANY"_s) {
bool ret = true; bool ret = true;
if (timedOut) { switch (cmsysProcess_GetState(cp)) {
status.SetError("Process terminated due to timeout"); case cmsysProcess_State_Exited: {
ret = false; std::map<int, std::string> failureIndices;
} else { for (int i = 0; i < static_cast<int>(arguments.Commands.size()); ++i) {
std::map<std::size_t, std::string> failureIndices;
auto statuses = chain.GetStatus();
for (std::size_t i = 0; i < statuses.size(); ++i) {
std::string processStatus = queryProcessStatusByIndex(i); std::string processStatus = queryProcessStatusByIndex(i);
if (!processStatus.empty()) { if (!processStatus.empty()) {
failureIndices[i] = processStatus; failureIndices[i] = processStatus;
} }
}
if (!failureIndices.empty()) { if (!failureIndices.empty()) {
std::ostringstream oss; std::ostringstream oss;
oss << "failed command indexes:\n"; oss << "failed command indexes:\n";
@ -469,6 +435,22 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
ret = false; ret = false;
} }
} }
} break;
case cmsysProcess_State_Exception:
status.SetError(
cmStrCat("abnormal exit: ", cmsysProcess_GetExceptionString(cp)));
ret = false;
break;
case cmsysProcess_State_Error:
status.SetError(cmStrCat("error getting child return code: ",
cmsysProcess_GetErrorString(cp)));
ret = false;
break;
case cmsysProcess_State_Expired:
status.SetError("Process terminated due to timeout");
ret = false;
break;
}
if (!ret) { if (!ret) {
cmSystemTools::SetFatalErrorOccurred(); cmSystemTools::SetFatalErrorOccurred();
@ -478,23 +460,29 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
if (arguments.CommandErrorIsFatal == "LAST"_s) { if (arguments.CommandErrorIsFatal == "LAST"_s) {
bool ret = true; bool ret = true;
if (timedOut) { switch (cmsysProcess_GetState(cp)) {
status.SetError("Process terminated due to timeout"); case cmsysProcess_State_Exited: {
ret = false;
} else {
auto const& lastStatus = chain.GetStatus(arguments.Commands.size() - 1);
auto exception = lastStatus.GetException();
if (exception.first != cmUVProcessChain::ExceptionCode::None) {
status.SetError(cmStrCat("Abnormal exit: ", exception.second));
ret = false;
} else {
int lastIndex = static_cast<int>(arguments.Commands.size() - 1); int lastIndex = static_cast<int>(arguments.Commands.size() - 1);
const std::string processStatus = queryProcessStatusByIndex(lastIndex); const std::string processStatus = queryProcessStatusByIndex(lastIndex);
if (!processStatus.empty()) { if (!processStatus.empty()) {
status.SetError("last command failed"); status.SetError("last command failed");
ret = false; ret = false;
} }
} } break;
case cmsysProcess_State_Exception:
status.SetError(
cmStrCat("Abnormal exit: ", cmsysProcess_GetExceptionString(cp)));
ret = false;
break;
case cmsysProcess_State_Error:
status.SetError(cmStrCat("Error getting child return code: ",
cmsysProcess_GetErrorString(cp)));
ret = false;
break;
case cmsysProcess_State_Expired:
status.SetError("Process terminated due to timeout");
ret = false;
break;
} }
if (!ret) { if (!ret) {
cmSystemTools::SetFatalErrorOccurred(); cmSystemTools::SetFatalErrorOccurred();
@ -537,7 +525,7 @@ void cmExecuteProcessCommandFixText(std::vector<char>& output,
} }
void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
std::size_t length) int length)
{ {
#if defined(__APPLE__) #if defined(__APPLE__)
// HACK on Apple to work around bug with inserting at the // HACK on Apple to work around bug with inserting at the

@ -3,6 +3,7 @@
#include "cmExportBuildFileGenerator.h" #include "cmExportBuildFileGenerator.h"
#include <algorithm> #include <algorithm>
#include <cstddef>
#include <map> #include <map>
#include <memory> #include <memory>
#include <set> #include <set>
@ -13,6 +14,7 @@
#include <cmext/algorithm> #include <cmext/algorithm>
#include <cmext/string_view> #include <cmext/string_view>
#include "cmCryptoHash.h"
#include "cmExportSet.h" #include "cmExportSet.h"
#include "cmFileSet.h" #include "cmFileSet.h"
#include "cmGeneratedFileStream.h" #include "cmGeneratedFileStream.h"
@ -156,7 +158,19 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
this->GenerateTargetFileSets(gte, os); this->GenerateTargetFileSets(gte, os);
} }
this->GenerateCxxModuleInformation(os); std::string cxx_modules_name;
if (this->ExportSet) {
cxx_modules_name = this->ExportSet->GetName();
} else {
cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
constexpr std::size_t HASH_TRUNCATION = 12;
for (auto const& target : this->Targets) {
hasher.Append(target);
}
cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION);
}
this->GenerateCxxModuleInformation(cxx_modules_name, os);
// Generate import file content for each configuration. // Generate import file content for each configuration.
for (std::string const& c : this->Configurations) { for (std::string const& c : this->Configurations) {
@ -165,7 +179,7 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
// Generate import file content for each configuration. // Generate import file content for each configuration.
for (std::string const& c : this->Configurations) { for (std::string const& c : this->Configurations) {
this->GenerateImportCxxModuleConfigTargetInclusion(c); this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, c);
} }
this->GenerateMissingTargetsCheckCode(os); this->GenerateMissingTargetsCheckCode(os);
@ -506,7 +520,7 @@ std::string cmExportBuildFileGenerator::GetCxxModulesDirectory() const
} }
void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation( void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation(
std::ostream& os) const std::string const& name, std::ostream& os) const
{ {
const char* opt = ""; const char* opt = "";
if (this->Configurations.size() > 1) { if (this->Configurations.size() > 1) {
@ -519,13 +533,13 @@ void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation(
if (c.empty()) { if (c.empty()) {
c = "noconfig"; c = "noconfig";
} }
os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << c << ".cmake\"" os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << '-'
<< opt << ")\n"; << c << ".cmake\"" << opt << ")\n";
} }
} }
bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion( bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion(
std::string config) const std::string const& name, std::string config) const
{ {
auto cxx_modules_dirname = this->GetCxxModulesDirectory(); auto cxx_modules_dirname = this->GetCxxModulesDirectory();
if (cxx_modules_dirname.empty()) { if (cxx_modules_dirname.empty()) {
@ -536,8 +550,9 @@ bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion(
config = "noconfig"; config = "noconfig";
} }
std::string fileName = cmStrCat(this->FileDir, '/', cxx_modules_dirname, std::string fileName =
"/cxx-modules-", config, ".cmake"); cmStrCat(this->FileDir, '/', cxx_modules_dirname, "/cxx-modules-", name,
'-', config, ".cmake");
cmGeneratedFileStream os(fileName, true); cmGeneratedFileStream os(fileName, true);
if (!os) { if (!os) {

@ -92,8 +92,10 @@ protected:
cmTargetExport* te) override; cmTargetExport* te) override;
std::string GetCxxModulesDirectory() const override; std::string GetCxxModulesDirectory() const override;
void GenerateCxxModuleConfigInformation(std::ostream&) const override; void GenerateCxxModuleConfigInformation(std::string const&,
bool GenerateImportCxxModuleConfigTargetInclusion(std::string) const; std::ostream&) const override;
bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&,
std::string) const;
std::pair<std::vector<std::string>, std::string> FindBuildExportInfo( std::pair<std::vector<std::string>, std::string> FindBuildExportInfo(
cmGlobalGenerator* gg, const std::string& name); cmGlobalGenerator* gg, const std::string& name);

@ -1461,7 +1461,8 @@ void cmExportFileGenerator::GenerateTargetFileSets(cmGeneratorTarget* gte,
} }
} }
void cmExportFileGenerator::GenerateCxxModuleInformation(std::ostream& os) void cmExportFileGenerator::GenerateCxxModuleInformation(
std::string const& name, std::ostream& os)
{ {
auto const cxx_module_dirname = this->GetCxxModulesDirectory(); auto const cxx_module_dirname = this->GetCxxModulesDirectory();
if (cxx_module_dirname.empty()) { if (cxx_module_dirname.empty()) {
@ -1471,17 +1472,17 @@ void cmExportFileGenerator::GenerateCxxModuleInformation(std::ostream& os)
// Write the include. // Write the include.
os << "# Include C++ module properties\n" os << "# Include C++ module properties\n"
<< "include(\"${CMAKE_CURRENT_LIST_DIR}/" << cxx_module_dirname << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << cxx_module_dirname
<< "/cxx-modules.cmake\")\n\n"; << "/cxx-modules-" << name << ".cmake\")\n\n";
// Get the path to the file we're going to write. // Get the path to the file we're going to write.
std::string path = this->MainImportFile; std::string path = this->MainImportFile;
path = cmSystemTools::GetFilenamePath(path); path = cmSystemTools::GetFilenamePath(path);
auto trampoline_path = auto trampoline_path =
cmStrCat(path, '/', cxx_module_dirname, "/cxx-modules.cmake"); cmStrCat(path, '/', cxx_module_dirname, "/cxx-modules-", name, ".cmake");
// Include all configuration-specific include files. // Include all configuration-specific include files.
cmGeneratedFileStream ap(trampoline_path, true); cmGeneratedFileStream ap(trampoline_path, true);
ap.SetCopyIfDifferent(true); ap.SetCopyIfDifferent(true);
this->GenerateCxxModuleConfigInformation(ap); this->GenerateCxxModuleConfigInformation(name, ap);
} }

@ -187,7 +187,7 @@ protected:
void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os, void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os,
cmTargetExport* te = nullptr); cmTargetExport* te = nullptr);
void GenerateCxxModuleInformation(std::ostream& os); void GenerateCxxModuleInformation(std::string const& name, std::ostream& os);
virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte, virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte,
cmFileSet* fileSet, cmFileSet* fileSet,
@ -235,5 +235,6 @@ private:
const std::string& config) = 0; const std::string& config) = 0;
virtual std::string GetCxxModulesDirectory() const = 0; virtual std::string GetCxxModulesDirectory() const = 0;
virtual void GenerateCxxModuleConfigInformation(std::ostream& os) const = 0; virtual void GenerateCxxModuleConfigInformation(std::string const&,
std::ostream& os) const = 0;
}; };

@ -181,10 +181,12 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
bool result = true; bool result = true;
this->GenerateCxxModuleInformation(os); std::string cxx_modules_name = this->IEGen->GetExportSet()->GetName();
this->GenerateCxxModuleInformation(cxx_modules_name, os);
if (requiresConfigFiles) { if (requiresConfigFiles) {
for (std::string const& c : this->Configurations) { for (std::string const& c : this->Configurations) {
if (!this->GenerateImportCxxModuleConfigTargetInclusion(c)) { if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name,
c)) {
result = false; result = false;
} }
} }
@ -718,12 +720,12 @@ std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const
} }
void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation( void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation(
std::ostream& os) const std::string const& name, std::ostream& os) const
{ {
// Now load per-configuration properties for them. // Now load per-configuration properties for them.
/* clang-format off */ /* clang-format off */
os << "# Load information for each installed configuration.\n" os << "# Load information for each installed configuration.\n"
"file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-*.cmake\")\n" "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << "-*.cmake\")\n"
"foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n" "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n"
" include(\"${_cmake_cxx_module_include}\")\n" " include(\"${_cmake_cxx_module_include}\")\n"
"endforeach()\n" "endforeach()\n"
@ -733,7 +735,8 @@ void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation(
} }
bool cmExportInstallFileGenerator:: bool cmExportInstallFileGenerator::
GenerateImportCxxModuleConfigTargetInclusion(std::string const& config) GenerateImportCxxModuleConfigTargetInclusion(std::string const& name,
std::string const& config)
{ {
auto cxx_modules_dirname = this->GetCxxModulesDirectory(); auto cxx_modules_dirname = this->GetCxxModulesDirectory();
if (cxx_modules_dirname.empty()) { if (cxx_modules_dirname.empty()) {
@ -748,7 +751,7 @@ bool cmExportInstallFileGenerator::
std::string const dest = std::string const dest =
cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/'); cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/');
std::string fileName = std::string fileName =
cmStrCat(dest, "cxx-modules-", filename_config, ".cmake"); cmStrCat(dest, "cxx-modules-", name, '-', filename_config, ".cmake");
cmGeneratedFileStream os(fileName, true); cmGeneratedFileStream os(fileName, true);
if (!os) { if (!os) {

@ -118,8 +118,10 @@ protected:
cmTargetExport* te) override; cmTargetExport* te) override;
std::string GetCxxModulesDirectory() const override; std::string GetCxxModulesDirectory() const override;
void GenerateCxxModuleConfigInformation(std::ostream&) const override; void GenerateCxxModuleConfigInformation(std::string const&,
bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&); std::ostream&) const override;
bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&,
std::string const&);
cmInstallExportGenerator* IEGen; cmInstallExportGenerator* IEGen;

@ -56,7 +56,10 @@ protected:
cmTargetExport* te) override; cmTargetExport* te) override;
std::string GetCxxModulesDirectory() const override { return {}; } std::string GetCxxModulesDirectory() const override { return {}; }
void GenerateCxxModuleConfigInformation(std::ostream&) const override {} void GenerateCxxModuleConfigInformation(std::string const&,
std::ostream&) const override
{
}
private: private:
std::string FindTargets(const std::string& prop, std::string FindTargets(const std::string& prop,

@ -207,7 +207,7 @@ struct cmFindLibraryHelper
std::string BestPath; std::string BestPath;
// Support for OpenBSD shared library naming: lib<name>.so.<major>.<minor> // Support for OpenBSD shared library naming: lib<name>.so.<major>.<minor>
bool OpenBSD; bool IsOpenBSD;
bool DebugMode; bool DebugMode;
@ -320,7 +320,7 @@ cmFindLibraryHelper::cmFindLibraryHelper(std::string debugName, cmMakefile* mf,
this->RegexFromList(this->SuffixRegexStr, this->Suffixes); this->RegexFromList(this->SuffixRegexStr, this->Suffixes);
// Check whether to use OpenBSD-style library version comparisons. // Check whether to use OpenBSD-style library version comparisons.
this->OpenBSD = this->Makefile->GetState()->GetGlobalPropertyAsBool( this->IsOpenBSD = this->Makefile->GetState()->GetGlobalPropertyAsBool(
"FIND_LIBRARY_USE_OPENBSD_VERSIONING"); "FIND_LIBRARY_USE_OPENBSD_VERSIONING");
} }
@ -390,7 +390,7 @@ void cmFindLibraryHelper::AddName(std::string const& name)
std::string regex = cmStrCat('^', this->PrefixRegexStr); std::string regex = cmStrCat('^', this->PrefixRegexStr);
this->RegexFromLiteral(regex, name); this->RegexFromLiteral(regex, name);
regex += this->SuffixRegexStr; regex += this->SuffixRegexStr;
if (this->OpenBSD) { if (this->IsOpenBSD) {
regex += "(\\.[0-9]+\\.[0-9]+)?"; regex += "(\\.[0-9]+\\.[0-9]+)?";
} }
regex += "$"; regex += "$";
@ -472,7 +472,7 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path,
size_type suffix = this->GetSuffixIndex(name.Regex.match(2)); size_type suffix = this->GetSuffixIndex(name.Regex.match(2));
unsigned int major = 0; unsigned int major = 0;
unsigned int minor = 0; unsigned int minor = 0;
if (this->OpenBSD) { if (this->IsOpenBSD) {
sscanf(name.Regex.match(3).c_str(), ".%u.%u", &major, &minor); sscanf(name.Regex.match(3).c_str(), ".%u.%u", &major, &minor);
} }
if (this->BestPath.empty() || prefix < bestPrefix || if (this->BestPath.empty() || prefix < bestPrefix ||

@ -8319,14 +8319,14 @@ bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache,
} }
if (gt->HaveCxx20ModuleSources()) { if (gt->HaveCxx20ModuleSources()) {
auto hasher = cmCryptoHash::New("SHA3_512"); cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
constexpr size_t HASH_TRUNCATION = 12; constexpr size_t HASH_TRUNCATION = 12;
auto dirhash = hasher->HashString( auto dirhash = hasher.HashString(
gt->GetLocalGenerator()->GetCurrentBinaryDirectory()); gt->GetLocalGenerator()->GetCurrentBinaryDirectory());
std::string safeName = gt->GetName(); std::string safeName = gt->GetName();
cmSystemTools::ReplaceString(safeName, ":", "_"); cmSystemTools::ReplaceString(safeName, ":", "_");
auto targetIdent = auto targetIdent =
hasher->HashString(cmStrCat("@d_", dirhash, "@u_", usage.GetHash())); hasher.HashString(cmStrCat("@d_", dirhash, "@u_", usage.GetHash()));
std::string targetName = std::string targetName =
cmStrCat(safeName, "@synth_", targetIdent.substr(0, HASH_TRUNCATION)); cmStrCat(safeName, "@synth_", targetIdent.substr(0, HASH_TRUNCATION));
@ -8762,11 +8762,23 @@ bool cmGeneratorTarget::IsFrameworkOnApple() const
bool cmGeneratorTarget::IsImportedFrameworkFolderOnApple( bool cmGeneratorTarget::IsImportedFrameworkFolderOnApple(
const std::string& config) const const std::string& config) const
{ {
return this->IsApple() && this->IsImported() && if (this->IsApple() && this->IsImported() &&
(this->GetType() == cmStateEnums::STATIC_LIBRARY || (this->GetType() == cmStateEnums::STATIC_LIBRARY ||
this->GetType() == cmStateEnums::SHARED_LIBRARY || this->GetType() == cmStateEnums::SHARED_LIBRARY ||
this->GetType() == cmStateEnums::UNKNOWN_LIBRARY) && this->GetType() == cmStateEnums::UNKNOWN_LIBRARY)) {
cmSystemTools::IsPathToFramework(this->GetLocation(config)); std::string cfg = config;
if (cfg.empty() && this->GetGlobalGenerator()->IsXcode()) {
// FIXME(#25515): Remove the need for this workaround.
// The Xcode generator queries include directories without any
// specific configuration. Pick one in case this target does
// not set either IMPORTED_LOCATION or IMPORTED_CONFIGURATIONS.
cfg =
this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)[0];
}
return cmSystemTools::IsPathToFramework(this->GetLocation(cfg));
}
return false;
} }
bool cmGeneratorTarget::IsAppBundleOnApple() const bool cmGeneratorTarget::IsAppBundleOnApple() const

@ -1587,6 +1587,13 @@ bool cmGlobalGenerator::Compute()
} }
} }
// Add unity sources after computing compile features. Unity sources do
// not change the set of languages or features, but we need to know them
// to filter out sources that are scanned for C++ module dependencies.
if (!this->AddUnitySources()) {
return false;
}
for (const auto& localGen : this->LocalGenerators) { for (const auto& localGen : this->LocalGenerators) {
cmMakefile* mf = localGen->GetMakefile(); cmMakefile* mf = localGen->GetMakefile();
for (const auto& g : mf->GetInstallGenerators()) { for (const auto& g : mf->GetInstallGenerators()) {
@ -1863,7 +1870,6 @@ bool cmGlobalGenerator::AddAutomaticSources()
if (!gt->CanCompileSources()) { if (!gt->CanCompileSources()) {
continue; continue;
} }
lg->AddUnityBuild(gt.get());
lg->AddISPCDependencies(gt.get()); lg->AddISPCDependencies(gt.get());
// Targets that reuse a PCH are handled below. // Targets that reuse a PCH are handled below.
if (!gt->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) { if (!gt->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) {
@ -1895,6 +1901,28 @@ bool cmGlobalGenerator::AddAutomaticSources()
return true; return true;
} }
bool cmGlobalGenerator::AddUnitySources()
{
for (const auto& lg : this->LocalGenerators) {
for (const auto& gt : lg->GetGeneratorTargets()) {
if (!gt->CanCompileSources()) {
continue;
}
lg->AddUnityBuild(gt.get());
}
}
// The above transformation may have changed the classification of sources.
// Clear the source list and classification cache (KindedSources) of all
// targets so that it will be recomputed correctly by the generators later
// now that the above transformations are done for all targets.
for (const auto& lg : this->LocalGenerators) {
for (const auto& gt : lg->GetGeneratorTargets()) {
gt->ClearSourcesCache();
}
}
return true;
}
std::unique_ptr<cmLinkLineComputer> cmGlobalGenerator::CreateLinkLineComputer( std::unique_ptr<cmLinkLineComputer> cmGlobalGenerator::CreateLinkLineComputer(
cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) const cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) const
{ {

@ -677,6 +677,7 @@ protected:
bool AddHeaderSetVerification(); bool AddHeaderSetVerification();
bool AddAutomaticSources(); bool AddAutomaticSources();
bool AddUnitySources();
std::string SelectMakeProgram(const std::string& makeProgram, std::string SelectMakeProgram(const std::string& makeProgram,
const std::string& makeDefault = "") const; const std::string& makeDefault = "") const;

@ -564,6 +564,7 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm)
this->Comspec = "cmd.exe"; this->Comspec = "cmd.exe";
} }
#endif #endif
cm->GetState()->SetNinja(true);
this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake"; this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
} }

@ -4,7 +4,6 @@
#include "cmImportedCxxModuleInfo.h" #include "cmImportedCxxModuleInfo.h"
#include <cstddef> #include <cstddef>
#include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -60,13 +59,13 @@ std::string ImportedCxxModuleLookup::BmiNameForSource(std::string const& path)
auto importit = this->ImportedInfo.find(path); auto importit = this->ImportedInfo.find(path);
std::string bmiName; std::string bmiName;
auto hasher = cmCryptoHash::New("SHA3_512"); cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
constexpr size_t HASH_TRUNCATION = 12; constexpr size_t HASH_TRUNCATION = 12;
if (importit != this->ImportedInfo.end()) { if (importit != this->ImportedInfo.end()) {
auto safename = hasher->HashString(importit->second.Name); auto safename = hasher.HashString(importit->second.Name);
bmiName = cmStrCat(safename.substr(0, HASH_TRUNCATION), ".bmi"); bmiName = cmStrCat(safename.substr(0, HASH_TRUNCATION), ".bmi");
} else { } else {
auto dirhash = hasher->HashString(path); auto dirhash = hasher.HashString(path);
bmiName = cmStrCat(dirhash.substr(0, HASH_TRUNCATION), ".bmi"); bmiName = cmStrCat(dirhash.substr(0, HASH_TRUNCATION), ".bmi");
} }

@ -158,10 +158,11 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os,
// Remove old per-configuration export files if the main changes. // Remove old per-configuration export files if the main changes.
std::string installedDir = cmStrCat( std::string installedDir = cmStrCat(
"$ENV{DESTDIR}", ConvertToAbsoluteDestination(cxx_module_dest), '/'); "$ENV{DESTDIR}", ConvertToAbsoluteDestination(cxx_module_dest), '/');
std::string installedFile = cmStrCat(installedDir, "/cxx-modules.cmake"); std::string installedFile = cmStrCat(installedDir, "/cxx-modules-",
this->ExportSet->GetName(), ".cmake");
std::string toInstallFile = std::string toInstallFile =
cmStrCat(cmSystemTools::GetFilenamePath(config_file_example), cmStrCat(cmSystemTools::GetFilenamePath(config_file_example),
"/cxx-modules.cmake"); "/cxx-modules-", this->ExportSet->GetName(), ".cmake");
os << indent << "if(EXISTS \"" << installedFile << "\")\n"; os << indent << "if(EXISTS \"" << installedFile << "\")\n";
Indent indentN = indent.Next(); Indent indentN = indent.Next();
Indent indentNN = indentN.Next(); Indent indentNN = indentN.Next();

@ -3134,6 +3134,15 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
std::vector<cmSourceFile*> sources; std::vector<cmSourceFile*> sources;
target->GetSourceFiles(sources, configs[ci]); target->GetSourceFiles(sources, configs[ci]);
for (cmSourceFile* sf : sources) { for (cmSourceFile* sf : sources) {
// Files which need C++ scanning cannot participate in unity builds as
// there is a single place in TUs that may perform module-dependency bits
// and a unity source cannot `#include` them in-order and represent a
// valid TU.
if (sf->GetLanguage() == "CXX"_s &&
target->NeedDyndepForSource("CXX", configs[ci], sf)) {
continue;
}
auto mi = index.find(sf); auto mi = index.find(sf);
if (mi == index.end()) { if (mi == index.end()) {
unitySources.emplace_back(sf); unitySources.emplace_back(sf);

@ -1722,11 +1722,10 @@ void cmMakefileTargetGenerator::GenerateCustomRuleFile(
if (!ccg.GetCC().GetDepfile().empty()) { if (!ccg.GetCC().GetDepfile().empty()) {
// Add dependency over timestamp file for dependencies management // Add dependency over timestamp file for dependencies management
auto dependTimestamp = cmSystemTools::ConvertToOutputPath( auto dependTimestamp = this->LocalGenerator->MaybeRelativeToTopBinDir(
this->LocalGenerator->MaybeRelativeToTopBinDir( cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts"));
cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")));
depends.push_back(dependTimestamp); depends.emplace_back(std::move(dependTimestamp));
} }
// Write the rule. // Write the rule.

@ -1373,8 +1373,10 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
bool const needDyndep = !isPch && bool const needDyndep = !isPch &&
this->GeneratorTarget->NeedDyndepForSource(language, config, source); this->GeneratorTarget->NeedDyndepForSource(language, config, source);
cmNinjaBuild objBuild(this->LanguageCompilerRule( WithScanning withScanning =
language, config, needDyndep ? WithScanning::Yes : WithScanning::No)); needDyndep ? WithScanning::Yes : WithScanning::No;
cmNinjaBuild objBuild(
this->LanguageCompilerRule(language, config, withScanning));
cmNinjaVars& vars = objBuild.Variables; cmNinjaVars& vars = objBuild.Variables;
vars["FLAGS"] = vars["FLAGS"] =
this->ComputeFlagsForObject(source, language, config, objectFileName); this->ComputeFlagsForObject(source, language, config, objectFileName);
@ -1434,7 +1436,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
if (firstForConfig) { if (firstForConfig) {
this->ExportObjectCompileCommand( this->ExportObjectCompileCommand(
language, sourceFilePath, objectDir, objectFileName, objectFileDir, language, sourceFilePath, objectDir, objectFileName, objectFileDir,
vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config, withScanning);
} }
objBuild.Outputs.push_back(objectFileName); objBuild.Outputs.push_back(objectFileName);
@ -1780,7 +1782,7 @@ void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement(
if (firstForConfig) { if (firstForConfig) {
this->ExportObjectCompileCommand( this->ExportObjectCompileCommand(
language, sourceFilePath, bmiDir, bmiFileName, bmiFileDir, vars["FLAGS"], language, sourceFilePath, bmiDir, bmiFileName, bmiFileDir, vars["FLAGS"],
vars["DEFINES"], vars["INCLUDES"], config); vars["DEFINES"], vars["INCLUDES"], config, WithScanning::Yes);
} }
bmiBuild.Outputs.push_back(bmiFileName); bmiBuild.Outputs.push_back(bmiFileName);
@ -1992,7 +1994,7 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand(
std::string const& objectDir, std::string const& objectFileName, std::string const& objectDir, std::string const& objectFileName,
std::string const& objectFileDir, std::string const& flags, std::string const& objectFileDir, std::string const& flags,
std::string const& defines, std::string const& includes, std::string const& defines, std::string const& includes,
std::string const& outputConfig) std::string const& outputConfig, WithScanning withScanning)
{ {
if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) { if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) {
return; return;
@ -2015,14 +2017,12 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand(
escapedSourceFileName, cmOutputConverter::SHELL); escapedSourceFileName, cmOutputConverter::SHELL);
std::string fullFlags = flags; std::string fullFlags = flags;
{ if (withScanning == WithScanning::Yes) {
bool const needDyndep =
this->GetGeneratorTarget()->NeedDyndep(language, outputConfig);
std::string const modmapFormatVar = std::string const modmapFormatVar =
cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT"); cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT");
std::string const modmapFormat = std::string const modmapFormat =
this->Makefile->GetSafeDefinition(modmapFormatVar); this->Makefile->GetSafeDefinition(modmapFormatVar);
if (needDyndep && !modmapFormat.empty()) { if (!modmapFormat.empty()) {
std::string modmapFlags = this->GetMakefile()->GetRequiredDefinition( std::string modmapFlags = this->GetMakefile()->GetRequiredDefinition(
cmStrCat("CMAKE_", language, "_MODULE_MAP_FLAG")); cmStrCat("CMAKE_", language, "_MODULE_MAP_FLAG"));
// XXX(modmap): If changing this path construction, change // XXX(modmap): If changing this path construction, change

@ -189,7 +189,7 @@ protected:
std::string const& objectDir, std::string const& objectFileName, std::string const& objectDir, std::string const& objectFileName,
std::string const& objectFileDir, std::string const& flags, std::string const& objectFileDir, std::string const& flags,
std::string const& defines, std::string const& includes, std::string const& defines, std::string const& includes,
std::string const& outputConfig); std::string const& outputConfig, WithScanning withScanning);
void AdditionalCleanFiles(const std::string& config); void AdditionalCleanFiles(const std::string& config);

@ -275,6 +275,9 @@ std::string cmOutputConverter::EscapeForShell(cm::string_view str,
if (this->GetState()->UseNMake()) { if (this->GetState()->UseNMake()) {
flags |= Shell_Flag_NMake; flags |= Shell_Flag_NMake;
} }
if (this->GetState()->UseNinja()) {
flags |= Shell_Flag_Ninja;
}
if (!this->GetState()->UseWindowsShell()) { if (!this->GetState()->UseWindowsShell()) {
flags |= Shell_Flag_IsUnix; flags |= Shell_Flag_IsUnix;
} }
@ -677,6 +680,12 @@ std::string cmOutputConverter::Shell_GetArgument(cm::string_view in, int flags)
/* Otherwise a semicolon is written just ;. */ /* Otherwise a semicolon is written just ;. */
out += ';'; out += ';';
} }
} else if (*cit == '\n') {
if (flags & Shell_Flag_Ninja) {
out += "$\n";
} else {
out += '\n';
}
} else { } else {
/* Store this character. */ /* Store this character. */
out += *cit; out += *cit;

@ -100,7 +100,10 @@ public:
Shell_Flag_UnescapeNinjaConfiguration = (1 << 9), Shell_Flag_UnescapeNinjaConfiguration = (1 << 9),
Shell_Flag_IsResponse = (1 << 10) Shell_Flag_IsResponse = (1 << 10),
/** The target shell is in a Ninja build file. */
Shell_Flag_Ninja = (1 << 11)
}; };
std::string EscapeForShell(cm::string_view str, bool makeVars = false, std::string EscapeForShell(cm::string_view str, bool makeVars = false,

@ -2,68 +2,48 @@
file Copyright.txt or https://cmake.org/licensing for details. */ file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmProcessTools.h" #include "cmProcessTools.h"
#include <algorithm>
#include <iterator>
#include <ostream> #include <ostream>
#include <cm3p/uv.h> #include "cmsys/Process.h"
#include "cmProcessOutput.h" #include "cmProcessOutput.h"
#include "cmUVHandlePtr.h"
#include "cmUVStream.h"
std::vector<cmUVProcessChain::Status> cmProcessTools::RunProcess( void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
cmUVProcessChainBuilder& builder, OutputParser* out, OutputParser* err, OutputParser* err, Encoding encoding)
Encoding encoding)
{ {
cmsysProcess_Execute(cp);
char* data = nullptr;
int length = 0;
int p;
cmProcessOutput processOutput(encoding); cmProcessOutput processOutput(encoding);
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
auto chain = builder.Start();
std::string strdata; std::string strdata;
cm::uv_pipe_ptr outputPipe; while ((out || err) &&
outputPipe.init(chain.GetLoop(), 0); (p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
uv_pipe_open(outputPipe, chain.OutputStream()); if (out && p == cmsysProcess_Pipe_STDOUT) {
auto outputHandle = cmUVStreamRead( processOutput.DecodeText(data, length, strdata, 1);
outputPipe,
[&out, &processOutput, &strdata](std::vector<char> data) {
if (out) {
processOutput.DecodeText(data.data(), data.size(), strdata, 1);
if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) { if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
out = nullptr; out = nullptr;
} }
} } else if (err && p == cmsysProcess_Pipe_STDERR) {
}, processOutput.DecodeText(data, length, strdata, 2);
[&out]() { out = nullptr; });
cm::uv_pipe_ptr errorPipe;
errorPipe.init(chain.GetLoop(), 0);
uv_pipe_open(errorPipe, chain.ErrorStream());
auto errorHandle = cmUVStreamRead(
errorPipe,
[&err, &processOutput, &strdata](std::vector<char> data) {
if (err) {
processOutput.DecodeText(data.data(), data.size(), strdata, 2);
if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) { if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
err = nullptr; err = nullptr;
} }
} }
},
[&err]() { err = nullptr; });
while (out || err || !chain.Finished()) {
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
} }
if (out) {
std::vector<cmUVProcessChain::Status> result; processOutput.DecodeText(std::string(), strdata, 1);
auto status = chain.GetStatus(); if (!strdata.empty()) {
std::transform( out->Process(strdata.c_str(), static_cast<int>(strdata.size()));
status.begin(), status.end(), std::back_inserter(result), }
[](const cmUVProcessChain::Status* s) -> cmUVProcessChain::Status { }
return *s; if (err) {
}); processOutput.DecodeText(std::string(), strdata, 2);
return result; if (!strdata.empty()) {
err->Process(strdata.c_str(), static_cast<int>(strdata.size()));
}
}
cmsysProcess_WaitForExit(cp, nullptr);
} }
cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR) cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR)

@ -7,10 +7,8 @@
#include <cstring> #include <cstring>
#include <iosfwd> #include <iosfwd>
#include <string> #include <string>
#include <vector>
#include "cmProcessOutput.h" #include "cmProcessOutput.h"
#include "cmUVProcessChain.h"
/** \class cmProcessTools /** \class cmProcessTools
* \brief Helper classes for process output parsing * \brief Helper classes for process output parsing
@ -83,7 +81,7 @@ public:
}; };
/** Run a process and send output to given parsers. */ /** Run a process and send output to given parsers. */
static std::vector<cmUVProcessChain::Status> RunProcess( static void RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
cmUVProcessChainBuilder& builder, OutputParser* out, OutputParser* err = nullptr,
OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto); Encoding encoding = cmProcessOutput::Auto);
}; };

@ -70,7 +70,7 @@ unsigned int GetParallelCPUCount()
return count; return count;
} }
std::string FileProjectRelativePath(cmMakefile* makefile, std::string FileProjectRelativePath(cmMakefile const* makefile,
std::string const& fileName) std::string const& fileName)
{ {
std::string res; std::string res;
@ -1369,7 +1369,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
std::vector<std::string> dependencies( std::vector<std::string> dependencies(
this->AutogenTarget.DependFiles.begin(), this->AutogenTarget.DependFiles.begin(),
this->AutogenTarget.DependFiles.end()); this->AutogenTarget.DependFiles.end());
if (useDepfile) { if (useDepfile) {
// Create a custom command that generates a timestamp file and // Create a custom command that generates a timestamp file and
// has a depfile assigned. The depfile is created by JobDepFilesMergeT. // has a depfile assigned. The depfile is created by JobDepFilesMergeT.
@ -1408,6 +1407,16 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
cc->SetEscapeOldStyle(false); cc->SetEscapeOldStyle(false);
cmTarget* timestampTarget = this->LocalGen->AddUtilityCommand( cmTarget* timestampTarget = this->LocalGen->AddUtilityCommand(
timestampTargetName, true, std::move(cc)); timestampTargetName, true, std::move(cc));
auto const isMake =
this->GlobalGen->GetName().find("Make") != std::string::npos;
if (this->AutogenTarget.DependOrigin && isMake) {
for (BT<std::pair<std::string, bool>> const& depName :
this->GenTarget->GetUtilities()) {
timestampTarget->AddUtility(depName.Value.first, false,
this->Makefile);
}
}
this->LocalGen->AddGeneratorTarget( this->LocalGen->AddGeneratorTarget(
cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen)); cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen));

@ -752,6 +752,16 @@ bool cmState::UseMSYSShell() const
return this->MSYSShell; return this->MSYSShell;
} }
void cmState::SetNinja(bool ninja)
{
this->Ninja = ninja;
}
bool cmState::UseNinja() const
{
return this->Ninja;
}
void cmState::SetNinjaMulti(bool ninjaMulti) void cmState::SetNinjaMulti(bool ninjaMulti)
{ {
this->NinjaMulti = ninjaMulti; this->NinjaMulti = ninjaMulti;

@ -220,6 +220,8 @@ public:
bool UseNMake() const; bool UseNMake() const;
void SetMSYSShell(bool mSYSShell); void SetMSYSShell(bool mSYSShell);
bool UseMSYSShell() const; bool UseMSYSShell() const;
void SetNinja(bool ninja);
bool UseNinja() const;
void SetNinjaMulti(bool ninjaMulti); void SetNinjaMulti(bool ninjaMulti);
bool UseNinjaMulti() const; bool UseNinjaMulti() const;
@ -297,6 +299,7 @@ private:
bool MinGWMake = false; bool MinGWMake = false;
bool NMake = false; bool NMake = false;
bool MSYSShell = false; bool MSYSShell = false;
bool Ninja = false;
bool NinjaMulti = false; bool NinjaMulti = false;
Mode StateMode = Unknown; Mode StateMode = Unknown;
ProjectKind StateProjectKind = ProjectKind::Normal; ProjectKind StateProjectKind = ProjectKind::Normal;

@ -25,17 +25,12 @@
#include <cm3p/uv.h> #include <cm3p/uv.h>
#include "cm_fileno.hxx"
#include "cmDuration.h" #include "cmDuration.h"
#include "cmELF.h" #include "cmELF.h"
#include "cmMessageMetadata.h" #include "cmMessageMetadata.h"
#include "cmProcessOutput.h" #include "cmProcessOutput.h"
#include "cmRange.h" #include "cmRange.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmUVHandlePtr.h"
#include "cmUVProcessChain.h"
#include "cmUVStream.h"
#include "cmValue.h" #include "cmValue.h"
#if !defined(CMAKE_BOOTSTRAP) #if !defined(CMAKE_BOOTSTRAP)
@ -64,14 +59,12 @@
#include <cassert> #include <cassert>
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
#include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <memory>
#include <sstream> #include <sstream>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -575,115 +568,85 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
const char* dir, OutputOption outputflag, const char* dir, OutputOption outputflag,
cmDuration timeout, Encoding encoding) cmDuration timeout, Encoding encoding)
{ {
cmUVProcessChainBuilder builder; std::vector<const char*> argv;
builder argv.reserve(command.size() + 1);
.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, cm_fileno(stdin)) for (std::string const& cmd : command) {
.AddCommand(command); argv.push_back(cmd.c_str());
if (dir) { }
builder.SetWorkingDirectory(dir); argv.push_back(nullptr);
cmsysProcess* cp = cmsysProcess_New();
cmsysProcess_SetCommand(cp, argv.data());
cmsysProcess_SetWorkingDirectory(cp, dir);
if (cmSystemTools::GetRunCommandHideConsole()) {
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
} }
if (outputflag == OUTPUT_PASSTHROUGH) { if (outputflag == OUTPUT_PASSTHROUGH) {
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
captureStdOut = nullptr; captureStdOut = nullptr;
captureStdErr = nullptr; captureStdErr = nullptr;
builder
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
cm_fileno(stdout))
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
cm_fileno(stderr));
} else if (outputflag == OUTPUT_MERGE || } else if (outputflag == OUTPUT_MERGE ||
(captureStdErr && captureStdErr == captureStdOut)) { (captureStdErr && captureStdErr == captureStdOut)) {
builder.SetMergedBuiltinStreams(); cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
captureStdErr = nullptr; captureStdErr = nullptr;
} else {
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
} }
assert(!captureStdErr || captureStdErr != captureStdOut); assert(!captureStdErr || captureStdErr != captureStdOut);
auto chain = builder.Start(); cmsysProcess_SetTimeout(cp, timeout.count());
bool timedOut = false; cmsysProcess_Execute(cp);
cm::uv_timer_ptr timer;
if (timeout.count()) {
timer.init(chain.GetLoop(), &timedOut);
timer.start(
[](uv_timer_t* t) {
auto* timedOutPtr = static_cast<bool*>(t->data);
*timedOutPtr = true;
},
static_cast<uint64_t>(timeout.count() * 1000.0), 0);
}
std::vector<char> tempStdOut; std::vector<char> tempStdOut;
std::vector<char> tempStdErr; std::vector<char> tempStdErr;
cm::uv_pipe_ptr outStream; char* data;
bool outFinished = true; int length;
cm::uv_pipe_ptr errStream; int pipe;
bool errFinished = true;
cmProcessOutput processOutput(encoding); cmProcessOutput processOutput(encoding);
std::unique_ptr<cmUVStreamReadHandle> outputHandle; std::string strdata;
std::unique_ptr<cmUVStreamReadHandle> errorHandle;
if (outputflag != OUTPUT_PASSTHROUGH && if (outputflag != OUTPUT_PASSTHROUGH &&
(captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) { (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
auto startRead = while ((pipe = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) >
[&outputflag, &processOutput, 0) {
&chain](cm::uv_pipe_ptr& pipe, int stream, std::string* captureStd,
std::vector<char>& tempStd, int id,
void (*outputFunc)(const std::string&),
bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
if (stream < 0) {
return nullptr;
}
pipe.init(chain.GetLoop(), 0);
uv_pipe_open(pipe, stream);
finished = false;
return cmUVStreamRead(
pipe,
[outputflag, &processOutput, captureStd, &tempStd, id,
outputFunc](std::vector<char> data) {
// Translate NULL characters in the output into valid text. // Translate NULL characters in the output into valid text.
for (auto& c : data) { for (int i = 0; i < length; ++i) {
if (c == '\0') { if (data[i] == '\0') {
c = ' '; data[i] = ' ';
} }
} }
if (pipe == cmsysProcess_Pipe_STDOUT) {
if (outputflag != OUTPUT_NONE) { if (outputflag != OUTPUT_NONE) {
std::string strdata; processOutput.DecodeText(data, length, strdata, 1);
processOutput.DecodeText(data.data(), data.size(), strdata, id); cmSystemTools::Stdout(strdata);
outputFunc(strdata);
} }
if (captureStd) { if (captureStdOut) {
cm::append(tempStd, data.data(), data.data() + data.size()); cm::append(tempStdOut, data, data + length);
} }
}, } else if (pipe == cmsysProcess_Pipe_STDERR) {
[&finished, outputflag, &processOutput, id, outputFunc]() {
finished = true;
if (outputflag != OUTPUT_NONE) { if (outputflag != OUTPUT_NONE) {
std::string strdata; processOutput.DecodeText(data, length, strdata, 2);
processOutput.DecodeText(std::string(), strdata, id); cmSystemTools::Stderr(strdata);
if (!strdata.empty()) {
outputFunc(strdata);
} }
if (captureStdErr) {
cm::append(tempStdErr, data, data + length);
} }
});
};
outputHandle =
startRead(outStream, chain.OutputStream(), captureStdOut, tempStdOut, 1,
cmSystemTools::Stdout, outFinished);
if (chain.OutputStream() != chain.ErrorStream()) {
errorHandle =
startRead(errStream, chain.ErrorStream(), captureStdErr, tempStdErr, 2,
cmSystemTools::Stderr, errFinished);
} }
} }
while (!timedOut && !(chain.Finished() && outFinished && errFinished)) { if (outputflag != OUTPUT_NONE) {
uv_run(&chain.GetLoop(), UV_RUN_ONCE); processOutput.DecodeText(std::string(), strdata, 1);
if (!strdata.empty()) {
cmSystemTools::Stdout(strdata);
}
processOutput.DecodeText(std::string(), strdata, 2);
if (!strdata.empty()) {
cmSystemTools::Stderr(strdata);
}
} }
}
cmsysProcess_WaitForExit(cp, nullptr);
if (captureStdOut) { if (captureStdOut) {
captureStdOut->assign(tempStdOut.begin(), tempStdOut.end()); captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
@ -695,43 +658,48 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
} }
bool result = true; bool result = true;
if (timedOut) { if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
const char* error_str = "Process terminated due to timeout\n";
if (outputflag != OUTPUT_NONE) {
std::cerr << error_str << std::endl;
}
if (captureStdErr) {
captureStdErr->append(error_str, strlen(error_str));
}
result = false;
} else {
auto const& status = chain.GetStatus(0);
auto exception = status.GetException();
switch (exception.first) {
case cmUVProcessChain::ExceptionCode::None:
if (retVal) { if (retVal) {
*retVal = static_cast<int>(status.ExitStatus); *retVal = cmsysProcess_GetExitValue(cp);
} else { } else {
if (status.ExitStatus != 0) { if (cmsysProcess_GetExitValue(cp) != 0) {
result = false; result = false;
} }
} }
break; } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
default: { const char* exception_str = cmsysProcess_GetExceptionString(cp);
if (outputflag != OUTPUT_NONE) { if (outputflag != OUTPUT_NONE) {
std::cerr << exception.second << std::endl; std::cerr << exception_str << std::endl;
} }
if (captureStdErr) { if (captureStdErr) {
captureStdErr->append(exception.second); captureStdErr->append(exception_str, strlen(exception_str));
} else if (captureStdOut) { } else if (captureStdOut) {
captureStdOut->append(exception.second); captureStdOut->append(exception_str, strlen(exception_str));
} }
result = false; result = false;
} break; } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
const char* error_str = cmsysProcess_GetErrorString(cp);
if (outputflag != OUTPUT_NONE) {
std::cerr << error_str << std::endl;
} }
if (captureStdErr) {
captureStdErr->append(error_str, strlen(error_str));
} else if (captureStdOut) {
captureStdOut->append(error_str, strlen(error_str));
}
result = false;
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
const char* error_str = "Process terminated due to timeout\n";
if (outputflag != OUTPUT_NONE) {
std::cerr << error_str << std::endl;
}
if (captureStdErr) {
captureStdErr->append(error_str, strlen(error_str));
}
result = false;
} }
cmsysProcess_Delete(cp);
return result; return result;
} }
@ -2245,9 +2213,8 @@ bool cmSystemTools::ListTar(const std::string& outFileName,
#endif #endif
} }
cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine( int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
uv_loop_t* loop, uv_stream_t* outPipe, uv_stream_t* errPipe, cmDuration timeout, std::vector<char>& out,
std::string& line, cmDuration timeout, std::vector<char>& out,
std::vector<char>& err) std::vector<char>& err)
{ {
line.clear(); line.clear();
@ -2270,7 +2237,7 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
line.append(out.data(), length); line.append(out.data(), length);
} }
out.erase(out.begin(), outiter + 1); out.erase(out.begin(), outiter + 1);
return WaitForLineResult::STDOUT; return cmsysProcess_Pipe_STDOUT;
} }
} }
@ -2288,66 +2255,33 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
line.append(err.data(), length); line.append(err.data(), length);
} }
err.erase(err.begin(), erriter + 1); err.erase(err.begin(), erriter + 1);
return WaitForLineResult::STDERR; return cmsysProcess_Pipe_STDERR;
} }
} }
// No newlines found. Wait for more data from the process. // No newlines found. Wait for more data from the process.
struct ReadData int length;
{ char* data;
uv_stream_t* Stream; double timeoutAsDbl = timeout.count();
std::vector<char> Buffer; int pipe =
bool Read = false; cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl);
bool Finished = false; if (pipe == cmsysProcess_Pipe_Timeout) {
};
auto startRead =
[](uv_stream_t* stream,
ReadData& data) -> std::unique_ptr<cmUVStreamReadHandle> {
data.Stream = stream;
return cmUVStreamRead(
stream,
[&data](std::vector<char> buf) {
data.Buffer = std::move(buf);
data.Read = true;
uv_read_stop(data.Stream);
},
[&data]() { data.Finished = true; });
};
ReadData outData;
auto outHandle = startRead(outPipe, outData);
ReadData errData;
auto errHandle = startRead(errPipe, errData);
cm::uv_timer_ptr timer;
bool timedOut = false;
timer.init(*loop, &timedOut);
timer.start(
[](uv_timer_t* handle) {
auto* timedOutPtr = static_cast<bool*>(handle->data);
*timedOutPtr = true;
},
static_cast<uint64_t>(timeout.count() * 1000.0), 0);
uv_run(loop, UV_RUN_ONCE);
if (timedOut) {
// Timeout has been exceeded. // Timeout has been exceeded.
return WaitForLineResult::Timeout; return pipe;
} }
if (outData.Read) { if (pipe == cmsysProcess_Pipe_STDOUT) {
processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(), processOutput.DecodeText(data, length, strdata, 1);
strdata, 1);
// Append to the stdout buffer. // Append to the stdout buffer.
std::vector<char>::size_type size = out.size(); std::vector<char>::size_type size = out.size();
cm::append(out, strdata); cm::append(out, strdata);
outiter = out.begin() + size; outiter = out.begin() + size;
} else if (errData.Read) { } else if (pipe == cmsysProcess_Pipe_STDERR) {
processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(), processOutput.DecodeText(data, length, strdata, 2);
strdata, 2);
// Append to the stderr buffer. // Append to the stderr buffer.
std::vector<char>::size_type size = err.size(); std::vector<char>::size_type size = err.size();
cm::append(err, strdata); cm::append(err, strdata);
erriter = err.begin() + size; erriter = err.begin() + size;
} else if (outData.Finished && errData.Finished) { } else if (pipe == cmsysProcess_Pipe_None) {
// Both stdout and stderr pipes have broken. Return leftover data. // Both stdout and stderr pipes have broken. Return leftover data.
processOutput.DecodeText(std::string(), strdata, 1); processOutput.DecodeText(std::string(), strdata, 1);
if (!strdata.empty()) { if (!strdata.empty()) {
@ -2364,20 +2298,14 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
if (!out.empty()) { if (!out.empty()) {
line.append(out.data(), outiter - out.begin()); line.append(out.data(), outiter - out.begin());
out.erase(out.begin(), out.end()); out.erase(out.begin(), out.end());
return WaitForLineResult::STDOUT; return cmsysProcess_Pipe_STDOUT;
} }
if (!err.empty()) { if (!err.empty()) {
line.append(err.data(), erriter - err.begin()); line.append(err.data(), erriter - err.begin());
err.erase(err.begin(), err.end()); err.erase(err.begin(), err.end());
return WaitForLineResult::STDERR; return cmsysProcess_Pipe_STDERR;
}
return WaitForLineResult::None;
}
if (!outData.Finished) {
uv_read_stop(outPipe);
} }
if (!errData.Finished) { return cmsysProcess_Pipe_None;
uv_read_stop(errPipe);
} }
} }
} }

@ -18,8 +18,7 @@
#include <cm/optional> #include <cm/optional>
#include <cm/string_view> #include <cm/string_view>
#include <cm3p/uv.h> #include "cmsys/Process.h"
#include "cmsys/Status.hxx" // IWYU pragma: export #include "cmsys/Status.hxx" // IWYU pragma: export
#include "cmsys/SystemTools.hxx" // IWYU pragma: export #include "cmsys/SystemTools.hxx" // IWYU pragma: export
@ -340,19 +339,9 @@ public:
*/ */
static void ReportLastSystemError(const char* m); static void ReportLastSystemError(const char* m);
enum class WaitForLineResult /** a general output handler for cmsysProcess */
{ static int WaitForLine(cmsysProcess* process, std::string& line,
None, cmDuration timeout, std::vector<char>& out,
STDOUT,
STDERR,
Timeout,
};
/** a general output handler for libuv */
static WaitForLineResult WaitForLine(uv_loop_t* loop, uv_stream_t* outPipe,
uv_stream_t* errPipe, std::string& line,
cmDuration timeout,
std::vector<char>& out,
std::vector<char>& err); std::vector<char>& err);
static void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; } static void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; }

@ -12,6 +12,8 @@
#include <cm3p/uv.h> #include <cm3p/uv.h>
#include "cm_fileno.hxx"
#include "cmGetPipes.h" #include "cmGetPipes.h"
#include "cmUVHandlePtr.h" #include "cmUVHandlePtr.h"
@ -57,12 +59,7 @@ struct cmUVProcessChain::InternalData
void Finish(); void Finish();
}; };
cmUVProcessChainBuilder::cmUVProcessChainBuilder() cmUVProcessChainBuilder::cmUVProcessChainBuilder() = default;
{
this->SetNoStream(Stream_INPUT)
.SetNoStream(Stream_OUTPUT)
.SetNoStream(Stream_ERROR);
}
cmUVProcessChainBuilder& cmUVProcessChainBuilder::AddCommand( cmUVProcessChainBuilder& cmUVProcessChainBuilder::AddCommand(
const std::vector<std::string>& arguments) const std::vector<std::string>& arguments)
@ -122,6 +119,16 @@ cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalStream(
return *this; return *this;
} }
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalStream(
Stream stdio, FILE* stream)
{
int fd = cm_fileno(stream);
if (fd >= 0) {
return this->SetExternalStream(stdio, fd);
}
return this->SetNoStream(stdio);
}
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetMergedBuiltinStreams() cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetMergedBuiltinStreams()
{ {
this->MergedBuiltinStreams = true; this->MergedBuiltinStreams = true;

@ -7,6 +7,7 @@
#include <array> #include <array>
#include <cstddef> // IWYU pragma: keep #include <cstddef> // IWYU pragma: keep
#include <cstdint> #include <cstdint>
#include <cstdio>
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
@ -34,6 +35,7 @@ public:
cmUVProcessChainBuilder& SetBuiltinStream(Stream stdio); cmUVProcessChainBuilder& SetBuiltinStream(Stream stdio);
cmUVProcessChainBuilder& SetMergedBuiltinStreams(); cmUVProcessChainBuilder& SetMergedBuiltinStreams();
cmUVProcessChainBuilder& SetExternalStream(Stream stdio, int fd); cmUVProcessChainBuilder& SetExternalStream(Stream stdio, int fd);
cmUVProcessChainBuilder& SetExternalStream(Stream stdio, FILE* stream);
cmUVProcessChainBuilder& SetWorkingDirectory(std::string dir); cmUVProcessChainBuilder& SetWorkingDirectory(std::string dir);
cmUVProcessChain Start() const; cmUVProcessChain Start() const;
@ -50,8 +52,8 @@ private:
struct StdioConfiguration struct StdioConfiguration
{ {
StdioType Type; StdioType Type = None;
int FileDescriptor; int FileDescriptor = -1;
}; };
struct ProcessConfiguration struct ProcessConfiguration

@ -2784,7 +2784,7 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
isCppModule = true; isCppModule = true;
if (shouldScanForModules && if (shouldScanForModules &&
this->GlobalGenerator->IsScanDependenciesSupported()) { this->GlobalGenerator->IsScanDependenciesSupported()) {
// ScanSourceforModuleDependencies uses 'cl /scanDependencies' and // ScanSourceForModuleDependencies uses 'cl /scanDependencies' and
// can distinguish module interface units and internal partitions. // can distinguish module interface units and internal partitions.
compileAsPerConfig = "CompileAsCpp"; compileAsPerConfig = "CompileAsCpp";
} else { } else {
@ -2827,7 +2827,7 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
// use them // use them
if (!flags.empty() || !options.empty() || !configDefines.empty() || if (!flags.empty() || !options.empty() || !configDefines.empty() ||
!includes.empty() || compileAsPerConfig || noWinRT || !includes.empty() || compileAsPerConfig || noWinRT ||
!options.empty() || needsPCHFlags) { !options.empty() || needsPCHFlags || shouldScanForModules) {
cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator;
cmIDEFlagTable const* flagtable = nullptr; cmIDEFlagTable const* flagtable = nullptr;
const std::string& srclang = source->GetLanguage(); const std::string& srclang = source->GetLanguage();
@ -2856,9 +2856,7 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
clOptions.AddFlag("CompileAs", compileAsPerConfig); clOptions.AddFlag("CompileAs", compileAsPerConfig);
} }
if (shouldScanForModules) { if (shouldScanForModules) {
clOptions.AddFlag("ScanSourceforModuleDependencies", "true"); clOptions.AddFlag("ScanSourceForModuleDependencies", "true");
} else {
clOptions.AddFlag("ScanSourceforModuleDependencies", "false");
} }
if (noWinRT) { if (noWinRT) {
clOptions.AddFlag("CompileAsWinRT", "false"); clOptions.AddFlag("CompileAsWinRT", "false");
@ -3564,6 +3562,9 @@ void cmVisualStudio10TargetGenerator::WriteClOptions(
e2.Element("AdditionalUsingDirectories", dirs); e2.Element("AdditionalUsingDirectories", dirs);
} }
} }
// Disable C++ source scanning by default.
e2.Element("ScanSourceForModuleDependencies", "false");
} }
bool cmVisualStudio10TargetGenerator::ComputeRcOptions() bool cmVisualStudio10TargetGenerator::ComputeRcOptions()

@ -27,7 +27,6 @@
#include "cmsys/Glob.hxx" #include "cmsys/Glob.hxx"
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cm_fileno.hxx"
#include "cm_sys_stat.h" #include "cm_sys_stat.h"
#include "cmBuildOptions.h" #include "cmBuildOptions.h"
@ -3916,10 +3915,8 @@ std::function<int()> cmake::BuildWorkflowStep(
{ {
cmUVProcessChainBuilder builder; cmUVProcessChainBuilder builder;
builder.AddCommand(args) builder.AddCommand(args)
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
cm_fileno(stdout)) .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
cm_fileno(stderr));
return [builder]() -> int { return [builder]() -> int {
auto chain = builder.Start(); auto chain = builder.Start();
chain.Wait(); chain.Wait();

@ -11,8 +11,6 @@
#include <cm3p/uv.h> #include <cm3p/uv.h>
#include <fcntl.h> #include <fcntl.h>
#include "cm_fileno.hxx"
#include "cmCommandLineArgument.h" #include "cmCommandLineArgument.h"
#include "cmConsoleBuf.h" #include "cmConsoleBuf.h"
#include "cmCryptoHash.h" #include "cmCryptoHash.h"
@ -74,6 +72,7 @@
#include "cmsys/Directory.hxx" #include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cmsys/Terminal.h" #include "cmsys/Terminal.h"
@ -296,8 +295,14 @@ int CLCompileAndDependencies(const std::vector<std::string>& args)
} }
} }
cmUVProcessChainBuilder builder; std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp(
builder.AddCommand(command).SetWorkingDirectory(currentBinaryDir); cmsysProcess_New(), cmsysProcess_Delete);
std::vector<const char*> argv(command.size() + 1);
std::transform(command.begin(), command.end(), argv.begin(),
[](std::string const& s) { return s.c_str(); });
argv.back() = nullptr;
cmsysProcess_SetCommand(cp.get(), argv.data());
cmsysProcess_SetWorkingDirectory(cp.get(), currentBinaryDir.c_str());
cmsys::ofstream fout(depFile.c_str()); cmsys::ofstream fout(depFile.c_str());
if (!fout) { if (!fout) {
@ -308,18 +313,22 @@ int CLCompileAndDependencies(const std::vector<std::string>& args)
CLOutputLogger errLogger(std::cerr); CLOutputLogger errLogger(std::cerr);
// Start the process. // Start the process.
auto result = cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger);
cmProcessTools::RunProcess(builder, &includeParser, &errLogger);
auto const& subStatus = result.front();
int status = 0; int status = 0;
// handle status of process // handle status of process
if (subStatus.SpawnResult != 0) { switch (cmsysProcess_GetState(cp.get())) {
status = 2; case cmsysProcess_State_Exited:
} else if (subStatus.TermSignal != 0) { status = cmsysProcess_GetExitValue(cp.get());
break;
case cmsysProcess_State_Exception:
status = 1; status = 1;
} else { break;
status = static_cast<int>(subStatus.ExitStatus); case cmsysProcess_State_Error:
status = 2;
break;
default:
break;
} }
if (status != 0) { if (status != 0) {
@ -1107,8 +1116,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
int ret = 0; int ret = 0;
auto time_start = std::chrono::steady_clock::now(); auto time_start = std::chrono::steady_clock::now();
cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret, nullptr, cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret);
cmSystemTools::OUTPUT_PASSTHROUGH);
auto time_finish = std::chrono::steady_clock::now(); auto time_finish = std::chrono::steady_clock::now();
std::chrono::duration<double> time_elapsed = time_finish - time_start; std::chrono::duration<double> time_elapsed = time_finish - time_start;
@ -1882,6 +1890,21 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
} }
} }
// Allocate a process instance.
cmsysProcess* cp = cmsysProcess_New();
if (!cp) {
std::cerr << "Error allocating process instance in link script."
<< std::endl;
return 1;
}
// Children should share stdout and stderr with this process.
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
// Run the command lines verbatim.
cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1);
// Read command lines from the script. // Read command lines from the script.
cmsys::ifstream fin(args[2].c_str()); cmsys::ifstream fin(args[2].c_str());
if (!fin) { if (!fin) {
@ -1899,24 +1922,9 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
continue; continue;
} }
// Allocate a process instance.
cmUVProcessChainBuilder builder;
// Children should share stdout and stderr with this process.
builder
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
cm_fileno(stdout))
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
cm_fileno(stderr));
// Setup this command line. // Setup this command line.
std::vector<std::string> args2; const char* cmd[2] = { command.c_str(), nullptr };
#ifdef _WIN32 cmsysProcess_SetCommand(cp, cmd);
cmSystemTools::ParseWindowsCommandLine(command.c_str(), args2);
#else
cmSystemTools::ParseUnixCommandLine(command.c_str(), args2);
#endif
builder.AddCommand(args2);
// Report the command if verbose output is enabled. // Report the command if verbose output is enabled.
if (verbose) { if (verbose) {
@ -1924,29 +1932,35 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
} }
// Run the command and wait for it to exit. // Run the command and wait for it to exit.
auto chain = builder.Start(); cmsysProcess_Execute(cp);
chain.Wait(); cmsysProcess_WaitForExit(cp, nullptr);
// Report failure if any. // Report failure if any.
auto const& status = chain.GetStatus(0); switch (cmsysProcess_GetState(cp)) {
auto exception = status.GetException(); case cmsysProcess_State_Exited: {
switch (exception.first) { int value = cmsysProcess_GetExitValue(cp);
case cmUVProcessChain::ExceptionCode::None: if (value != 0) {
if (status.ExitStatus != 0) { result = value;
result = static_cast<int>(status.ExitStatus);
} }
} break;
case cmsysProcess_State_Exception:
std::cerr << "Error running link command: "
<< cmsysProcess_GetExceptionString(cp) << std::endl;
result = 1;
break; break;
case cmUVProcessChain::ExceptionCode::Spawn: case cmsysProcess_State_Error:
std::cerr << "Error running link command: " << exception.second; std::cerr << "Error running link command: "
<< cmsysProcess_GetErrorString(cp) << std::endl;
result = 2; result = 2;
break; break;
default: default:
std::cerr << "Error running link command: " << exception.second;
result = 1;
break; break;
} }
} }
// Free the process instance.
cmsysProcess_Delete(cp);
// Return the final resulting return value. // Return the final resulting return value.
return result; return result;
} }

@ -12,8 +12,6 @@
#include <cm3p/uv.h> #include <cm3p/uv.h>
#include "cm_fileno.hxx"
#include "cmGetPipes.h" #include "cmGetPipes.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmUVHandlePtr.h" #include "cmUVHandlePtr.h"
@ -632,8 +630,7 @@ bool testUVProcessChainInputFile(const char* helperCommand)
cmUVProcessChainBuilder builder; cmUVProcessChainBuilder builder;
builder.AddCommand({ helperCommand, "dedup" }) builder.AddCommand({ helperCommand, "dedup" })
.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, .SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, f.get())
cm_fileno(f.get()))
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
auto chain = builder.Start(); auto chain = builder.Start();

@ -9,6 +9,10 @@
#include "cmSystemTools.h" #include "cmSystemTools.h"
#ifdef _WIN32
# include <windows.h>
#endif
static std::string getStdin() static std::string getStdin()
{ {
char buffer[1024]; char buffer[1024];

@ -25,6 +25,13 @@ add_CMakeOnly_test(CheckCXXSymbolExists)
add_CMakeOnly_test(CheckCXXCompilerFlag) add_CMakeOnly_test(CheckCXXCompilerFlag)
add_CMakeOnly_test(CheckLanguage) add_CMakeOnly_test(CheckLanguage)
if (CMake_TEST_HIP)
set_property(TEST CMakeOnly.CheckLanguage APPEND PROPERTY LABELS "HIP")
add_CMakeOnly_test(CheckLanguageHIPPlatform)
set_property(TEST CMakeOnly.CheckLanguageHIPPlatform APPEND PROPERTY LABELS "HIP")
add_CMakeOnly_test(CheckLanguageHIPPlatform2)
set_property(TEST CMakeOnly.CheckLanguageHIPPlatform2 APPEND PROPERTY LABELS "HIP")
endif()
add_CMakeOnly_test(CheckStructHasMember) add_CMakeOnly_test(CheckStructHasMember)

@ -0,0 +1,17 @@
cmake_minimum_required (VERSION 3.28)
project(CheckLanguageHIPPlatform NONE)
include(CheckLanguage)
check_language(HIP)
if(NOT DEFINED CMAKE_HIP_COMPILER)
message(FATAL_ERROR "check_language did not set result")
endif()
if (NOT CMAKE_HIP_COMPILER)
message(FATAL_ERROR "check_language should not fail!")
endif()
if (NOT DEFINED CMAKE_HIP_PLATFORM)
message(FATAL_ERROR "check_language did not set CMAKE_HIP_PLATFORM!")
endif()

@ -0,0 +1,14 @@
cmake_minimum_required (VERSION 3.28)
project(CheckLanguageHIPPlatform2 NONE)
include(CheckLanguage)
set(CMAKE_HIP_PLATFORM "not-a-hip-platform" CACHE STRING "")
check_language(HIP)
if(NOT DEFINED CMAKE_HIP_COMPILER)
message(FATAL_ERROR "check_language did not set result")
endif()
if (CMAKE_HIP_COMPILER)
message(FATAL_ERROR "check_language should have failed")
endif()

@ -14,6 +14,7 @@ add_cuda_test_macro(Cuda.MixedStandardLevels5 MixedStandardLevels5)
add_cuda_test_macro(Cuda.NotEnabled CudaNotEnabled) add_cuda_test_macro(Cuda.NotEnabled CudaNotEnabled)
add_cuda_test_macro(Cuda.SeparableCompCXXOnly SeparableCompCXXOnly) add_cuda_test_macro(Cuda.SeparableCompCXXOnly SeparableCompCXXOnly)
add_cuda_test_macro(Cuda.StubRPATH StubRPATH) add_cuda_test_macro(Cuda.StubRPATH StubRPATH)
set(Cuda.Toolkit_BUILD_OPTIONS -DHAS_CUPTI:BOOL=${CMake_TEST_CUDA_CUPTI})
add_cuda_test_macro(Cuda.Toolkit Toolkit) add_cuda_test_macro(Cuda.Toolkit Toolkit)
add_cuda_test_macro(Cuda.IncludePathNoToolkit IncludePathNoToolkit) add_cuda_test_macro(Cuda.IncludePathNoToolkit IncludePathNoToolkit)
add_cuda_test_macro(Cuda.SharedRuntimePlusToolkit SharedRuntimePlusToolkit) add_cuda_test_macro(Cuda.SharedRuntimePlusToolkit SharedRuntimePlusToolkit)

@ -79,6 +79,23 @@ endforeach()
add_executable(Toolkit main.cpp) add_executable(Toolkit main.cpp)
target_link_libraries(Toolkit PRIVATE CUDA::toolkit) target_link_libraries(Toolkit PRIVATE CUDA::toolkit)
if(HAS_CUPTI)
set(cupti_libs )
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 10.2)
list(APPEND cupti_libs cupti nvperf_target)
endif()
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.3)
list(APPEND cupti_libs pcsamplingutil)
endif()
foreach (cuda_lib IN LISTS cupti_libs)
if(NOT CUDA_${cuda_lib}_LIBRARY)
message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY not found")
endif()
if(NOT TARGET CUDA::${cuda_lib})
message(FATAL_ERROR "expected CUDAToolkit target CUDA::${cuda_lib} not found")
endif()
endforeach()
endif()
# cupti is an optional component of the CUDA toolkit # cupti is an optional component of the CUDA toolkit
if(TARGET CUDA::cupti) if(TARGET CUDA::cupti)
add_executable(cupti cupti.cpp) add_executable(cupti cupti.cpp)

@ -12,6 +12,7 @@ add_cuda_test_macro(CudaOnly.ExportPTX CudaOnlyExportPTX)
add_cuda_test_macro(CudaOnly.SharedRuntimePlusToolkit CudaOnlySharedRuntimePlusToolkit) add_cuda_test_macro(CudaOnly.SharedRuntimePlusToolkit CudaOnlySharedRuntimePlusToolkit)
add_cuda_test_macro(CudaOnly.StaticRuntimePlusToolkit CudaOnlyStaticRuntimePlusToolkit) add_cuda_test_macro(CudaOnly.StaticRuntimePlusToolkit CudaOnlyStaticRuntimePlusToolkit)
add_cuda_test_macro(CudaOnly.Standard98 CudaOnlyStandard98) add_cuda_test_macro(CudaOnly.Standard98 CudaOnlyStandard98)
set(CudaOnly.Toolkit_BUILD_OPTIONS -DHAS_CUPTI:BOOL=${CMake_TEST_CUDA_CUPTI})
add_cuda_test_macro(CudaOnly.Toolkit CudaOnlyToolkit) add_cuda_test_macro(CudaOnly.Toolkit CudaOnlyToolkit)
add_cuda_test_macro(CudaOnly.ToolkitBeforeLang CudaOnlyToolkitBeforeLang) add_cuda_test_macro(CudaOnly.ToolkitBeforeLang CudaOnlyToolkitBeforeLang)
add_cuda_test_macro(CudaOnly.ToolkitMultipleDirs CudaOnlyToolkitMultipleDirs) add_cuda_test_macro(CudaOnly.ToolkitMultipleDirs CudaOnlyToolkitMultipleDirs)

@ -41,7 +41,6 @@ if(CUDAToolkit_VERSION_MAJOR VERSION_LESS 11)
list(APPEND cuda_libs nvgraph) list(APPEND cuda_libs nvgraph)
endif() endif()
# Verify that all the CUDA:: targets and variables exist # Verify that all the CUDA:: targets and variables exist
foreach (cuda_lib IN LISTS cuda_libs) foreach (cuda_lib IN LISTS cuda_libs)
if(NOT CUDA_${cuda_lib}_LIBRARY) if(NOT CUDA_${cuda_lib}_LIBRARY)
@ -81,5 +80,24 @@ foreach (cuda_lib nvrtc nvToolsExt OpenCL)
endif() endif()
endforeach() endforeach()
if(HAS_CUPTI)
set(cupti_libs )
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 10.2)
list(APPEND cupti_libs cupti nvperf_target)
endif()
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.3)
list(APPEND cupti_libs pcsamplingutil)
endif()
foreach (cuda_lib IN LISTS cupti_libs)
if(NOT CUDA_${cuda_lib}_LIBRARY)
message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY not found")
endif()
if(NOT TARGET CUDA::${cuda_lib})
message(FATAL_ERROR "expected CUDAToolkit target CUDA::${cuda_lib} not found")
endif()
endforeach()
endif()
add_executable(CudaOnlyToolkit main.cu) add_executable(CudaOnlyToolkit main.cu)
target_link_libraries(CudaOnlyToolkit PRIVATE CUDA::toolkit) target_link_libraries(CudaOnlyToolkit PRIVATE CUDA::toolkit)

@ -16,14 +16,14 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
{ {
char* file; char* file;
char* str; char* str;
char* srcs; const char* srcs;
const char* cstr; const char* cstr;
char buffer[1024]; char buffer[1024];
void* source_file; void* source_file;
char* args[2]; char* args[2];
char* ccArgs[4]; const char* ccArgs[4];
char* ccDep[1]; const char* ccDep[1];
char* ccOut[1]; const char* ccOut[1];
cmLoadedCommandInfo* info = (cmLoadedCommandInfo*)inf; cmLoadedCommandInfo* info = (cmLoadedCommandInfo*)inf;
cmVTKWrapTclData* cdata = cmVTKWrapTclData* cdata =
@ -148,7 +148,10 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
if (info->CAPI->GetTotalArgumentSize(2, args) != 13) { if (info->CAPI->GetTotalArgumentSize(2, args) != 13) {
return 0; return 0;
} }
info->CAPI->ExecuteCommand(mf, "SET", 2, args);
ccArgs[0] = "TEST_EXEC";
ccArgs[1] = "TRUE";
info->CAPI->ExecuteCommand(mf, "SET", 2, ccArgs);
/* make sure we can find the source file */ /* make sure we can find the source file */
if (!info->CAPI->GetSource(mf, argv[1])) { if (!info->CAPI->GetSource(mf, argv[1])) {

@ -16,14 +16,14 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
{ {
char* file; char* file;
char* str; char* str;
char* srcs; const char* srcs;
const char* cstr; char* cstr;
char buffer[1024]; char buffer[1024];
void* source_file; void* source_file;
char* args[2]; char* args[2];
char* ccArgs[4]; const char* ccArgs[4];
char* ccDep[1]; const char* ccDep[1];
char* ccOut[1]; const char* ccOut[1];
cmLoadedCommandInfo* info = (cmLoadedCommandInfo*)inf; cmLoadedCommandInfo* info = (cmLoadedCommandInfo*)inf;
cmVTKWrapTclData* cdata = cmVTKWrapTclData* cdata =
@ -148,7 +148,10 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
if (info->CAPI->GetTotalArgumentSize(2, args) != 13) { if (info->CAPI->GetTotalArgumentSize(2, args) != 13) {
return 0; return 0;
} }
info->CAPI->ExecuteCommand(mf, "SET", 2, args);
ccArgs[0] = "TEST_EXEC";
ccArgs[1] = "TRUE";
info->CAPI->ExecuteCommand(mf, "SET", 2, ccArgs);
/* make sure we can find the source file */ /* make sure we can find the source file */
if (!info->CAPI->GetSource(mf, argv[1])) { if (!info->CAPI->GetSource(mf, argv[1])) {

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.28)
project(AutogenTimestampDeps)
include("../AutogenCoreTest.cmake")
set(CMAKE_AUTOMOC ON)
add_custom_target(ProjectInfo
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/UpdateProjectInfo.cmake
BYPRODUCTS ${CMAKE_BINARY_DIR}/ProjectInfo.hpp)
add_subdirectory(src)

@ -0,0 +1,6 @@
#ifndef PROJECTINFO_HPP
#define PROJECTINFO_HPP
extern int VersionMajor;
#endif // PROJECTINFO_HPP

@ -0,0 +1,2 @@
configure_file(${CMAKE_CURRENT_LIST_DIR}/../ProjectInfo.hpp.in ${CMAKE_BINARY_DIR}/ProjectInfo.hpp @ONLY)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save