diff options
96 files changed, 1489 insertions, 1329 deletions
diff --git a/.gitlab/ci/env_windows_vs2022_x64.ps1 b/.gitlab/ci/env_windows_vs2022_x64.ps1 new file mode 100755 index 0000000..42aec11 --- /dev/null +++ b/.gitlab/ci/env_windows_vs2022_x64.ps1 @@ -0,0 +1,4 @@ +if ("$env:CMAKE_CI_NIGHTLY" -eq "true") { + $cmake = "build\install\bin\cmake" + . ".gitlab/ci/qt-env.ps1" +} diff --git a/.gitlab/ci/qt-env.ps1 b/.gitlab/ci/qt-env.ps1 index 7eff55f..22b1099 100755 --- a/.gitlab/ci/qt-env.ps1 +++ b/.gitlab/ci/qt-env.ps1 @@ -1,6 +1,9 @@ +if ($cmake -eq $null) { + throw ('$cmake powershell variable not set ') +} if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") { $pwdpath = $pwd.Path - cmake -P .gitlab/ci/download_qt.cmake + & $cmake -P .gitlab/ci/download_qt.cmake Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\qt\bin;$env:PATH" qmake -v } elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") { diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index 422e147..81944cb 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml @@ -366,6 +366,7 @@ - Set-Item -Force -Path "env:WIX" -Value "$pwdpath\.gitlab\wix" - (& "$pwsh" -File ".gitlab/ci/cmake.ps1") - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\cmake\bin;$env:PATH" + - $cmake = "cmake" - . .gitlab/ci/ninja-env.ps1 - (& "$env:WIX\bin\light.exe" -help) | Select -First 1 - cmake --version diff --git a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst index 5e803f5..e7aff9c 100644 --- a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst +++ b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst @@ -245,10 +245,9 @@ then use :command:`target_compile_features` to add the compiler feature </details> Finally, with our interface library set up, we need to link our -executable ``Target``, our ``MathFunctions`` library, and our ``SqrtLibrary`` -library to our new -``tutorial_compiler_flags`` library. Respectively, the code will look like -this: +executable ``Tutorial``, our ``SqrtLibrary`` library and our ``MathFunctions`` +library to our new ``tutorial_compiler_flags`` library. Respectively, the code +will look like this: .. raw:: html @@ -275,7 +274,7 @@ this: :caption: TODO 6: MathFunctions/CMakeLists.txt :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4 :language: cmake - :start-after: # link our compiler flags interface library + :start-after: # link SqrtLibrary to tutorial_compiler_flags :end-before: target_link_libraries(MathFunctions .. raw:: html @@ -292,8 +291,7 @@ and this: :caption: TODO 7: MathFunctions/CMakeLists.txt :name: MathFunctions-SqrtLibrary-target_link_libraries-step4 :language: cmake - :start-after: # link our compiler flags interface library - :end-before: target_link_libraries(MathFunctions PUBLIC SqrtLibrary) + :start-after: # link MathFunctions to tutorial_compiler_flags .. raw:: html diff --git a/Help/guide/tutorial/Adding a Library.rst b/Help/guide/tutorial/Adding a Library.rst index 178334a..18ced97 100644 --- a/Help/guide/tutorial/Adding a Library.rst +++ b/Help/guide/tutorial/Adding a Library.rst @@ -184,7 +184,7 @@ Now let's use our library. In ``tutorial.cxx``, include ``MathFunctions.h``: </details> -Lastly, replace ``sqrt`` with our library function ``mathfunctions::mysqrt``. +Lastly, replace ``sqrt`` with the wrapper function ``mathfunctions::sqrt``. .. raw:: html diff --git a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt index b221506..1654564 100644 --- a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt @@ -33,10 +33,13 @@ if(USE_MYMATH) POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # define the symbol stating we are using the declspec(dllexport) when diff --git a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt index 36b3fe1..210563a 100644 --- a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt @@ -26,10 +26,13 @@ if(USE_MYMATH) ${CMAKE_CURRENT_BINARY_DIR} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt index 813bf90..eacc538 100644 --- a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt @@ -31,10 +31,13 @@ if(USE_MYMATH) POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # define the symbol stating we are using the declspec(dllexport) when diff --git a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt index 38694dd..8aa5904 100644 --- a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt @@ -33,10 +33,13 @@ if(USE_MYMATH) POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # define the symbol stating we are using the declspec(dllexport) when diff --git a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt index 0ffb9e1..ffb2f35 100644 --- a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt @@ -16,7 +16,7 @@ if (USE_MYMATH) # TODO 7: Link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(MathFunctions PUBLIC SqrtLibrary) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() # TODO 6: Link MathFunctions to tutorial_compiler_flags diff --git a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt index 48561eb..6931898 100644 --- a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt @@ -17,10 +17,11 @@ if (USE_MYMATH) mysqrt.cxx ) - # link our compiler flags interface library + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) - target_link_libraries(MathFunctions PUBLIC SqrtLibrary) + + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) diff --git a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt index 0c688f2..61b3899 100644 --- a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt @@ -16,12 +16,13 @@ if (USE_MYMATH) mysqrt.cxx ) - # link our compiler flags interface library + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # TODO 1: Create a variable called installable_libs that is a list of all diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt index b1b925e..8499a51 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt @@ -16,11 +16,13 @@ if (USE_MYMATH) mysqrt.cxx ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt index 897ec0e..a0b3037 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt @@ -16,6 +16,7 @@ if (USE_MYMATH) mysqrt.cxx ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) # TODO 1: Include CheckCXXSourceCompiles @@ -41,7 +42,7 @@ if (USE_MYMATH) target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt index 872a24a..b14d180 100644 --- a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt @@ -10,6 +10,7 @@ if (USE_MYMATH) mysqrt.cxx ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) # does this system provide the log and exp functions? @@ -45,7 +46,7 @@ target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ) -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt index 54cecf8..5addc6d 100644 --- a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt @@ -25,10 +25,13 @@ if (USE_MYMATH) ${CMAKE_CURRENT_BINARY_DIR} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst b/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst index 0c7845c..7e08b48 100644 --- a/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst +++ b/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst @@ -21,6 +21,8 @@ support per-configuration specification. For example, the code: selects for the target ``foo`` the program database debug information format for the Debug configuration. +This property is initialized from the value of the +:variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` variable, if it is set. If this property is not set, CMake selects a debug information format using the default value ``$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>``, if supported by the compiler, and otherwise diff --git a/Help/release/dev/xcode-no-legacy-buildsystem.rst b/Help/release/dev/xcode-no-legacy-buildsystem.rst new file mode 100644 index 0000000..f3d1f67 --- /dev/null +++ b/Help/release/dev/xcode-no-legacy-buildsystem.rst @@ -0,0 +1,8 @@ +xcode-no-legacy-buildsystem +--------------------------- + +* The :generator:`Xcode` generator will now issue a fatal error if + the Legacy Build System has been selected for Xcode 14 and + newer. Those Xcode versions dropped support for the Legacy Build + System and expect the project being set-up for their current + Build System. diff --git a/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst b/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst index d153061..f3c213c 100644 --- a/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst +++ b/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst @@ -12,7 +12,8 @@ mature enough for use by CMake. The possible values are: ``1`` The original Xcode build system. - This is the default when using Xcode 11.x or below. + This is the default when using Xcode 11.x or below and supported + up to Xcode 13.x. ``12`` The Xcode "new build system" introduced by Xcode 10. diff --git a/Modules/Compiler/IntelLLVM.cmake b/Modules/Compiler/IntelLLVM.cmake index e256c8f..f3c0bf4 100644 --- a/Modules/Compiler/IntelLLVM.cmake +++ b/Modules/Compiler/IntelLLVM.cmake @@ -44,6 +44,13 @@ else() string(APPEND CMAKE_${lang}_FLAGS_INIT " ") string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g") + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 2023.0.0) + if("x${lang}" STREQUAL "xFortran") + string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -diag-disable:10440") + else() + string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -Rno-debug-disables-optimization") + endif() + endif() string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os") string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3") string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g") diff --git a/Modules/FindFreetype.cmake b/Modules/FindFreetype.cmake index 82885cb..dcf271d 100644 --- a/Modules/FindFreetype.cmake +++ b/Modules/FindFreetype.cmake @@ -65,6 +65,64 @@ directory of a Freetype installation. # I'm going to attempt to cut out the middleman and hope # everything still works. +set(_Freetype_args) +if (Freetype_FIND_QUIETLY) + list(APPEND _Freetype_args + QUIET) +endif () +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) +find_package(freetype CONFIG ${_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 LOCATION) + list(APPEND FREETYPE_LIBRARIES + "${_Freetype_location}") + unset(_Freetype_location) + 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) + return () +endif () + set(FREETYPE_FIND_ARGS HINTS ENV FREETYPE_DIR diff --git a/Modules/FindGLEW.cmake b/Modules/FindGLEW.cmake index bfde40b..dff53e1 100644 --- a/Modules/FindGLEW.cmake +++ b/Modules/FindGLEW.cmake @@ -126,6 +126,10 @@ function(__glew_set_find_library_suffix shared_or_static) set(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib;.so" PARENT_SCOPE) elseif(APPLE AND "${shared_or_static}" MATCHES "STATIC") set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" PARENT_SCOPE) + elseif(WIN32 AND MINGW AND "${shared_or_static}" MATCHES "SHARED") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" PARENT_SCOPE) + elseif(WIN32 AND MINGW AND "${shared_or_static}" MATCHES "STATIC") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" PARENT_SCOPE) elseif(WIN32 AND "${shared_or_static}" MATCHES "SHARED") set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" PARENT_SCOPE) elseif(WIN32 AND "${shared_or_static}" MATCHES "STATIC") diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake index 3ab6bc1..8a2e1a1 100644 --- a/Modules/FindMatlab.cmake +++ b/Modules/FindMatlab.cmake @@ -336,9 +336,7 @@ set(MATLAB_VERSIONS_MAPPING # temporary folder for all Matlab runs set(_matlab_temporary_folder ${CMAKE_BINARY_DIR}/Matlab) -if(NOT EXISTS "${_matlab_temporary_folder}") - file(MAKE_DIRECTORY "${_matlab_temporary_folder}") -endif() +file(MAKE_DIRECTORY "${_matlab_temporary_folder}") #[=======================================================================[.rst: .. command:: matlab_get_version_from_release_name @@ -567,7 +565,7 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${_matlab_current_version};MATLABROOT]" ABSOLUTE) - if(EXISTS "${current_MATLAB_ROOT}") + if(IS_DIRECTORY "${current_MATLAB_ROOT}") list(APPEND _matlab_roots_list "MATLAB" ${_matlab_current_version} ${current_MATLAB_ROOT}) endif() @@ -583,7 +581,7 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_ # remove the dot string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") - if(EXISTS "${current_MATLAB_ROOT}") + if(IS_DIRECTORY "${current_MATLAB_ROOT}") list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}") endif() @@ -599,7 +597,7 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_ # remove the dot string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") - if(EXISTS "${current_MATLAB_ROOT}") + if(IS_DIRECTORY "${current_MATLAB_ROOT}") list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}") endif() @@ -1325,7 +1323,7 @@ function(_Matlab_get_version_from_root matlab_root matlab_or_mcr matlab_known_ve if(NOT _matlab_current_program) set(_find_matlab_options) - if(matlab_root AND EXISTS ${matlab_root}) + if(IS_DIRECTORY "${matlab_root}") set(_find_matlab_options PATHS ${matlab_root} ${matlab_root}/bin NO_DEFAULT_PATH) endif() @@ -1337,7 +1335,7 @@ function(_Matlab_get_version_from_root matlab_root matlab_or_mcr matlab_known_ve ) endif() - if(NOT _matlab_current_program OR NOT EXISTS ${_matlab_current_program}) + if(NOT _matlab_current_program) # if not found, clear the dependent variables if(MATLAB_FIND_DEBUG) message(WARNING "[MATLAB] Cannot find the main matlab program under ${matlab_root}") @@ -1463,7 +1461,7 @@ function(_Matlab_find_instances_osx matlab_roots) set(_matlab_base_path "/Applications/MATLAB_${_matlab_current_release}.app") # Check Matlab, has precedence over MCR - if(EXISTS ${_matlab_base_path}) + if(IS_DIRECTORY "${_matlab_base_path}") if(MATLAB_FIND_DEBUG) message(STATUS "[MATLAB] Found version ${_matlab_current_release} (${_matlab_current_version}) in ${_matlab_base_path}") endif() @@ -1472,7 +1470,7 @@ function(_Matlab_find_instances_osx matlab_roots) # Checks MCR set(_mcr_path "/Applications/MATLAB/MATLAB_Runtime/v${_matlab_current_version_without_dot}") - if(EXISTS "${_mcr_path}") + if(IS_DIRECTORY "${_mcr_path}") if(MATLAB_FIND_DEBUG) message(STATUS "[MATLAB] Found MCR version ${_matlab_current_release} (${_matlab_current_version}) in ${_mcr_path}") endif() @@ -1565,7 +1563,7 @@ endif() if(Matlab_ROOT_DIR) # if the user specifies a possible root, we keep this one - if(NOT EXISTS "${Matlab_ROOT_DIR}") + if(NOT IS_DIRECTORY "${Matlab_ROOT_DIR}") # if Matlab_ROOT_DIR specified but erroneous if(MATLAB_FIND_DEBUG) message(WARNING "[MATLAB] the specified path for Matlab_ROOT_DIR does not exist (${Matlab_ROOT_DIR})") diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake index 02f7fb4..4d29f45 100644 --- a/Modules/FindPkgConfig.cmake +++ b/Modules/FindPkgConfig.cmake @@ -425,13 +425,19 @@ macro(_pkg_set_path_internal) unset(_pkgconfig_path) endif() - # Tell pkg-config not to strip any -L paths so we can search them all. + # Tell pkg-config not to strip any -I or -L paths so we can search them all. if(DEFINED ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}) set(_pkgconfig_allow_system_libs_old "$ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}") else() unset(_pkgconfig_allow_system_libs_old) endif() set(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS} 1) + if(DEFINED ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}) + set(_pkgconfig_allow_system_cflags_old "$ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}") + else() + unset(_pkgconfig_allow_system_cflags_old) + endif() + set(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS} 1) endmacro() macro(_pkg_restore_path_internal) @@ -445,6 +451,12 @@ macro(_pkg_restore_path_internal) else() unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}) endif() + if(DEFINED _pkgconfig_allow_system_cflags_old) + set(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS} "${_pkgconfig_allow_system_cflags_old}") + unset(_pkgconfig_allow_system_cflags_old) + else() + unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}) + endif() unset(_extra_paths) unset(_pkgconfig_path_old) diff --git a/Modules/Internal/CPack/NSIS.template.in b/Modules/Internal/CPack/NSIS.template.in index 21753af..6349f9d 100644 --- a/Modules/Internal/CPack/NSIS.template.in +++ b/Modules/Internal/CPack/NSIS.template.in @@ -981,7 +981,7 @@ inst: ;MessageBox MB_OK 'User "$0" is in the Admin group' StrCpy $SV_ALLUSERS "AllUsers" Goto done - StrCmp $1 "Power" 0 +4 + StrCmp $1 "Power" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Power Users group' StrCpy $SV_ALLUSERS "AllUsers" diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 1f8d8a8..e8ebae6 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 27) -set(CMake_VERSION_PATCH 20230905) +set(CMake_VERSION_PATCH 20230908) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx index 6ad3755..1248d17 100644 --- a/Source/CPack/cmCPackSTGZGenerator.cxx +++ b/Source/CPack/cmCPackSTGZGenerator.cxx @@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <fcntl.h> + #include "cmsys/FStream.hxx" #include "cm_sys_stat.h" diff --git a/Source/CTest/cmCTestBZR.cxx b/Source/CTest/cmCTestBZR.cxx index 246e811..36df344 100644 --- a/Source/CTest/cmCTestBZR.cxx +++ b/Source/CTest/cmCTestBZR.cxx @@ -135,14 +135,14 @@ private: std::string cmCTestBZR::LoadInfo() { // Run "bzr info" to get the repository info from the work tree. - const char* bzr = this->CommandLineTool.c_str(); - const char* bzr_info[] = { bzr, "info", nullptr }; + std::string bzr = this->CommandLineTool; + std::vector<std::string> bzr_info = { bzr, "info" }; InfoParser iout(this, "info-out> "); OutputLogger ierr(this->Log, "info-err> "); this->RunChild(bzr_info, &iout, &ierr); // Run "bzr revno" to get the repository revision number from the work tree. - const char* bzr_revno[] = { bzr, "revno", nullptr }; + std::vector<std::string> bzr_revno = { bzr, "revno" }; std::string rev; RevnoParser rout(this, "revno-out> ", rev); OutputLogger rerr(this->Log, "revno-err> "); @@ -372,22 +372,18 @@ bool cmCTestBZR::UpdateImpl() // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) // Use "bzr pull" to update the working tree. - std::vector<char const*> bzr_update; - bzr_update.push_back(this->CommandLineTool.c_str()); + std::vector<std::string> bzr_update; + bzr_update.push_back(this->CommandLineTool); bzr_update.push_back("pull"); - for (std::string const& arg : args) { - bzr_update.push_back(arg.c_str()); - } - - bzr_update.push_back(this->URL.c_str()); + cm::append(bzr_update, args); - bzr_update.push_back(nullptr); + bzr_update.push_back(this->URL); // For some reason bzr uses stderr to display the update status. OutputLogger out(this->Log, "pull-out> "); UpdateParser err(this, "pull-err> "); - return this->RunUpdateCommand(bzr_update.data(), &out, &err); + return this->RunUpdateCommand(bzr_update, &out, &err); } bool cmCTestBZR::LoadRevisions() @@ -408,10 +404,9 @@ bool cmCTestBZR::LoadRevisions() } // Run "bzr log" to get all global revisions of interest. - const char* bzr = this->CommandLineTool.c_str(); - const char* bzr_log[] = { - bzr, "log", "-v", "-r", revs.c_str(), "--xml", this->URL.c_str(), nullptr - }; + std::string bzr = this->CommandLineTool; + std::vector<std::string> bzr_log = { bzr, "log", "-v", "-r", + revs, "--xml", this->URL }; { LogParser out(this, "log-out> "); OutputLogger err(this->Log, "log-err> "); @@ -467,8 +462,8 @@ private: bool cmCTestBZR::LoadModifications() { // Run "bzr status" which reports local modifications. - const char* bzr = this->CommandLineTool.c_str(); - const char* bzr_status[] = { bzr, "status", "-SV", nullptr }; + std::string bzr = this->CommandLineTool; + std::vector<std::string> bzr_status = { bzr, "status", "-SV" }; StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); this->RunChild(bzr_status, &out, &err); diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index 5feb953..bb6ccc3 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -7,8 +7,6 @@ #include <cstring> #include <ratio> -#include "cmsys/Process.h" - #include "cmBuildOptions.h" #include "cmCTest.h" #include "cmCTestTestHandler.h" @@ -308,12 +306,11 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) return 1; } - std::vector<const char*> testCommand; - testCommand.push_back(fullPath.c_str()); + std::vector<std::string> testCommand; + testCommand.push_back(fullPath); for (std::string const& testCommandArg : this->TestCommandArgs) { - testCommand.push_back(testCommandArg.c_str()); + testCommand.push_back(testCommandArg); } - testCommand.push_back(nullptr); std::string outs; int retval = 0; // run the test from the this->BuildRunDir if set @@ -349,10 +346,10 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) } } - int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr, - remainingTime, nullptr); + bool runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr, + remainingTime, nullptr); - if (runTestRes != cmsysProcess_State_Exited || retval != 0) { + if (!runTestRes || retval != 0) { out << "Test command failed: " << testCommand[0] << "\n"; retval = 1; } diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index 00ecf42..859798e 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -3,15 +3,17 @@ #include "cmCTestBuildHandler.h" #include <cstdlib> +#include <memory> #include <ratio> #include <set> #include <utility> #include <cmext/algorithm> +#include <cm3p/uv.h> + #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmCTest.h" #include "cmCTestLaunchReporter.h" @@ -24,6 +26,9 @@ #include "cmStringAlgorithms.h" #include "cmStringReplaceHelper.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" #include "cmValue.h" #include "cmXMLWriter.h" @@ -420,7 +425,7 @@ int cmCTestBuildHandler::ProcessHandler() cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr); this->ColorRemover = &colorRemover; int retVal = 0; - int res = cmsysProcess_State_Exited; + bool res = true; if (!this->CTest->GetShowOnly()) { res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0, ofs); @@ -475,7 +480,7 @@ int cmCTestBuildHandler::ProcessHandler() } this->GenerateXMLFooter(xml, elapsed_build_time); - if (res != cmsysProcess_State_Exited || retVal || this->TotalErrors > 0) { + if (!res || retVal || this->TotalErrors > 0) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Error(s) when building project" << std::endl); } @@ -764,10 +769,10 @@ void cmCTestBuildHandler::LaunchHelper::WriteScrapeMatchers( } } -int cmCTestBuildHandler::RunMakeCommand(const std::string& command, - int* retVal, const char* dir, - int timeout, std::ostream& ofs, - Encoding encoding) +bool cmCTestBuildHandler::RunMakeCommand(const std::string& command, + int* retVal, const char* dir, + int timeout, std::ostream& ofs, + Encoding encoding) { // First generate the command and arguments std::vector<std::string> args = cmSystemTools::ParseArguments(command); @@ -776,19 +781,9 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, 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, "Run command:", this->Quiet); - for (char const* arg : argv) { - if (!arg) { - break; - } + for (auto const& arg : args) { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"", this->Quiet); } @@ -800,21 +795,20 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, static_cast<void>(launchHelper); // Now create process object - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); - cmsysProcess_SetWorkingDirectory(cp, dir); - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - cmsysProcess_SetTimeout(cp, timeout); - cmsysProcess_Execute(cp); + cmUVProcessChainBuilder builder; + builder.AddCommand(args) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + if (dir) { + builder.SetWorkingDirectory(dir); + } + auto chain = builder.Start(); // Initialize tick's std::string::size_type tick = 0; - const std::string::size_type tick_len = 1024; + static constexpr std::string::size_type tick_len = 1024; - char* data; - int length; cmProcessOutput processOutput(encoding); - std::string strdata; cmCTestOptionalLog( this->CTest, HANDLER_PROGRESS_OUTPUT, " Each symbol represents " @@ -836,39 +830,65 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, this->WarningQuotaReached = 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 - int res; - while ((res = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) { - // Replace '\0' with '\n', since '\0' does not really make sense. This is - // for Visual Studio output - for (int cc = 0; cc < length; ++cc) { - if (data[cc] == 0) { - data[cc] = '\n'; - } - } + cm::uv_pipe_ptr outputStream; + bool outFinished = false; + cm::uv_pipe_ptr errorStream; + bool errFinished = false; + auto startRead = [this, &chain, &processOutput, &tick, + &ofs](cm::uv_pipe_ptr& pipe, int stream, + t_BuildProcessingQueueType& queue, bool& finished, + 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 - if (res == cmsysProcess_Pipe_STDERR) { - processOutput.DecodeText(data, length, strdata, 1); - this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, - &this->BuildProcessingErrorQueue); - } else { - processOutput.DecodeText(data, length, strdata, 2); - this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, - &this->BuildProcessingQueue); - } - } - processOutput.DecodeText(std::string(), strdata, 1); - if (!strdata.empty()) { - this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, - &this->BuildProcessingErrorQueue); - } - processOutput.DecodeText(std::string(), strdata, 2); - if (!strdata.empty()) { - this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, - &this->BuildProcessingQueue); + // Process the chunk of data + std::string strdata; + processOutput.DecodeText(data.data(), data.size(), strdata, id); + this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, + ofs, &queue); + }, + [this, &processOutput, &queue, id, &tick, &ofs, &finished]() { + std::string strdata; + processOutput.DecodeText(std::string(), strdata, id); + if (!strdata.empty()) { + this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, + ofs, &queue); + } + finished = true; + }); + }; + auto outputHandle = startRead(outputStream, chain.OutputStream(), + 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->BuildProcessingQueue); this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs, @@ -879,90 +899,93 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, << std::endl, this->Quiet); - // Properly handle output of the build command - cmsysProcess_WaitForExit(cp, nullptr); - int result = cmsysProcess_GetState(cp); - - if (result == cmsysProcess_State_Exited) { - if (retVal) { - *retVal = cmsysProcess_GetExitValue(cp); - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "Command exited with the value: " << *retVal - << std::endl, - this->Quiet); - // if a non zero return value - if (*retVal) { - // If there was an error running command, report that on the - // dashboard. - if (this->UseCTestLaunch) { - // For launchers, do not record this top-level error if other - // more granular build errors have already been captured. - bool launcherXMLFound = false; - cmsys::Directory launchDir; - launchDir.Load(this->CTestLaunchDir); - unsigned long n = launchDir.GetNumberOfFiles(); - for (unsigned long i = 0; i < n; ++i) { - const char* fname = launchDir.GetFile(i); - if (cmHasLiteralSuffix(fname, ".xml")) { - launcherXMLFound = true; - break; + if (chain.Finished()) { + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + if (retVal) { + *retVal = static_cast<int>(status.ExitStatus); + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Command exited with the value: " << *retVal + << std::endl, + this->Quiet); + // if a non zero return value + if (*retVal) { + // If there was an error running command, report that on the + // dashboard. + if (this->UseCTestLaunch) { + // For launchers, do not record this top-level error if other + // more granular build errors have already been captured. + bool launcherXMLFound = false; + cmsys::Directory launchDir; + launchDir.Load(this->CTestLaunchDir); + unsigned long n = launchDir.GetNumberOfFiles(); + for (unsigned long i = 0; i < n; ++i) { + const char* fname = launchDir.GetFile(i); + if (cmHasLiteralSuffix(fname, ".xml")) { + launcherXMLFound = true; + break; + } + } + if (!launcherXMLFound) { + cmCTestLaunchReporter reporter; + reporter.RealArgs = args; + reporter.ComputeFileNames(); + reporter.ExitCode = *retVal; + reporter.Status = status; + // Use temporary BuildLog file to populate this error for + // CDash. + ofs.flush(); + reporter.LogOut = this->LogFileNames["Build"]; + reporter.LogOut += ".tmp"; + reporter.WriteXML(); + } + } else { + cmCTestBuildErrorWarning errorwarning; + errorwarning.LineNumber = 0; + errorwarning.LogLine = 1; + errorwarning.Text = cmStrCat( + "*** WARNING non-zero return value in ctest from: ", args[0]); + errorwarning.PreContext.clear(); + errorwarning.PostContext.clear(); + errorwarning.Error = false; + this->ErrorsAndWarnings.push_back(std::move(errorwarning)); + this->TotalWarnings++; } } - if (!launcherXMLFound) { - cmCTestLaunchReporter reporter; - reporter.RealArgs = args; - reporter.ComputeFileNames(); - reporter.ExitCode = *retVal; - reporter.Process = cp; - // Use temporary BuildLog file to populate this error for CDash. - ofs.flush(); - reporter.LogOut = this->LogFileNames["Build"]; - reporter.LogOut += ".tmp"; - reporter.WriteXML(); - } - } else { - cmCTestBuildErrorWarning errorwarning; - errorwarning.LineNumber = 0; - errorwarning.LogLine = 1; - errorwarning.Text = cmStrCat( - "*** WARNING non-zero return value in ctest from: ", argv[0]); - errorwarning.PreContext.clear(); - errorwarning.PostContext.clear(); - errorwarning.Error = false; - this->ErrorsAndWarnings.push_back(std::move(errorwarning)); - this->TotalWarnings++; } - } - } - } else if (result == cmsysProcess_State_Exception) { - if (retVal) { - *retVal = cmsysProcess_GetExitException(cp); - cmCTestOptionalLog(this->CTest, WARNING, - "There was an exception: " << *retVal << std::endl, - this->Quiet); + break; + case cmUVProcessChain::ExceptionCode::Spawn: { + // If there was an error running command, report that on the dashboard. + cmCTestBuildErrorWarning errorwarning; + errorwarning.LineNumber = 0; + errorwarning.LogLine = 1; + errorwarning.Text = + cmStrCat("*** ERROR executing: ", exception.second); + errorwarning.PreContext.clear(); + errorwarning.PostContext.clear(); + errorwarning.Error = true; + this->ErrorsAndWarnings.push_back(std::move(errorwarning)); + this->TotalErrors++; + cmCTestLog(this->CTest, ERROR_MESSAGE, + "There was an error: " << exception.second << std::endl); + } break; + default: + if (retVal) { + *retVal = status.TermSignal; + cmCTestOptionalLog( + this->CTest, WARNING, + "There was an exception: " << *retVal << std::endl, this->Quiet); + } + break; } - } else if (result == cmsysProcess_State_Expired) { + } else { 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. - cmCTestBuildErrorWarning errorwarning; - errorwarning.LineNumber = 0; - errorwarning.LogLine = 1; - errorwarning.Text = - cmStrCat("*** ERROR executing: ", cmsysProcess_GetErrorString(cp)); - errorwarning.PreContext.clear(); - errorwarning.PostContext.clear(); - errorwarning.Error = true; - this->ErrorsAndWarnings.push_back(std::move(errorwarning)); - this->TotalErrors++; - cmCTestLog(this->CTest, ERROR_MESSAGE, - "There was an error: " << cmsysProcess_GetErrorString(cp) - << std::endl); } - cmsysProcess_Delete(cp); - return result; + return true; } // ###################################################################### diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h index e33294d..90945b1 100644 --- a/Source/CTest/cmCTestBuildHandler.h +++ b/Source/CTest/cmCTestBuildHandler.h @@ -53,9 +53,9 @@ private: //! Run command specialized for make and configure. Returns process status // and retVal is return value or exception. - int RunMakeCommand(const std::string& command, int* retVal, const char* dir, - int timeout, std::ostream& ofs, - Encoding encoding = cmProcessOutput::Auto); + bool RunMakeCommand(const std::string& command, int* retVal, const char* dir, + int timeout, std::ostream& ofs, + Encoding encoding = cmProcessOutput::Auto); enum { diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx index 95e898c..ef95b25 100644 --- a/Source/CTest/cmCTestCVS.cxx +++ b/Source/CTest/cmCTestCVS.cxx @@ -5,6 +5,7 @@ #include <utility> #include <cm/string_view> +#include <cmext/algorithm> #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" @@ -89,18 +90,15 @@ bool cmCTestCVS::UpdateImpl() } // Run "cvs update" to update the work tree. - std::vector<char const*> cvs_update; - cvs_update.push_back(this->CommandLineTool.c_str()); + std::vector<std::string> cvs_update; + cvs_update.push_back(this->CommandLineTool); cvs_update.push_back("-z3"); cvs_update.push_back("update"); - for (std::string const& arg : args) { - cvs_update.push_back(arg.c_str()); - } - cvs_update.push_back(nullptr); + cm::append(cvs_update, args); UpdateParser out(this, "up-out> "); UpdateParser err(this, "up-err> "); - return this->RunUpdateCommand(cvs_update.data(), &out, &err); + return this->RunUpdateCommand(cvs_update, &out, &err); } class cmCTestCVS::LogParser : public cmCTestVC::LineParser @@ -221,10 +219,8 @@ void cmCTestCVS::LoadRevisions(std::string const& file, const char* branchFlag, cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush); // Run "cvs log" to get revisions of this file on this branch. - const char* cvs = this->CommandLineTool.c_str(); - const char* cvs_log[] = { - cvs, "log", "-N", branchFlag, file.c_str(), nullptr - }; + std::string cvs = this->CommandLineTool; + std::vector<std::string> cvs_log = { cvs, "log", "-N", branchFlag, file }; LogParser out(this, "log-out> ", revisions); OutputLogger err(this->Log, "log-err> "); diff --git a/Source/CTest/cmCTestConfigureHandler.cxx b/Source/CTest/cmCTestConfigureHandler.cxx index 914930e..dd8952f 100644 --- a/Source/CTest/cmCTestConfigureHandler.cxx +++ b/Source/CTest/cmCTestConfigureHandler.cxx @@ -45,7 +45,7 @@ int cmCTestConfigureHandler::ProcessHandler() auto elapsed_time_start = std::chrono::steady_clock::now(); std::string output; int retVal = 0; - int res = 0; + bool res = false; if (!this->CTest->GetShowOnly()) { cmGeneratedFileStream os; if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) { diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index 2874be7..1aa49cf 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -9,6 +9,7 @@ #include <cstring> #include <iomanip> #include <iterator> +#include <memory> #include <ratio> #include <sstream> #include <type_traits> @@ -18,9 +19,10 @@ #include "cmsys/FStream.hxx" #include "cmsys/Glob.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" +#include "cm_fileno.hxx" + #include "cmCTest.h" #include "cmDuration.h" #include "cmGeneratedFileStream.h" @@ -33,6 +35,7 @@ #include "cmParsePHPCoverage.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVProcessChain.h" #include "cmWorkingDirectory.h" #include "cmXMLWriter.h" @@ -40,85 +43,6 @@ class cmMakefile; #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; void cmCTestCoverageHandler::Initialize() @@ -1940,34 +1864,35 @@ int cmCTestCoverageHandler::RunBullseyeCommand( cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n"); return 0; } + std::vector<std::string> args{ cmd }; if (arg) { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run : " << program << " " << arg << "\n", this->Quiet); + args.emplace_back(arg); } else { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run : " << program << "\n", this->Quiet); } // create a process object and start it - cmCTestRunProcess runCoverageSrc; - runCoverageSrc.SetCommand(program.c_str()); - runCoverageSrc.AddArgument(arg); + cmUVProcessChainBuilder builder; std::string stdoutFile = cmStrCat(cont->BinaryDir, "/Testing/Temporary/", this->GetCTestInstance()->GetCurrentTag(), '-', cmd); std::string stderrFile = stdoutFile; stdoutFile += ".stdout"; stderrFile += ".stderr"; - runCoverageSrc.SetStdoutFile(stdoutFile.c_str()); - runCoverageSrc.SetStderrFile(stderrFile.c_str()); - if (!runCoverageSrc.StartProcess()) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Could not run : " << program << " " << arg << "\n" - << "kwsys process state : " - << runCoverageSrc.GetProcessState()); - return 0; - } + std::unique_ptr<FILE, int (*)(FILE*)> stdoutHandle( + cmsys::SystemTools::Fopen(stdoutFile, "w"), fclose); + std::unique_ptr<FILE, int (*)(FILE*)> stderrHandle( + cmsys::SystemTools::Fopen(stderrFile, "w"), fclose); + builder.AddCommand(args) + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, + cm_fileno(stdoutHandle.get())) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(stderrHandle.get())); // since we set the output file names wait for it to end - runCoverageSrc.WaitForExit(); + auto chain = builder.Start(); + chain.Wait(); outputFile = stdoutFile; return 1; } diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx index 5f8cb74..ca8659e 100644 --- a/Source/CTest/cmCTestGIT.cxx +++ b/Source/CTest/cmCTestGIT.cxx @@ -9,8 +9,9 @@ #include <utility> #include <vector> +#include <cmext/algorithm> + #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmCTest.h" #include "cmCTestVC.h" @@ -18,6 +19,7 @@ #include "cmProcessOutput.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVProcessChain.h" #include "cmValue.h" static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major, @@ -58,9 +60,9 @@ private: std::string cmCTestGIT::GetWorkingRevision() { // Run plumbing "git rev-list" to get work tree revision. - const char* git = this->CommandLineTool.c_str(); - const char* git_rev_list[] = { git, "rev-list", "-n", "1", - "HEAD", "--", nullptr }; + std::string git = this->CommandLineTool; + std::vector<std::string> git_rev_list = { git, "rev-list", "-n", + "1", "HEAD", "--" }; std::string rev; OneLineParser out(this, "rl-out> ", rev); OutputLogger err(this->Log, "rl-err> "); @@ -92,13 +94,13 @@ std::string cmCTestGIT::FindGitDir() std::string git_dir; // Run "git rev-parse --git-dir" to locate the real .git directory. - const char* git = this->CommandLineTool.c_str(); - char const* git_rev_parse[] = { git, "rev-parse", "--git-dir", nullptr }; + std::string git = this->CommandLineTool; + std::vector<std::string> git_rev_parse = { git, "rev-parse", "--git-dir" }; std::string git_dir_line; OneLineParser rev_parse_out(this, "rev-parse-out> ", git_dir_line); OutputLogger rev_parse_err(this->Log, "rev-parse-err> "); - if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr, - cmProcessOutput::UTF8)) { + if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, + std::string{}, cmProcessOutput::UTF8)) { git_dir = git_dir_line; } if (git_dir.empty()) { @@ -117,11 +119,10 @@ std::string cmCTestGIT::FindGitDir() std::string cygpath_exe = cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe"); if (cmSystemTools::FileExists(cygpath_exe)) { - char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(), - 0 }; + std::vector<std::string> cygpath = { cygpath_exe, "-w", git_dir }; OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line); OutputLogger cygpath_err(this->Log, "cygpath-err> "); - if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, nullptr, + if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, std::string{}, cmProcessOutput::UTF8)) { git_dir = git_dir_line; } @@ -136,12 +137,12 @@ std::string cmCTestGIT::FindTopDir() std::string top_dir = this->SourceDirectory; // Run "git rev-parse --show-cdup" to locate the top of the tree. - const char* git = this->CommandLineTool.c_str(); - char const* git_rev_parse[] = { git, "rev-parse", "--show-cdup", nullptr }; + std::string git = this->CommandLineTool; + std::vector<std::string> git_rev_parse = { git, "rev-parse", "--show-cdup" }; std::string cdup; OneLineParser rev_parse_out(this, "rev-parse-out> ", cdup); OutputLogger rev_parse_err(this->Log, "rev-parse-err> "); - if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr, + if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, "", cmProcessOutput::UTF8) && !cdup.empty()) { top_dir += "/"; @@ -153,10 +154,10 @@ std::string cmCTestGIT::FindTopDir() bool cmCTestGIT::UpdateByFetchAndReset() { - const char* git = this->CommandLineTool.c_str(); + std::string git = this->CommandLineTool; // Use "git fetch" to get remote commits. - std::vector<char const*> git_fetch; + std::vector<std::string> git_fetch; git_fetch.push_back(git); git_fetch.push_back("fetch"); @@ -166,17 +167,12 @@ bool cmCTestGIT::UpdateByFetchAndReset() opts = this->CTest->GetCTestConfiguration("GITUpdateOptions"); } std::vector<std::string> args = cmSystemTools::ParseArguments(opts); - for (std::string const& arg : args) { - git_fetch.push_back(arg.c_str()); - } - - // Sentinel argument. - git_fetch.push_back(nullptr); + cm::append(git_fetch, args); // Fetch upstream refs. OutputLogger fetch_out(this->Log, "fetch-out> "); OutputLogger fetch_err(this->Log, "fetch-err> "); - if (!this->RunUpdateCommand(git_fetch.data(), &fetch_out, &fetch_err)) { + if (!this->RunUpdateCommand(git_fetch, &fetch_out, &fetch_err)) { return false; } @@ -207,25 +203,22 @@ bool cmCTestGIT::UpdateByFetchAndReset() } // Reset the local branch to point at that tracked from upstream. - char const* git_reset[] = { git, "reset", "--hard", sha1.c_str(), nullptr }; + std::vector<std::string> git_reset = { git, "reset", "--hard", sha1 }; OutputLogger reset_out(this->Log, "reset-out> "); OutputLogger reset_err(this->Log, "reset-err> "); - return this->RunChild(&git_reset[0], &reset_out, &reset_err); + return this->RunChild(git_reset, &reset_out, &reset_err); } bool cmCTestGIT::UpdateByCustom(std::string const& custom) { cmList git_custom_command{ custom, cmList::EmptyElements::Yes }; - std::vector<char const*> git_custom; - git_custom.reserve(git_custom_command.size() + 1); - for (std::string const& i : git_custom_command) { - git_custom.push_back(i.c_str()); - } - git_custom.push_back(nullptr); + std::vector<std::string> git_custom; + git_custom.reserve(git_custom_command.size()); + cm::append(git_custom, git_custom_command); OutputLogger custom_out(this->Log, "custom-out> "); OutputLogger custom_err(this->Log, "custom-err> "); - return this->RunUpdateCommand(git_custom.data(), &custom_out, &custom_err); + return this->RunUpdateCommand(git_custom, &custom_out, &custom_err); } bool cmCTestGIT::UpdateInternal() @@ -244,13 +237,14 @@ bool cmCTestGIT::UpdateImpl() } std::string top_dir = this->FindTopDir(); - const char* git = this->CommandLineTool.c_str(); - const char* recursive = "--recursive"; - const char* sync_recursive = "--recursive"; + std::string git = this->CommandLineTool; + std::string recursive = "--recursive"; + std::string sync_recursive = "--recursive"; // Git < 1.6.5 did not support submodule --recursive + bool support_recursive = true; if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) { - recursive = nullptr; + support_recursive = false; // No need to require >= 1.6.5 if there are no submodules. if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) { this->Log << "Git < 1.6.5 cannot update submodules recursively\n"; @@ -258,8 +252,9 @@ bool cmCTestGIT::UpdateImpl() } // Git < 1.8.1 did not support sync --recursive + bool support_sync_recursive = true; if (this->GetGitVersion() < cmCTestGITVersion(1, 8, 1, 0)) { - sync_recursive = nullptr; + support_sync_recursive = false; // No need to require >= 1.8.1 if there are no submodules. if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) { this->Log << "Git < 1.8.1 cannot synchronize submodules recursively\n"; @@ -274,35 +269,39 @@ bool cmCTestGIT::UpdateImpl() std::string init_submodules = this->CTest->GetCTestConfiguration("GITInitSubmodules"); if (cmIsOn(init_submodules)) { - char const* git_submodule_init[] = { git, "submodule", "init", nullptr }; + std::vector<std::string> git_submodule_init = { git, "submodule", "init" }; ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err, - top_dir.c_str()); + top_dir); if (!ret) { return false; } } - char const* git_submodule_sync[] = { git, "submodule", "sync", - sync_recursive, nullptr }; + std::vector<std::string> git_submodule_sync = { git, "submodule", "sync" }; + if (support_sync_recursive) { + git_submodule_sync.push_back(sync_recursive); + } ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err, - top_dir.c_str()); + top_dir); if (!ret) { return false; } - char const* git_submodule[] = { git, "submodule", "update", recursive, - nullptr }; + std::vector<std::string> git_submodule = { git, "submodule", "update" }; + if (support_recursive) { + git_submodule.push_back(recursive); + } return this->RunChild(git_submodule, &submodule_out, &submodule_err, - top_dir.c_str()); + top_dir); } unsigned int cmCTestGIT::GetGitVersion() { if (!this->CurrentGitVersion) { - const char* git = this->CommandLineTool.c_str(); - char const* git_version[] = { git, "--version", nullptr }; + std::string git = this->CommandLineTool; + std::vector<std::string> git_version = { git, "--version" }; std::string version; OneLineParser version_out(this, "version-out> ", version); OutputLogger version_err(this->Log, "version-err> "); @@ -605,50 +604,49 @@ bool cmCTestGIT::LoadRevisions() { // Use 'git rev-list ... | git diff-tree ...' to get revisions. std::string range = this->OldRevision + ".." + this->NewRevision; - const char* git = this->CommandLineTool.c_str(); - const char* git_rev_list[] = { git, "rev-list", "--reverse", - range.c_str(), "--", nullptr }; - const char* git_diff_tree[] = { - git, "diff-tree", "--stdin", "--always", "-z", - "-r", "--pretty=raw", "--encoding=utf-8", nullptr + std::string git = this->CommandLineTool; + std::vector<std::string> git_rev_list = { git, "rev-list", "--reverse", + range, "--" }; + std::vector<std::string> git_diff_tree = { + git, "diff-tree", "--stdin", "--always", + "-z", "-r", "--pretty=raw", "--encoding=utf-8" }; this->Log << cmCTestGIT::ComputeCommandLine(git_rev_list) << " | " << cmCTestGIT::ComputeCommandLine(git_diff_tree) << "\n"; - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_AddCommand(cp, git_rev_list); - cmsysProcess_AddCommand(cp, git_diff_tree); - cmsysProcess_SetWorkingDirectory(cp, this->SourceDirectory.c_str()); + cmUVProcessChainBuilder builder; + builder.AddCommand(git_rev_list) + .AddCommand(git_diff_tree) + .SetWorkingDirectory(this->SourceDirectory); CommitParser out(this, "dt-out> "); OutputLogger err(this->Log, "dt-err> "); - cmCTestGIT::RunProcess(cp, &out, &err, cmProcessOutput::UTF8); + cmCTestGIT::RunProcess(builder, &out, &err, cmProcessOutput::UTF8); // Send one extra zero-byte to terminate the last record. out.Process("", 1); - cmsysProcess_Delete(cp); return true; } bool cmCTestGIT::LoadModifications() { - const char* git = this->CommandLineTool.c_str(); + std::string git = this->CommandLineTool; // Use 'git update-index' to refresh the index w.r.t. the work tree. - const char* git_update_index[] = { git, "update-index", "--refresh", - nullptr }; + std::vector<std::string> git_update_index = { git, "update-index", + "--refresh" }; OutputLogger ui_out(this->Log, "ui-out> "); OutputLogger ui_err(this->Log, "ui-err> "); - this->RunChild(git_update_index, &ui_out, &ui_err, nullptr, + this->RunChild(git_update_index, &ui_out, &ui_err, "", cmProcessOutput::UTF8); // Use 'git diff-index' to get modified files. - const char* git_diff_index[] = { git, "diff-index", "-z", - "HEAD", "--", nullptr }; + std::vector<std::string> git_diff_index = { git, "diff-index", "-z", "HEAD", + "--" }; DiffParser out(this, "di-out> "); OutputLogger err(this->Log, "di-err> "); - this->RunChild(git_diff_index, &out, &err, nullptr, cmProcessOutput::UTF8); + this->RunChild(git_diff_index, &out, &err, "", cmProcessOutput::UTF8); for (Change const& c : out.Changes) { this->DoModification(PathModified, c.Path); diff --git a/Source/CTest/cmCTestHG.cxx b/Source/CTest/cmCTestHG.cxx index 02837ba..e1a945d 100644 --- a/Source/CTest/cmCTestHG.cxx +++ b/Source/CTest/cmCTestHG.cxx @@ -95,8 +95,8 @@ private: std::string cmCTestHG::GetWorkingRevision() { // Run plumbing "hg identify" to get work tree revision. - const char* hg = this->CommandLineTool.c_str(); - const char* hg_identify[] = { hg, "identify", "-i", nullptr }; + std::string hg = this->CommandLineTool; + std::vector<std::string> hg_identify = { hg, "identify", "-i" }; std::string rev; IdentifyParser out(this, "rev-out> ", rev); 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. { - const char* hg = this->CommandLineTool.c_str(); - const char* hg_pull[] = { hg, "pull", "-v", nullptr }; + std::string hg = this->CommandLineTool; + std::vector<std::string> hg_pull = { hg, "pull", "-v" }; OutputLogger out(this->Log, "pull-out> "); OutputLogger err(this->Log, "pull-err> "); - this->RunChild(&hg_pull[0], &out, &err); + this->RunChild(hg_pull, &out, &err); } // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) - std::vector<char const*> hg_update; + std::vector<std::string> hg_update; hg_update.push_back(this->CommandLineTool.c_str()); hg_update.push_back("update"); hg_update.push_back("-v"); @@ -147,16 +147,11 @@ bool cmCTestHG::UpdateImpl() opts = this->CTest->GetCTestConfiguration("HGUpdateOptions"); } std::vector<std::string> args = cmSystemTools::ParseArguments(opts); - for (std::string const& arg : args) { - hg_update.push_back(arg.c_str()); - } - - // Sentinel argument. - hg_update.push_back(nullptr); + cm::append(hg_update, args); OutputLogger out(this->Log, "update-out> "); OutputLogger err(this->Log, "update-err> "); - return this->RunUpdateCommand(hg_update.data(), &out, &err); + return this->RunUpdateCommand(hg_update, &out, &err); } class cmCTestHG::LogParser @@ -277,8 +272,8 @@ bool cmCTestHG::LoadRevisions() // the project has spaces in the path. Also, they may not have // proper XML escapes. std::string range = this->OldRevision + ":" + this->NewRevision; - const char* hg = this->CommandLineTool.c_str(); - const char* hgXMLTemplate = "<logentry\n" + std::string hg = this->CommandLineTool; + std::string hgXMLTemplate = "<logentry\n" " revision=\"{node|short}\">\n" " <author>{author|person}</author>\n" " <email>{author|email}</email>\n" @@ -288,10 +283,8 @@ bool cmCTestHG::LoadRevisions() " <file_adds>{file_adds}</file_adds>\n" " <file_dels>{file_dels}</file_dels>\n" "</logentry>\n"; - const char* hg_log[] = { - hg, "log", "--removed", "-r", range.c_str(), - "--template", hgXMLTemplate, nullptr - }; + std::vector<std::string> hg_log = { hg, "log", "--removed", "-r", + range, "--template", hgXMLTemplate }; LogParser out(this, "log-out> "); out.Process("<?xml version=\"1.0\"?>\n" @@ -305,8 +298,8 @@ bool cmCTestHG::LoadRevisions() bool cmCTestHG::LoadModifications() { // Use 'hg status' to get modified files. - const char* hg = this->CommandLineTool.c_str(); - const char* hg_status[] = { hg, "status", nullptr }; + std::string hg = this->CommandLineTool; + std::vector<std::string> hg_status = { hg, "status" }; StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); this->RunChild(hg_status, &out, &err); diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx index 4a33869..6b13ad1 100644 --- a/Source/CTest/cmCTestLaunch.cxx +++ b/Source/CTest/cmCTestLaunch.cxx @@ -2,13 +2,19 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestLaunch.h" +#include <cstdio> #include <cstring> #include <iostream> +#include <memory> +#include <utility> + +#include <cm3p/uv.h> #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" +#include "cm_fileno.hxx" + #include "cmCTestLaunchReporter.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" @@ -17,6 +23,9 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" #include "cmake.h" #ifdef _WIN32 @@ -28,8 +37,6 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) { - this->Process = nullptr; - if (!this->ParseArguments(argc, argv)) { return; } @@ -40,13 +47,9 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) this->ScrapeRulesLoaded = false; this->HaveOut = false; this->HaveErr = false; - this->Process = cmsysProcess_New(); } -cmCTestLaunch::~cmCTestLaunch() -{ - cmsysProcess_Delete(this->Process); -} +cmCTestLaunch::~cmCTestLaunch() = default; bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) { @@ -113,15 +116,12 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) // Extract the real command line. if (arg0) { - this->RealArgC = argc - arg0; - this->RealArgV = argv + arg0; - for (int i = 0; i < this->RealArgC; ++i) { - this->HandleRealArg(this->RealArgV[i]); + for (int i = 0; i < argc - arg0; ++i) { + this->RealArgV.emplace_back((argv + arg0)[i]); + this->HandleRealArg((argv + arg0)[i]); } return true; } - this->RealArgC = 0; - this->RealArgV = nullptr; std::cerr << "No launch/command separator ('--') found!\n"; return false; } @@ -151,17 +151,22 @@ void cmCTestLaunch::RunChild() } // Prepare to run the real command. - cmsysProcess* cp = this->Process; - cmsysProcess_SetCommand(cp, this->RealArgV); + cmUVProcessChainBuilder builder; + builder.AddCommand(this->RealArgV); cmsys::ofstream fout; cmsys::ofstream ferr; if (this->Reporter.Passthru) { // In passthru mode we just share the output pipes. - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1); - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); + builder + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, + cm_fileno(stdout)) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(stderr)); } else { // 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); ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary); } @@ -174,51 +179,65 @@ void cmCTestLaunch::RunChild() #endif // Run the real command. - cmsysProcess_Execute(cp); + auto chain = builder.Start(); // 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) { - char* data = nullptr; - int length = 0; - cmProcessOutput processOutput; - std::string strdata; - 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()) { - fout.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()); - } + auto beginRead = [&chain, &processOutput]( + cm::uv_pipe_ptr& pipe, int stream, std::ostream& out, + cmsys::ofstream& file, bool& haveData, bool& finished, + 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; + processOutput.DecodeText(std::string(), strdata, id); + if (!strdata.empty()) { + file.write(strdata.c_str(), strdata.size()); + out.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. - cmsysProcess_WaitForExit(cp, nullptr); - this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp); + while (!(chain.Finished() && outFinished && errFinished)) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); + } + 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() { - if (!this->Process) { - std::cerr << "Could not allocate cmsysProcess instance!\n"; - return -1; - } - this->RunChild(); if (this->CheckResults()) { @@ -226,7 +245,6 @@ int cmCTestLaunch::Run() } this->LoadConfig(); - this->Reporter.Process = this->Process; this->Reporter.WriteXML(); return this->Reporter.ExitCode; diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h index c5a6476..ef21a26 100644 --- a/Source/CTest/cmCTestLaunch.h +++ b/Source/CTest/cmCTestLaunch.h @@ -43,15 +43,12 @@ private: bool ParseArguments(int argc, const char* const* argv); // The real command line appearing after launcher arguments. - int RealArgC; - const char* const* RealArgV; + std::vector<std::string> RealArgV; // The real command line after response file expansion. std::vector<std::string> RealArgs; void HandleRealArg(const char* arg); - struct cmsysProcess_s* Process; - // Whether or not any data have been written to stdout or stderr. bool HaveOut; bool HaveErr; diff --git a/Source/CTest/cmCTestLaunchReporter.cxx b/Source/CTest/cmCTestLaunchReporter.cxx index 149ba5d..4b4e5c5 100644 --- a/Source/CTest/cmCTestLaunchReporter.cxx +++ b/Source/CTest/cmCTestLaunchReporter.cxx @@ -2,8 +2,9 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestLaunchReporter.h" +#include <utility> + #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" #include "cmCryptoHash.h" @@ -22,6 +23,7 @@ cmCTestLaunchReporter::cmCTestLaunchReporter() { this->Passthru = true; + this->Status.Finished = true; this->ExitCode = 1; this->CWD = cmSystemTools::GetCurrentWorkingDirectory(); @@ -231,35 +233,23 @@ void cmCTestLaunchReporter::WriteXMLResult(cmXMLElement& e2) // ExitCondition cmXMLElement e4(e3, "ExitCondition"); - cmsysProcess* cp = this->Process; - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Starting: - e4.Content("No process has been executed"); - break; - case cmsysProcess_State_Executing: - e4.Content("The process is still executing"); - break; - case cmsysProcess_State_Disowned: - e4.Content("Disowned"); - break; - case cmsysProcess_State_Killed: - e4.Content("Killed by parent"); - break; - - case cmsysProcess_State_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; + if (this->Status.Finished) { + auto exception = this->Status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + e4.Content(this->ExitCode); + break; + case cmUVProcessChain::ExceptionCode::Spawn: + e4.Content("Error administrating child process: "); + e4.Content(exception.second); + break; + default: + e4.Content("Terminated abnormally: "); + e4.Content(exception.second); + break; + } + } else { + e4.Content("Killed when timeout expired"); } } diff --git a/Source/CTest/cmCTestLaunchReporter.h b/Source/CTest/cmCTestLaunchReporter.h index 4be0d9b..2bb78f8 100644 --- a/Source/CTest/cmCTestLaunchReporter.h +++ b/Source/CTest/cmCTestLaunchReporter.h @@ -10,6 +10,8 @@ #include "cmsys/RegularExpression.hxx" +#include "cmUVProcessChain.h" + class cmXMLElement; /** \class cmCTestLaunchReporter @@ -48,7 +50,7 @@ public: void ComputeFileNames(); bool Passthru; - struct cmsysProcess_s* Process; + cmUVProcessChain::Status Status; int ExitCode; // Temporary log files for stdout and stderr of real command. diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx index 0e002b9..5d71b84 100644 --- a/Source/CTest/cmCTestP4.cxx +++ b/Source/CTest/cmCTestP4.cxx @@ -149,17 +149,16 @@ cmCTestP4::User cmCTestP4::GetUserData(const std::string& username) auto it = this->Users.find(username); if (it == this->Users.end()) { - std::vector<char const*> p4_users; + std::vector<std::string> p4_users; this->SetP4Options(p4_users); p4_users.push_back("users"); p4_users.push_back("-m"); p4_users.push_back("1"); - p4_users.push_back(username.c_str()); - p4_users.push_back(nullptr); + p4_users.push_back(username); UserParser out(this, "users-out> "); OutputLogger err(this->Log, "users-err> "); - this->RunChild(p4_users.data(), &out, &err); + this->RunChild(p4_users, &out, &err); // The user should now be added to the map. Search again. it = this->Users.find(username); @@ -303,10 +302,10 @@ private: } }; -void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions) +void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions) { if (this->P4Options.empty()) { - const char* p4 = this->CommandLineTool.c_str(); + std::string p4 = this->CommandLineTool; this->P4Options.emplace_back(p4); // The CTEST_P4_CLIENT variable sets the P4 client used when issuing @@ -328,15 +327,12 @@ void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions) cm::append(this->P4Options, cmSystemTools::ParseArguments(opts)); } - CommandOptions.clear(); - for (std::string const& o : this->P4Options) { - CommandOptions.push_back(o.c_str()); - } + CommandOptions = this->P4Options; } std::string cmCTestP4::GetWorkingRevision() { - std::vector<char const*> p4_identify; + std::vector<std::string> p4_identify; this->SetP4Options(p4_identify); p4_identify.push_back("changes"); @@ -345,14 +341,13 @@ std::string cmCTestP4::GetWorkingRevision() p4_identify.push_back("-t"); std::string source = this->SourceDirectory + "/...#have"; - p4_identify.push_back(source.c_str()); - p4_identify.push_back(nullptr); + p4_identify.push_back(source); std::string rev; IdentifyParser out(this, "p4_changes-out> ", rev); OutputLogger err(this->Log, "p4_changes-err> "); - bool result = this->RunChild(p4_identify.data(), &out, &err); + bool result = this->RunChild(p4_identify, &out, &err); // If there was a problem contacting the server return "<unknown>" if (!result) { @@ -388,7 +383,7 @@ bool cmCTestP4::NoteNewRevision() bool cmCTestP4::LoadRevisions() { - std::vector<char const*> p4_changes; + std::vector<std::string> p4_changes; this->SetP4Options(p4_changes); // Use 'p4 changes ...@old,new' to get a list of changelists @@ -409,38 +404,36 @@ bool cmCTestP4::LoadRevisions() .append(this->NewRevision); p4_changes.push_back("changes"); - p4_changes.push_back(range.c_str()); - p4_changes.push_back(nullptr); + p4_changes.push_back(range); ChangesParser out(this, "p4_changes-out> "); OutputLogger err(this->Log, "p4_changes-err> "); this->ChangeLists.clear(); - this->RunChild(p4_changes.data(), &out, &err); + this->RunChild(p4_changes, &out, &err); if (this->ChangeLists.empty()) { return true; } // p4 describe -s ...@1111111,2222222 - std::vector<char const*> p4_describe; + std::vector<std::string> p4_describe; for (std::string const& i : cmReverseRange(this->ChangeLists)) { this->SetP4Options(p4_describe); p4_describe.push_back("describe"); p4_describe.push_back("-s"); - p4_describe.push_back(i.c_str()); - p4_describe.push_back(nullptr); + p4_describe.push_back(i); DescribeParser outDescribe(this, "p4_describe-out> "); OutputLogger errDescribe(this->Log, "p4_describe-err> "); - this->RunChild(p4_describe.data(), &outDescribe, &errDescribe); + this->RunChild(p4_describe, &outDescribe, &errDescribe); } return true; } bool cmCTestP4::LoadModifications() { - std::vector<char const*> p4_diff; + std::vector<std::string> p4_diff; this->SetP4Options(p4_diff); p4_diff.push_back("diff"); @@ -448,12 +441,11 @@ bool cmCTestP4::LoadModifications() // Ideally we would use -Od but not all clients support it p4_diff.push_back("-dn"); std::string source = this->SourceDirectory + "/..."; - p4_diff.push_back(source.c_str()); - p4_diff.push_back(nullptr); + p4_diff.push_back(source); DiffParser out(this, "p4_diff-out> "); OutputLogger err(this->Log, "p4_diff-err> "); - this->RunChild(p4_diff.data(), &out, &err); + this->RunChild(p4_diff, &out, &err); return true; } @@ -461,17 +453,14 @@ bool cmCTestP4::UpdateCustom(const std::string& custom) { cmList p4_custom_command{ custom, cmList::EmptyElements::Yes }; - std::vector<char const*> p4_custom; - p4_custom.reserve(p4_custom_command.size() + 1); - for (std::string const& i : p4_custom_command) { - p4_custom.push_back(i.c_str()); - } - p4_custom.push_back(nullptr); + std::vector<std::string> p4_custom; + p4_custom.reserve(p4_custom_command.size()); + cm::append(p4_custom, p4_custom_command); OutputLogger custom_out(this->Log, "p4_customsync-out> "); OutputLogger custom_err(this->Log, "p4_customsync-err> "); - return this->RunUpdateCommand(p4_custom.data(), &custom_out, &custom_err); + return this->RunUpdateCommand(p4_custom, &custom_out, &custom_err); } bool cmCTestP4::UpdateImpl() @@ -488,7 +477,7 @@ bool cmCTestP4::UpdateImpl() return false; } - std::vector<char const*> p4_sync; + std::vector<std::string> p4_sync; this->SetP4Options(p4_sync); p4_sync.push_back("sync"); @@ -499,9 +488,7 @@ bool cmCTestP4::UpdateImpl() opts = this->CTest->GetCTestConfiguration("P4UpdateOptions"); } std::vector<std::string> args = cmSystemTools::ParseArguments(opts); - for (std::string const& arg : args) { - p4_sync.push_back(arg.c_str()); - } + cm::append(p4_sync, args); std::string source = this->SourceDirectory + "/..."; @@ -515,11 +502,10 @@ bool cmCTestP4::UpdateImpl() source.append("@\"").append(date).append("\""); } - p4_sync.push_back(source.c_str()); - p4_sync.push_back(nullptr); + p4_sync.push_back(source); OutputLogger out(this->Log, "p4_sync-out> "); OutputLogger err(this->Log, "p4_sync-err> "); - return this->RunUpdateCommand(p4_sync.data(), &out, &err); + return this->RunUpdateCommand(p4_sync, &out, &err); } diff --git a/Source/CTest/cmCTestP4.h b/Source/CTest/cmCTestP4.h index 1889520..827caa1 100644 --- a/Source/CTest/cmCTestP4.h +++ b/Source/CTest/cmCTestP4.h @@ -39,7 +39,7 @@ private: std::vector<std::string> P4Options; User GetUserData(const std::string& username); - void SetP4Options(std::vector<char const*>& options); + void SetP4Options(std::vector<std::string>& options); std::string GetWorkingRevision(); bool NoteOldRevision() override; diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx index 91a1177..14bc510 100644 --- a/Source/CTest/cmCTestSVN.cxx +++ b/Source/CTest/cmCTestSVN.cxx @@ -33,7 +33,7 @@ cmCTestSVN::~cmCTestSVN() = default; void cmCTestSVN::CleanupImpl() { - std::vector<const char*> svn_cleanup; + std::vector<std::string> svn_cleanup; svn_cleanup.push_back("cleanup"); OutputLogger out(this->Log, "cleanup-out> "); 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) { // Run "svn info" to get the repository info from the work tree. - std::vector<const char*> svn_info; + std::vector<std::string> svn_info; svn_info.push_back("info"); - svn_info.push_back(svninfo.LocalPath.c_str()); + svn_info.push_back(svninfo.LocalPath); std::string rev; InfoParser out(this, "info-out> ", rev, svninfo); OutputLogger err(this->Log, "info-err> "); @@ -251,26 +251,24 @@ bool cmCTestSVN::UpdateImpl() args.push_back("-r{" + this->GetNightlyTime() + " +0000}"); } - std::vector<char const*> svn_update; + std::vector<std::string> svn_update; svn_update.push_back("update"); - for (std::string const& arg : args) { - svn_update.push_back(arg.c_str()); - } + cm::append(svn_update, args); UpdateParser out(this, "up-out> "); OutputLogger err(this->Log, "up-err> "); return this->RunSVNCommand(svn_update, &out, &err); } -bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters, +bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters, OutputParser* out, OutputParser* err) { if (parameters.empty()) { return false; } - std::vector<char const*> args; - args.push_back(this->CommandLineTool.c_str()); + std::vector<std::string> args; + args.push_back(this->CommandLineTool); cm::append(args, parameters); args.push_back("--non-interactive"); @@ -278,16 +276,12 @@ bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters, std::vector<std::string> parsedUserOptions = cmSystemTools::ParseArguments(userOptions); - for (std::string const& opt : parsedUserOptions) { - args.push_back(opt.c_str()); - } - - args.push_back(nullptr); + cm::append(args, parsedUserOptions); - if (strcmp(parameters[0], "update") == 0) { - return this->RunUpdateCommand(args.data(), out, err); + if (parameters[0] == "update") { + return this->RunUpdateCommand(args, out, err); } - return this->RunChild(args.data(), out, err); + return this->RunChild(args, out, err); } class cmCTestSVN::LogParser @@ -393,7 +387,7 @@ bool cmCTestSVN::LoadRevisions(SVNInfo& svninfo) } // Run "svn log" to get all global revisions of interest. - std::vector<const char*> svn_log; + std::vector<std::string> svn_log; svn_log.push_back("log"); svn_log.push_back("--xml"); svn_log.push_back("-v"); @@ -472,7 +466,7 @@ private: bool cmCTestSVN::LoadModifications() { // Run "svn status" which reports local modifications. - std::vector<const char*> svn_status; + std::vector<std::string> svn_status; svn_status.push_back("status"); StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); @@ -534,7 +528,7 @@ bool cmCTestSVN::LoadRepositories() this->RootInfo = &(this->Repositories.back()); // Run "svn status" to get the list of external repositories - std::vector<const char*> svn_status; + std::vector<std::string> svn_status; svn_status.push_back("status"); ExternalParser out(this, "external-out> "); OutputLogger err(this->Log, "external-err> "); diff --git a/Source/CTest/cmCTestSVN.h b/Source/CTest/cmCTestSVN.h index 370d176..1485dc0 100644 --- a/Source/CTest/cmCTestSVN.h +++ b/Source/CTest/cmCTestSVN.h @@ -33,7 +33,7 @@ private: bool NoteNewRevision() override; bool UpdateImpl() override; - bool RunSVNCommand(std::vector<char const*> const& parameters, + bool RunSVNCommand(std::vector<std::string> const& parameters, OutputParser* out, OutputParser* err); // Information about an SVN repository (root repository or external) diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 461ad1a..48f8f6d 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -11,8 +11,9 @@ #include <cm/memory> +#include <cm3p/uv.h> + #include "cmsys/Directory.hxx" -#include "cmsys/Process.h" #include "cmCTest.h" #include "cmCTestBuildCommand.h" @@ -40,6 +41,8 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" #include "cmValue.h" #include "cmake.h" @@ -148,66 +151,65 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg) // now pass through all the other arguments std::vector<std::string>& initArgs = 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 - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); - // cmsysProcess_SetWorkingDirectory(cp, dir); - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - // cmsysProcess_SetTimeout(cp, timeout); - cmsysProcess_Execute(cp); + cmUVProcessChainBuilder builder; + builder.AddCommand(initArgs) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + auto process = builder.Start(); + cm::uv_pipe_ptr outPipe; + 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> err; std::string line; - int pipe = - cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err); - while (pipe != cmsysProcess_Pipe_None) { + auto pipe = + cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line, + std::chrono::seconds(100), out, err); + while (pipe != cmSystemTools::WaitForLineResult::None) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Output: " << line << "\n"); - if (pipe == cmsysProcess_Pipe_STDERR) { + if (pipe == cmSystemTools::WaitForLineResult::STDERR) { cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n"); - } else if (pipe == cmsysProcess_Pipe_STDOUT) { + } else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n"); } - pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, - err); + pipe = + cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line, + std::chrono::seconds(100), out, err); } // Properly handle output of the build command - cmsysProcess_WaitForExit(cp, nullptr); - int result = cmsysProcess_GetState(cp); + process.Wait(); + auto const& status = process.GetStatus(0); + auto result = status.GetException(); int retVal = 0; bool failed = false; - if (result == cmsysProcess_State_Exited) { - retVal = cmsysProcess_GetExitValue(cp); - } else if (result == cmsysProcess_State_Exception) { - retVal = cmsysProcess_GetExitException(cp); - 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, - "\tThere was a timeout" << std::endl); - failed = true; - } else if (result == cmsysProcess_State_Error) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "\tError executing ctest: " << cmsysProcess_GetErrorString(cp) - << std::endl); - failed = true; + switch (result.first) { + case cmUVProcessChain::ExceptionCode::None: + retVal = static_cast<int>(status.ExitStatus); + break; + case cmUVProcessChain::ExceptionCode::Spawn: + cmCTestLog(this->CTest, ERROR_MESSAGE, + "\tError executing ctest: " << result.second << std::endl); + failed = true; + break; + default: + retVal = status.TermSignal; + cmCTestLog(this->CTest, ERROR_MESSAGE, + "\tThere was an exception: " << result.second << " " << retVal + << std::endl); + failed = true; } - cmsysProcess_Delete(cp); if (failed) { std::ostringstream message; message << "Error running command: ["; - message << result << "] "; + message << static_cast<int>(result.first) << "] "; for (const char* arg : argv) { if (arg) { message << arg << " "; diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index 1934d8c..77af889 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -309,6 +309,9 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( // specify target ::curl_easy_setopt(curl, CURLOPT_URL, upload_as.c_str()); + // follow redirects + ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + // CURLAUTH_BASIC is default, and here we allow additional methods, // including more secure ones ::curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); diff --git a/Source/CTest/cmCTestVC.cxx b/Source/CTest/cmCTestVC.cxx index 609ccba..cbbb5a5 100644 --- a/Source/CTest/cmCTestVC.cxx +++ b/Source/CTest/cmCTestVC.cxx @@ -7,10 +7,9 @@ #include <sstream> #include <vector> -#include "cmsys/Process.h" - #include "cmCTest.h" #include "cmSystemTools.h" +#include "cmUVProcessChain.h" #include "cmValue.h" #include "cmXMLWriter.h" @@ -55,18 +54,12 @@ bool cmCTestVC::InitialCheckout(const std::string& command) // Construct the initial checkout command line. 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. this->Log << "--- Begin Initial Checkout ---\n"; OutputLogger out(this->Log, "co-out> "); OutputLogger err(this->Log, "co-err> "); - bool result = this->RunChild(vc_co.data(), &out, &err, parent.c_str()); + bool result = this->RunChild(args, &out, &err, parent); this->Log << "--- End Initial Checkout ---\n"; if (!result) { cmCTestLog(this->CTest, ERROR_MESSAGE, @@ -75,35 +68,35 @@ bool cmCTestVC::InitialCheckout(const std::string& command) return result; } -bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out, - OutputParser* err, const char* workDir, - Encoding encoding) +bool cmCTestVC::RunChild(const std::vector<std::string>& cmd, + OutputParser* out, OutputParser* err, + std::string workDir, Encoding encoding) { this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n"; - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, cmd); - workDir = workDir ? workDir : this->SourceDirectory.c_str(); - cmsysProcess_SetWorkingDirectory(cp, workDir); - cmCTestVC::RunProcess(cp, out, err, encoding); - int result = cmsysProcess_GetExitValue(cp); - cmsysProcess_Delete(cp); - return result == 0; + cmUVProcessChainBuilder builder; + if (workDir.empty()) { + workDir = this->SourceDirectory; + } + builder.AddCommand(cmd).SetWorkingDirectory(workDir); + auto status = cmCTestVC::RunProcess(builder, out, err, encoding); + return status.front().SpawnResult == 0 && status.front().ExitStatus == 0; } -std::string cmCTestVC::ComputeCommandLine(char const* const* cmd) +std::string cmCTestVC::ComputeCommandLine(const std::vector<std::string>& cmd) { std::ostringstream line; const char* sep = ""; - for (const char* const* arg = cmd; *arg; ++arg) { - line << sep << "\"" << *arg << "\""; + for (auto const& arg : cmd) { + line << sep << "\"" << arg << "\""; sep = " "; } return line.str(); } -bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out, - OutputParser* err, Encoding encoding) +bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd, + OutputParser* out, OutputParser* err, + Encoding encoding) { // Report the command line. this->UpdateCommandLine = this->ComputeCommandLine(cmd); @@ -113,7 +106,7 @@ bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out, } // Run the command. - return this->RunChild(cmd, out, err, nullptr, encoding); + return this->RunChild(cmd, out, err, "", encoding); } std::string cmCTestVC::GetNightlyTime() diff --git a/Source/CTest/cmCTestVC.h b/Source/CTest/cmCTestVC.h index 7b03d10..dd5456d 100644 --- a/Source/CTest/cmCTestVC.h +++ b/Source/CTest/cmCTestVC.h @@ -6,6 +6,7 @@ #include <iosfwd> #include <string> +#include <vector> #include "cmProcessOutput.h" #include "cmProcessTools.h" @@ -108,15 +109,15 @@ protected: }; /** Convert a list of arguments to a human-readable command line. */ - static std::string ComputeCommandLine(char const* const* cmd); + static std::string ComputeCommandLine(const std::vector<std::string>& cmd); /** Run a command line and send output to given parsers. */ - bool RunChild(char const* const* cmd, OutputParser* out, OutputParser* err, - const char* workDir = nullptr, + bool RunChild(const std::vector<std::string>& cmd, OutputParser* out, + OutputParser* err, std::string workDir = {}, Encoding encoding = cmProcessOutput::Auto); /** Run VC update command line and send output to given parsers. */ - bool RunUpdateCommand(char const* const* cmd, OutputParser* out, + bool RunUpdateCommand(const std::vector<std::string>& cmd, OutputParser* out, OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto); diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx index 85b379b..f1d351a 100644 --- a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx +++ b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx @@ -667,6 +667,10 @@ Modify cmCTestResourceGroupsLexer.cxx: #include <cstddef> +#ifndef _WIN32 +# include <termios.h> +#endif + /*--------------------------------------------------------------------------*/ #define INITIAL 0 diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l index 2befa85..ac9cbaf 100644 --- a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l +++ b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l @@ -26,6 +26,10 @@ Modify cmCTestResourceGroupsLexer.cxx: #include <cstddef> +#ifndef _WIN32 +# include <termios.h> +#endif + /*--------------------------------------------------------------------------*/ %} diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 6ff6437..36fd3a8 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -5,6 +5,7 @@ #include <algorithm> #include <cctype> #include <chrono> +#include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> @@ -24,13 +25,13 @@ #include <cmext/string_view> #include <cm3p/curl/curl.h> +#include <cm3p/uv.h> #include <cm3p/zlib.h> #include "cmsys/Base64.h" #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" #include "cmsys/Glob.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" #include "cmsys/SystemInformation.hxx" #if defined(_WIN32) @@ -64,6 +65,9 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" #include "cmValue.h" #include "cmVersion.h" #include "cmVersionConfig.h" @@ -1074,9 +1078,9 @@ int cmCTest::GetTestModelFromString(const std::string& str) // ###################################################################### // ###################################################################### -int cmCTest::RunMakeCommand(const std::string& command, std::string& output, - int* retVal, const char* dir, cmDuration timeout, - std::ostream& ofs, Encoding encoding) +bool cmCTest::RunMakeCommand(const std::string& command, std::string& output, + int* retVal, const char* dir, cmDuration timeout, + std::ostream& ofs, Encoding encoding) { // First generate the command and arguments std::vector<std::string> args = cmSystemTools::ParseArguments(command); @@ -1085,107 +1089,107 @@ int cmCTest::RunMakeCommand(const std::string& command, std::string& output, 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(); cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:"); - for (char const* arg : argv) { - if (!arg) { - break; - } + for (auto const& arg : args) { cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\""); } cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl); // Now create process object - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); - cmsysProcess_SetWorkingDirectory(cp, dir); - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - cmsysProcess_SetTimeout(cp, timeout.count()); - cmsysProcess_Execute(cp); + cmUVProcessChainBuilder builder; + builder.AddCommand(args).SetMergedBuiltinStreams(); + if (dir) { + builder.SetWorkingDirectory(dir); + } + auto chain = builder.Start(); + cm::uv_pipe_ptr outputStream; + outputStream.init(chain.GetLoop(), 0); + uv_pipe_open(outputStream, chain.OutputStream()); // Initialize tick's std::string::size_type tick = 0; std::string::size_type tick_len = 1024; std::string::size_type tick_line_len = 50; - char* data; - int length; cmProcessOutput processOutput(encoding); - std::string strdata; cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, " Each . represents " << tick_len << " bytes of output\n" " " << std::flush); - while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) { - processOutput.DecodeText(data, length, strdata); - for (char& cc : strdata) { - if (cc == 0) { - cc = '\n'; + auto outputHandle = cmUVStreamRead( + outputStream, + [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) { + if (cc == 0) { + cc = '\n'; + } } - } - output.append(strdata); - while (output.size() > (tick * tick_len)) { - tick++; - cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush); - if (tick % tick_line_len == 0 && tick > 0) { - cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, - " Size: " << int((double(output.size()) / 1024.0) + 1) - << "K\n " << std::flush); + output.append(strdata); + while (output.size() > (tick * tick_len)) { + tick++; + cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush); + if (tick % tick_line_len == 0 && tick > 0) { + cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, + " Size: " << int((double(output.size()) / 1024.0) + 1) + << "K\n " << std::flush); + } } - } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); - if (ofs) { - ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); - } - } - processOutput.DecodeText(std::string(), strdata); - if (!strdata.empty()) { - output.append(strdata); - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); - if (ofs) { - ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); - } - } + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + cmCTestLogWrite(strdata.c_str(), strdata.size())); + if (ofs) { + ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); + } + }, + [this, &processOutput, &output, &ofs]() { + std::string strdata; + processOutput.DecodeText(std::string(), strdata); + if (!strdata.empty()) { + output.append(strdata); + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + cmCTestLogWrite(strdata.c_str(), strdata.size())); + if (ofs) { + ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); + } + } + }); + + bool finished = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0)); cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, " Size of output: " << int(double(output.size()) / 1024.0) << "K" << std::endl); - cmsysProcess_WaitForExit(cp, nullptr); - - int result = cmsysProcess_GetState(cp); - - if (result == cmsysProcess_State_Exited) { - *retVal = cmsysProcess_GetExitValue(cp); - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - "Command exited with the value: " << *retVal << std::endl); - } else if (result == cmsysProcess_State_Exception) { - *retVal = cmsysProcess_GetExitException(cp); - cmCTestLog(this, WARNING, - "There was an exception: " << *retVal << std::endl); - } else if (result == cmsysProcess_State_Expired) { + if (finished) { + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + *retVal = static_cast<int>(status.ExitStatus); + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + "Command exited with the value: " << *retVal << std::endl); + break; + case cmUVProcessChain::ExceptionCode::Spawn: + 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, + "There was an exception: " << *retVal << std::endl); + break; + } + } else { 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); } - cmsysProcess_Delete(cp); - - return result; + return true; } // ###################################################################### @@ -1193,9 +1197,10 @@ int cmCTest::RunMakeCommand(const std::string& command, std::string& output, // ###################################################################### // ###################################################################### -int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, - int* retVal, std::ostream* log, cmDuration testTimeOut, - std::vector<std::string>* environment, Encoding encoding) +bool cmCTest::RunTest(const std::vector<std::string>& argv, + std::string* output, int* retVal, std::ostream* log, + cmDuration testTimeOut, + std::vector<std::string>* environment, Encoding encoding) { bool modifyEnv = (environment && !environment->empty()); @@ -1234,19 +1239,16 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, inst.SetStreams(&oss, &oss); std::vector<std::string> args; - for (char const* i : argv) { - if (i) { - // make sure we pass the timeout in for any build and test - // invocations. Since --build-generator is required this is a - // good place to check for it, and to add the arguments in - if (strcmp(i, "--build-generator") == 0 && - timeout != cmCTest::MaxDuration() && - timeout > cmDuration::zero()) { - args.emplace_back("--test-timeout"); - args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout))); - } - args.emplace_back(i); + for (auto const& i : argv) { + // make sure we pass the timeout in for any build and test + // invocations. Since --build-generator is required this is a + // good place to check for it, and to add the arguments in + if (i == "--build-generator" && timeout != cmCTest::MaxDuration() && + timeout > cmDuration::zero()) { + args.emplace_back("--test-timeout"); + args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout))); } + args.emplace_back(i); } if (log) { *log << "* Run internal CTest" << std::endl; @@ -1272,7 +1274,7 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, << std::endl); } - return cmsysProcess_State_Exited; + return true; } std::vector<char> tempOutput; if (output) { @@ -1285,41 +1287,43 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, cmSystemTools::AppendEnv(*environment); } - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); + cmUVProcessChainBuilder builder; + builder.AddCommand(argv).SetMergedBuiltinStreams(); cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl); - if (cmSystemTools::GetRunCommandHideConsole()) { - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - } + auto chain = builder.Start(); - cmsysProcess_SetTimeout(cp, timeout.count()); - cmsysProcess_Execute(cp); - - char* data; - int length; cmProcessOutput processOutput(encoding); - std::string strdata; - while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) { - processOutput.DecodeText(data, length, strdata); - if (output) { - cm::append(tempOutput, data, data + length); - } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); - if (log) { - log->write(strdata.c_str(), strdata.size()); - } - } - processOutput.DecodeText(std::string(), strdata); - if (!strdata.empty()) { - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); - if (log) { - log->write(strdata.c_str(), strdata.size()); - } - } + 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; + processOutput.DecodeText(data.data(), data.size(), strdata); + if (output) { + cm::append(tempOutput, data.data(), data.data() + data.size()); + } + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + cmCTestLogWrite(strdata.c_str(), strdata.size())); + if (log) { + log->write(strdata.c_str(), strdata.size()); + } + }, + [this, &processOutput, &log]() { + std::string strdata; + processOutput.DecodeText(std::string(), strdata); + if (!strdata.empty()) { + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + cmCTestLogWrite(strdata.c_str(), strdata.size())); + if (log) { + log->write(strdata.c_str(), strdata.size()); + } + } + }); - cmsysProcess_WaitForExit(cp, nullptr); + bool complete = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0)); processOutput.DecodeText(tempOutput, tempOutput); if (output && tempOutput.begin() != tempOutput.end()) { output->append(tempOutput.data(), tempOutput.size()); @@ -1327,33 +1331,41 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "-- Process completed" << std::endl); - int result = cmsysProcess_GetState(cp); + bool result = false; - if (result == cmsysProcess_State_Exited) { - *retVal = cmsysProcess_GetExitValue(cp); - if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) { - this->OutputTestErrors(tempOutput); - } - } else if (result == cmsysProcess_State_Exception) { - if (this->Impl->OutputTestOutputOnTestFailure) { - this->OutputTestErrors(tempOutput); - } - *retVal = cmsysProcess_GetExitException(cp); - std::string outerr = cmStrCat("\n*** Exception executing: ", - cmsysProcess_GetExceptionString(cp)); - if (output) { - *output += outerr; - } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); - } else if (result == cmsysProcess_State_Error) { - std::string outerr = - cmStrCat("\n*** ERROR executing: ", cmsysProcess_GetErrorString(cp)); - if (output) { - *output += outerr; + if (complete) { + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + *retVal = static_cast<int>(status.ExitStatus); + if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) { + this->OutputTestErrors(tempOutput); + } + result = true; + break; + case cmUVProcessChain::ExceptionCode::Spawn: { + std::string outerr = + cmStrCat("\n*** ERROR executing: ", exception.second); + if (output) { + *output += outerr; + } + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); + } break; + default: { + if (this->Impl->OutputTestOutputOnTestFailure) { + this->OutputTestErrors(tempOutput); + } + *retVal = status.TermSignal; + std::string outerr = + cmStrCat("\n*** Exception executing: ", exception.second); + if (output) { + *output += outerr; + } + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); + } break; } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); } - cmsysProcess_Delete(cp); return result; } @@ -3471,49 +3483,70 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, stdOut->clear(); stdErr->clear(); - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); - cmsysProcess_SetWorkingDirectory(cp, dir); - if (cmSystemTools::GetRunCommandHideConsole()) { - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); + cmUVProcessChainBuilder builder; + builder.AddCommand(args) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + if (dir) { + 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; + bool outFinished = false; + cm::uv_pipe_ptr outStream; std::vector<char> tempError; - char* data; - int length; + bool errFinished = false; + cm::uv_pipe_ptr errStream; cmProcessOutput processOutput(encoding); - std::string 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); - } + 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; + processOutput.DecodeText(data.data(), data.size(), 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) { + std::string strdata; processOutput.DecodeText(std::string(), strdata); if (!strdata.empty()) { cmSystemTools::Stdout(strdata); } } - cmsysProcess_WaitForExit(cp, nullptr); + while (!timedOut && !chain.Finished()) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); + } if (!tempOutput.empty()) { processOutput.DecodeText(tempOutput, tempOutput); stdOut->append(tempOutput.data(), tempOutput.size()); @@ -3524,32 +3557,32 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, } bool result = true; - if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) { - if (retVal) { - *retVal = cmsysProcess_GetExitValue(cp); - } else { - if (cmsysProcess_GetExitValue(cp) != 0) { - result = false; - } - } - } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) { - const char* exception_str = cmsysProcess_GetExceptionString(cp); - cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl); - 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) { + if (timedOut) { 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) { + *retVal = static_cast<int>(status.ExitStatus); + } else { + if (status.ExitStatus != 0) { + result = false; + } + } + break; + default: { + cmCTestLog(this, ERROR_MESSAGE, exception.second << std::endl); + stdErr->append(exception.second); + result = false; + } break; + } } - cmsysProcess_Delete(cp); return result; } diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 9a8d5a6..1644d84 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -254,10 +254,10 @@ public: * Run command specialized for make and configure. Returns process status * and retVal is return value or exception. */ - int RunMakeCommand(const std::string& command, std::string& output, - int* retVal, const char* dir, cmDuration timeout, - std::ostream& ofs, - Encoding encoding = cmProcessOutput::Auto); + bool RunMakeCommand(const std::string& command, std::string& output, + int* retVal, const char* dir, cmDuration timeout, + std::ostream& ofs, + Encoding encoding = cmProcessOutput::Auto); /** Return the current tag */ std::string GetCurrentTag(); @@ -303,10 +303,10 @@ public: * environment variables prior to running the test. After running the test, * environment variables are restored to their previous values. */ - int RunTest(std::vector<const char*> args, std::string* output, int* retVal, - std::ostream* logfile, cmDuration testTimeOut, - std::vector<std::string>* environment, - Encoding encoding = cmProcessOutput::Auto); + bool RunTest(const std::vector<std::string>& args, std::string* output, + int* retVal, std::ostream* logfile, cmDuration testTimeOut, + std::vector<std::string>* environment, + Encoding encoding = cmProcessOutput::Auto); /** * Get the handler object diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index ccd20a0..91395e2 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -538,11 +538,14 @@ cmComputeLinkInformation::GetSharedLibrariesLinked() const bool cmComputeLinkInformation::Compute() { - // Skip targets that do not link. + // Skip targets that do not link or have link-like information consumers may + // need (namely modules). if (!(this->Target->GetType() == cmStateEnums::EXECUTABLE || this->Target->GetType() == cmStateEnums::SHARED_LIBRARY || this->Target->GetType() == cmStateEnums::MODULE_LIBRARY || - this->Target->GetType() == cmStateEnums::STATIC_LIBRARY)) { + this->Target->GetType() == cmStateEnums::STATIC_LIBRARY || + this->Target->HaveCxx20ModuleSources() || + this->Target->HaveFortranSources())) { return false; } diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 3b98219..9b5f2b4 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -2,8 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExecuteProcessCommand.h" -#include <algorithm> #include <cctype> /* isspace */ +#include <cstdint> #include <cstdio> #include <iostream> #include <map> @@ -16,7 +16,9 @@ #include <cmext/algorithm> #include <cmext/string_view> -#include "cmsys/Process.h" +#include <cm3p/uv.h> + +#include "cm_fileno.hxx" #include "cmArgumentParser.h" #include "cmExecutionStatus.h" @@ -26,6 +28,9 @@ #include "cmProcessOutput.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" namespace { bool cmExecuteProcessCommandIsWhitespace(char c) @@ -36,7 +41,7 @@ bool cmExecuteProcessCommandIsWhitespace(char c) void cmExecuteProcessCommandFixText(std::vector<char>& output, bool strip_trailing_whitespace); void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, - int length); + std::size_t length); } // cmExecuteProcessCommand @@ -143,57 +148,61 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } // Create a process instance. - std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr( - cmsysProcess_New(), cmsysProcess_Delete); - cmsysProcess* cp = cp_ptr.get(); + cmUVProcessChainBuilder builder; // Set the command sequence. for (std::vector<std::string> const& cmd : arguments.Commands) { - 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()); + builder.AddCommand(cmd); } // Set the process working directory. if (!arguments.WorkingDirectory.empty()) { - cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str()); + builder.SetWorkingDirectory(arguments.WorkingDirectory); } - // Always hide the process window. - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - // Check the output variables. - bool merge_output = false; + std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose); if (!arguments.InputFile.empty()) { - cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN, - arguments.InputFile.c_str()); + inputFile.reset(cmsys::SystemTools::Fopen(arguments.InputFile, "rb")); + 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); if (!arguments.OutputFile.empty()) { - cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT, - arguments.OutputFile.c_str()); + outputFile.reset(cmsys::SystemTools::Fopen(arguments.OutputFile, "wb")); + builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, + cm_fileno(outputFile.get())); + } else { + if (arguments.OutputVariable == arguments.ErrorVariable && + !arguments.ErrorVariable.empty()) { + builder.SetMergedBuiltinStreams(); + } else { + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + } } + + std::unique_ptr<FILE, int (*)(FILE*)> errorFile(nullptr, fclose); if (!arguments.ErrorFile.empty()) { if (arguments.ErrorFile == arguments.OutputFile) { - merge_output = true; + builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(outputFile.get())); } else { - cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR, - arguments.ErrorFile.c_str()); + errorFile.reset(cmsys::SystemTools::Fopen(arguments.ErrorFile, "wb")); + builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(errorFile.get())); } - } - if (!arguments.OutputVariable.empty() && - arguments.OutputVariable == arguments.ErrorVariable) { - merge_output = true; - } - if (merge_output) { - cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1); + } else if (arguments.ErrorVariable.empty() || + (!arguments.ErrorVariable.empty() && + arguments.OutputVariable != arguments.ErrorVariable)) { + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); } // Set the timeout if any. - if (timeout >= 0) { - cmsysProcess_SetTimeout(cp, timeout); - } + int64_t timeoutMillis = static_cast<int64_t>(timeout * 1000.0); bool echo_stdout = false; bool echo_stderr = false; @@ -241,36 +250,86 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } // Start the process. - cmsysProcess_Execute(cp); + auto chain = builder.Start(); + + 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. - std::vector<char> tempOutput; - std::vector<char> tempError; - int length; - char* data; - int p; + struct ReadData + { + bool Finished = false; + std::vector<char> Output; + cm::uv_pipe_ptr Stream; + }; + ReadData outputData; + ReadData errorData; cmProcessOutput processOutput( cmProcessOutput::FindEncoding(arguments.Encoding)); std::string strdata; - while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) { - // Put the output in the right place. - if (p == cmsysProcess_Pipe_STDOUT && !arguments.OutputQuiet) { - if (arguments.OutputVariable.empty() || arguments.EchoOutputVariable) { - processOutput.DecodeText(data, length, strdata, 1); - cmSystemTools::Stdout(strdata); - } - if (!arguments.OutputVariable.empty()) { - cmExecuteProcessCommandAppend(tempOutput, data, length); - } - } else if (p == cmsysProcess_Pipe_STDERR && !arguments.ErrorQuiet) { - if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) { - processOutput.DecodeText(data, length, strdata, 2); - cmSystemTools::Stderr(strdata); - } - if (!arguments.ErrorVariable.empty()) { - cmExecuteProcessCommandAppend(tempError, data, length); - } - } + + std::unique_ptr<cmUVStreamReadHandle> outputHandle; + if (chain.OutputStream() >= 0) { + outputData.Stream.init(chain.GetLoop(), 0); + uv_pipe_open(outputData.Stream, chain.OutputStream()); + 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); + } + if (!arguments.OutputVariable.empty()) { + cmExecuteProcessCommandAppend(outputData.Output, data.data(), + data.size()); + } + } + }, + [&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) { + processOutput.DecodeText(data.data(), data.size(), strdata, 2); + cmSystemTools::Stderr(strdata); + } + if (!arguments.ErrorVariable.empty()) { + cmExecuteProcessCommandAppend(errorData.Output, data.data(), + 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 && (arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) { @@ -287,151 +346,102 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } - // All output has been read. Wait for the process to exit. - cmsysProcess_WaitForExit(cp, nullptr); - processOutput.DecodeText(tempOutput, tempOutput); - processOutput.DecodeText(tempError, tempError); + // All output has been read. + processOutput.DecodeText(outputData.Output, outputData.Output); + processOutput.DecodeText(errorData.Output, errorData.Output); // Fix the text in the output strings. - cmExecuteProcessCommandFixText(tempOutput, + cmExecuteProcessCommandFixText(outputData.Output, arguments.OutputStripTrailingWhitespace); - cmExecuteProcessCommandFixText(tempError, + cmExecuteProcessCommandFixText(errorData.Output, arguments.ErrorStripTrailingWhitespace); // Store the output obtained. - if (!arguments.OutputVariable.empty() && !tempOutput.empty()) { + if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) { status.GetMakefile().AddDefinition(arguments.OutputVariable, - tempOutput.data()); + outputData.Output.data()); } - if (!merge_output && !arguments.ErrorVariable.empty() && - !tempError.empty()) { + if (arguments.ErrorVariable != arguments.OutputVariable && + !arguments.ErrorVariable.empty() && !errorData.Output.empty()) { status.GetMakefile().AddDefinition(arguments.ErrorVariable, - tempError.data()); + errorData.Output.data()); } // Store the result of running the process. if (!arguments.ResultVariable.empty()) { - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { - int v = cmsysProcess_GetExitValue(cp); - char buf[16]; - snprintf(buf, sizeof(buf), "%d", v); - status.GetMakefile().AddDefinition(arguments.ResultVariable, buf); - } break; - case cmsysProcess_State_Exception: + if (timedOut) { + status.GetMakefile().AddDefinition(arguments.ResultVariable, + "Process terminated due to timeout"); + } else { + auto const* lastStatus = chain.GetStatus().back(); + auto exception = lastStatus->GetException(); + if (exception.first == cmUVProcessChain::ExceptionCode::None) { status.GetMakefile().AddDefinition( - arguments.ResultVariable, cmsysProcess_GetExceptionString(cp)); - break; - case cmsysProcess_State_Error: + arguments.ResultVariable, + std::to_string(static_cast<int>(lastStatus->ExitStatus))); + } else { status.GetMakefile().AddDefinition(arguments.ResultVariable, - cmsysProcess_GetErrorString(cp)); - break; - case cmsysProcess_State_Expired: - status.GetMakefile().AddDefinition( - arguments.ResultVariable, "Process terminated due to timeout"); - break; + exception.second); + } } } // Store the result of running the processes. if (!arguments.ResultsVariable.empty()) { - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { - std::vector<std::string> res; - for (size_t i = 0; i < arguments.Commands.size(); ++i) { - switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) { - case kwsysProcess_StateByIndex_Exited: { - int exitCode = - cmsysProcess_GetExitValueByIndex(cp, static_cast<int>(i)); - char buf[16]; - 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; - } + if (timedOut) { + status.GetMakefile().AddDefinition(arguments.ResultsVariable, + "Process terminated due to timeout"); + } else { + std::vector<std::string> res; + for (auto const* processStatus : chain.GetStatus()) { + auto exception = processStatus->GetException(); + if (exception.first == cmUVProcessChain::ExceptionCode::None) { + res.emplace_back( + std::to_string(static_cast<int>(processStatus->ExitStatus))); + } else { + res.emplace_back(exception.second); } - status.GetMakefile().AddDefinition(arguments.ResultsVariable, - 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; + } + status.GetMakefile().AddDefinition(arguments.ResultsVariable, + cmList::to_string(res)); } } - auto queryProcessStatusByIndex = [&cp](int index) -> std::string { - std::string processStatus; - switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(index))) { - case kwsysProcess_StateByIndex_Exited: { - int exitCode = cmsysProcess_GetExitValueByIndex(cp, index); - 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; + auto queryProcessStatusByIndex = [&chain](std::size_t index) -> std::string { + auto const& processStatus = chain.GetStatus(index); + auto exception = processStatus.GetException(); + if (exception.first == cmUVProcessChain::ExceptionCode::None) { + if (processStatus.ExitStatus) { + return cmStrCat("Child return code: ", processStatus.ExitStatus); } - case kwsysProcess_StateByIndex_Error: - default: - processStatus = "Error getting the child return code"; - break; + return ""; } - return processStatus; + return cmStrCat("Abnormal exit with child return code: ", + exception.second); }; if (arguments.CommandErrorIsFatal == "ANY"_s) { bool ret = true; - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { - std::map<int, std::string> failureIndices; - for (int i = 0; i < static_cast<int>(arguments.Commands.size()); ++i) { - std::string processStatus = queryProcessStatusByIndex(i); - if (!processStatus.empty()) { - failureIndices[i] = processStatus; - } - if (!failureIndices.empty()) { - std::ostringstream oss; - oss << "failed command indexes:\n"; - for (auto const& e : failureIndices) { - oss << " " << e.first + 1 << ": \"" << e.second << "\"\n"; - } - status.SetError(oss.str()); - ret = false; - } + if (timedOut) { + status.SetError("Process terminated due to timeout"); + ret = false; + } else { + 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); + if (!processStatus.empty()) { + failureIndices[i] = processStatus; } - } 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"); + } + if (!failureIndices.empty()) { + std::ostringstream oss; + oss << "failed command indexes:\n"; + for (auto const& e : failureIndices) { + oss << " " << e.first + 1 << ": \"" << e.second << "\"\n"; + } + status.SetError(oss.str()); ret = false; - break; + } } if (!ret) { @@ -442,29 +452,23 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, if (arguments.CommandErrorIsFatal == "LAST"_s) { bool ret = true; - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { + if (timedOut) { + status.SetError("Process terminated due to timeout"); + 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); const std::string processStatus = queryProcessStatusByIndex(lastIndex); if (!processStatus.empty()) { status.SetError("last command failed"); 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) { cmSystemTools::SetFatalErrorOccurred(); @@ -507,7 +511,7 @@ void cmExecuteProcessCommandFixText(std::vector<char>& output, } void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, - int length) + std::size_t length) { #if defined(__APPLE__) // HACK on Apple to work around bug with inserting at the diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 7799e1c..764618e 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -9042,6 +9042,15 @@ bool cmGeneratorTarget::HaveFortranSources(std::string const& config) const }); } +bool cmGeneratorTarget::HaveFortranSources() const +{ + auto sources = cmGeneratorTarget::GetAllConfigSources(); + return std::any_of(sources.begin(), sources.end(), + [](AllConfigSource const& sf) -> bool { + return sf.Source->GetLanguage() == "Fortran"_s; + }); +} + bool cmGeneratorTarget::HaveCxx20ModuleSources(std::string* errorMessage) const { auto const& fs_names = this->Target->GetAllFileSetNames(); diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index f347133..7673bef 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -1262,6 +1262,7 @@ public: cmGeneratorTarget const* t2) const; }; + bool HaveFortranSources() const; bool HaveFortranSources(std::string const& config) const; // C++20 module support queries. diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 3b9d2fd..90b8839 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -407,8 +407,10 @@ bool cmGlobalXCodeGenerator::ProcessGeneratorToolsetField( mf->IssueMessage(MessageType::FATAL_ERROR, e); return false; } - if (this->XcodeBuildSystem == BuildSystem::Twelve && - this->XcodeVersion < 120) { + if ((this->XcodeBuildSystem == BuildSystem::Twelve && + this->XcodeVersion < 120) || + (this->XcodeBuildSystem == BuildSystem::One && + this->XcodeVersion >= 140)) { /* clang-format off */ std::string const& e = cmStrCat( "Generator\n" diff --git a/Source/cmList.h b/Source/cmList.h index 0f875e7..1b94c2f 100644 --- a/Source/cmList.h +++ b/Source/cmList.h @@ -747,8 +747,10 @@ public: ExpandElements expandElements = ExpandElements::Yes, EmptyElements emptyElements = EmptyElements::No) { - this->insert(this->begin() + this->ComputeInsertIndex(index), first, last, - expandElements, emptyElements); + auto const offset = + static_cast<difference_type>(this->ComputeInsertIndex(index)); + this->insert(this->begin() + offset, first, last, expandElements, + emptyElements); return *this; } template <typename InputIterator> @@ -1186,13 +1188,13 @@ private: auto size = container.size(); insertPos = cmList::Insert(container, insertPos, *first, expandElements, emptyElements); - insertPos += container.size() - size; + insertPos += static_cast<decltype(delta)>(container.size() - size); } } else { for (; first != last; ++first) { if (!first->empty() || emptyElements == EmptyElements::Yes) { insertPos = container.insert(insertPos, *first); - insertPos++; + ++insertPos; } } } diff --git a/Source/cmProcessTools.cxx b/Source/cmProcessTools.cxx index 9e7854b..1dd1dce 100644 --- a/Source/cmProcessTools.cxx +++ b/Source/cmProcessTools.cxx @@ -2,48 +2,68 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmProcessTools.h" +#include <algorithm> +#include <iterator> #include <ostream> -#include "cmsys/Process.h" +#include <cm3p/uv.h> #include "cmProcessOutput.h" +#include "cmUVHandlePtr.h" +#include "cmUVStream.h" -void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, OutputParser* out, - OutputParser* err, Encoding encoding) +std::vector<cmUVProcessChain::Status> cmProcessTools::RunProcess( + cmUVProcessChainBuilder& builder, OutputParser* out, OutputParser* err, + Encoding encoding) { - cmsysProcess_Execute(cp); - char* data = nullptr; - int length = 0; - int p; cmProcessOutput processOutput(encoding); + + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + + auto chain = builder.Start(); + std::string strdata; - while ((out || err) && - (p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) { - if (out && p == cmsysProcess_Pipe_STDOUT) { - processOutput.DecodeText(data, length, strdata, 1); - if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) { - out = nullptr; + cm::uv_pipe_ptr outputPipe; + outputPipe.init(chain.GetLoop(), 0); + uv_pipe_open(outputPipe, chain.OutputStream()); + auto outputHandle = cmUVStreamRead( + 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()))) { + out = nullptr; + } } - } else if (err && p == cmsysProcess_Pipe_STDERR) { - processOutput.DecodeText(data, length, strdata, 2); - if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) { - err = nullptr; + }, + [&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()))) { + err = nullptr; + } } - } + }, + [&err]() { err = nullptr; }); + while (out || err || !chain.Finished()) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); } - if (out) { - processOutput.DecodeText(std::string(), strdata, 1); - if (!strdata.empty()) { - out->Process(strdata.c_str(), static_cast<int>(strdata.size())); - } - } - if (err) { - processOutput.DecodeText(std::string(), strdata, 2); - if (!strdata.empty()) { - err->Process(strdata.c_str(), static_cast<int>(strdata.size())); - } - } - cmsysProcess_WaitForExit(cp, nullptr); + + std::vector<cmUVProcessChain::Status> result; + auto status = chain.GetStatus(); + std::transform( + status.begin(), status.end(), std::back_inserter(result), + [](const cmUVProcessChain::Status* s) -> cmUVProcessChain::Status { + return *s; + }); + return result; } cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR) diff --git a/Source/cmProcessTools.h b/Source/cmProcessTools.h index 74ec5e0..2bdabea 100644 --- a/Source/cmProcessTools.h +++ b/Source/cmProcessTools.h @@ -7,8 +7,10 @@ #include <cstring> #include <iosfwd> #include <string> +#include <vector> #include "cmProcessOutput.h" +#include "cmUVProcessChain.h" /** \class cmProcessTools * \brief Helper classes for process output parsing @@ -81,7 +83,7 @@ public: }; /** Run a process and send output to given parsers. */ - static void RunProcess(struct cmsysProcess_s* cp, OutputParser* out, - OutputParser* err = nullptr, - Encoding encoding = cmProcessOutput::Auto); + static std::vector<cmUVProcessChain::Status> RunProcess( + cmUVProcessChainBuilder& builder, OutputParser* out, + OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto); }; diff --git a/Source/cmString.hxx b/Source/cmString.hxx index 86b21c8..1994c2c 100644 --- a/Source/cmString.hxx +++ b/Source/cmString.hxx @@ -493,8 +493,8 @@ public: char ch) { std::string out; - out.reserve((first - this->view_.begin()) + count2 + - (this->view_.end() - last)); + out.reserve(static_cast<size_type>(first - this->view_.begin()) + count2 + + static_cast<size_type>(this->view_.end() - last)); out.append(this->view_.begin(), first); out.append(count2, ch); out.append(last, this->view_.end()); diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 2bdc928..fe421ba 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -25,12 +25,17 @@ #include <cm3p/uv.h> +#include "cm_fileno.hxx" + #include "cmDuration.h" #include "cmELF.h" #include "cmMessageMetadata.h" #include "cmProcessOutput.h" #include "cmRange.h" #include "cmStringAlgorithms.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" #include "cmValue.h" #if !defined(CMAKE_BOOTSTRAP) @@ -59,12 +64,14 @@ #include <cassert> #include <cctype> #include <cerrno> +#include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <functional> #include <iostream> +#include <memory> #include <sstream> #include <utility> #include <vector> @@ -568,85 +575,113 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, const char* dir, OutputOption outputflag, cmDuration timeout, Encoding encoding) { - std::vector<const char*> argv; - argv.reserve(command.size() + 1); - for (std::string const& cmd : command) { - argv.push_back(cmd.c_str()); - } - 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); + cmUVProcessChainBuilder builder; + builder.AddCommand(command); + if (dir) { + builder.SetWorkingDirectory(dir); } if (outputflag == OUTPUT_PASSTHROUGH) { - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1); - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); captureStdOut = nullptr; captureStdErr = nullptr; + builder + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, + cm_fileno(stdout)) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(stderr)); } else if (outputflag == OUTPUT_MERGE || (captureStdErr && captureStdErr == captureStdOut)) { - cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1); + builder.SetMergedBuiltinStreams(); captureStdErr = nullptr; + } else { + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); } assert(!captureStdErr || captureStdErr != captureStdOut); - cmsysProcess_SetTimeout(cp, timeout.count()); - cmsysProcess_Execute(cp); + auto chain = builder.Start(); + bool timedOut = false; + 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> tempStdErr; - char* data; - int length; - int pipe; + cm::uv_pipe_ptr outStream; + bool outFinished = true; + cm::uv_pipe_ptr errStream; + bool errFinished = true; cmProcessOutput processOutput(encoding); - std::string strdata; + std::unique_ptr<cmUVStreamReadHandle> outputHandle; + std::unique_ptr<cmUVStreamReadHandle> errorHandle; if (outputflag != OUTPUT_PASSTHROUGH && (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) { - while ((pipe = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) > - 0) { - // Translate NULL characters in the output into valid text. - for (int i = 0; i < length; ++i) { - if (data[i] == '\0') { - data[i] = ' '; - } - } + auto startRead = + [&outputflag, &processOutput, + &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. + for (auto& c : data) { + if (c == '\0') { + c = ' '; + } + } - if (pipe == cmsysProcess_Pipe_STDOUT) { - if (outputflag != OUTPUT_NONE) { - processOutput.DecodeText(data, length, strdata, 1); - cmSystemTools::Stdout(strdata); - } - if (captureStdOut) { - cm::append(tempStdOut, data, data + length); - } - } else if (pipe == cmsysProcess_Pipe_STDERR) { - if (outputflag != OUTPUT_NONE) { - processOutput.DecodeText(data, length, strdata, 2); - cmSystemTools::Stderr(strdata); - } - if (captureStdErr) { - cm::append(tempStdErr, data, data + length); - } - } - } + if (outputflag != OUTPUT_NONE) { + std::string strdata; + processOutput.DecodeText(data.data(), data.size(), strdata, id); + outputFunc(strdata); + } + if (captureStd) { + cm::append(tempStd, data.data(), data.data() + data.size()); + } + }, + [&finished, outputflag, &processOutput, id, outputFunc]() { + finished = true; + if (outputflag != OUTPUT_NONE) { + std::string strdata; + processOutput.DecodeText(std::string(), strdata, id); + if (!strdata.empty()) { + outputFunc(strdata); + } + } + }); + }; - if (outputflag != OUTPUT_NONE) { - processOutput.DecodeText(std::string(), strdata, 1); - if (!strdata.empty()) { - cmSystemTools::Stdout(strdata); - } - processOutput.DecodeText(std::string(), strdata, 2); - if (!strdata.empty()) { - cmSystemTools::Stderr(strdata); - } + 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); } } - cmsysProcess_WaitForExit(cp, nullptr); + while (!timedOut && !(chain.Finished() && outFinished && errFinished)) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); + } if (captureStdOut) { captureStdOut->assign(tempStdOut.begin(), tempStdOut.end()); @@ -658,37 +693,7 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, } bool result = true; - if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) { - if (retVal) { - *retVal = cmsysProcess_GetExitValue(cp); - } else { - if (cmsysProcess_GetExitValue(cp) != 0) { - result = false; - } - } - } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) { - const char* exception_str = cmsysProcess_GetExceptionString(cp); - if (outputflag != OUTPUT_NONE) { - std::cerr << exception_str << std::endl; - } - if (captureStdErr) { - captureStdErr->append(exception_str, strlen(exception_str)); - } else if (captureStdOut) { - captureStdOut->append(exception_str, strlen(exception_str)); - } - result = false; - } 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) { + if (timedOut) { const char* error_str = "Process terminated due to timeout\n"; if (outputflag != OUTPUT_NONE) { std::cerr << error_str << std::endl; @@ -697,9 +702,34 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, 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) { + *retVal = static_cast<int>(status.ExitStatus); + } else { + if (status.ExitStatus != 0) { + result = false; + } + } + break; + default: { + if (outputflag != OUTPUT_NONE) { + std::cerr << exception.second << std::endl; + } + if (captureStdErr) { + captureStdErr->append(exception.second); + } else if (captureStdOut) { + captureStdOut->append(exception.second); + } + result = false; + } break; + } } - cmsysProcess_Delete(cp); return result; } @@ -2213,9 +2243,10 @@ bool cmSystemTools::ListTar(const std::string& outFileName, #endif } -int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, - cmDuration timeout, std::vector<char>& out, - std::vector<char>& err) +cmSystemTools::WaitForLineResult cmSystemTools::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) { line.clear(); auto outiter = out.begin(); @@ -2237,7 +2268,7 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, line.append(out.data(), length); } out.erase(out.begin(), outiter + 1); - return cmsysProcess_Pipe_STDOUT; + return WaitForLineResult::STDOUT; } } @@ -2255,33 +2286,66 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, line.append(err.data(), length); } err.erase(err.begin(), erriter + 1); - return cmsysProcess_Pipe_STDERR; + return WaitForLineResult::STDERR; } } // No newlines found. Wait for more data from the process. - int length; - char* data; - double timeoutAsDbl = timeout.count(); - int pipe = - cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl); - if (pipe == cmsysProcess_Pipe_Timeout) { + struct ReadData + { + uv_stream_t* Stream; + std::vector<char> Buffer; + bool Read = false; + bool Finished = false; + }; + 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. - return pipe; + return WaitForLineResult::Timeout; } - if (pipe == cmsysProcess_Pipe_STDOUT) { - processOutput.DecodeText(data, length, strdata, 1); + if (outData.Read) { + processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(), + strdata, 1); // Append to the stdout buffer. std::vector<char>::size_type size = out.size(); cm::append(out, strdata); outiter = out.begin() + size; - } else if (pipe == cmsysProcess_Pipe_STDERR) { - processOutput.DecodeText(data, length, strdata, 2); + } else if (errData.Read) { + processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(), + strdata, 2); // Append to the stderr buffer. std::vector<char>::size_type size = err.size(); cm::append(err, strdata); erriter = err.begin() + size; - } else if (pipe == cmsysProcess_Pipe_None) { + } else if (outData.Finished && errData.Finished) { // Both stdout and stderr pipes have broken. Return leftover data. processOutput.DecodeText(std::string(), strdata, 1); if (!strdata.empty()) { @@ -2298,14 +2362,20 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, if (!out.empty()) { line.append(out.data(), outiter - out.begin()); out.erase(out.begin(), out.end()); - return cmsysProcess_Pipe_STDOUT; + return WaitForLineResult::STDOUT; } if (!err.empty()) { line.append(err.data(), erriter - err.begin()); err.erase(err.begin(), err.end()); - return cmsysProcess_Pipe_STDERR; + return WaitForLineResult::STDERR; } - return cmsysProcess_Pipe_None; + return WaitForLineResult::None; + } + if (!outData.Finished) { + uv_read_stop(outPipe); + } + if (!errData.Finished) { + uv_read_stop(errPipe); } } } diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 9563fd6..7e33e58 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -18,7 +18,8 @@ #include <cm/optional> #include <cm/string_view> -#include "cmsys/Process.h" +#include <cm3p/uv.h> + #include "cmsys/Status.hxx" // IWYU pragma: export #include "cmsys/SystemTools.hxx" // IWYU pragma: export @@ -339,10 +340,20 @@ public: */ static void ReportLastSystemError(const char* m); - /** a general output handler for cmsysProcess */ - static int WaitForLine(cmsysProcess* process, std::string& line, - cmDuration timeout, std::vector<char>& out, - std::vector<char>& err); + enum class WaitForLineResult + { + None, + 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); static void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; } static bool GetForceUnixPaths() { return s_ForceUnixPaths; } diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 2ac9d8e..94f4d38 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -11,6 +11,8 @@ #include <cm3p/uv.h> #include <fcntl.h> +#include "cm_fileno.hxx" + #include "cmCommandLineArgument.h" #include "cmConsoleBuf.h" #include "cmCryptoHash.h" @@ -72,7 +74,6 @@ #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" #include "cmsys/Terminal.h" @@ -295,14 +296,8 @@ int CLCompileAndDependencies(const std::vector<std::string>& args) } } - std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp( - 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()); + cmUVProcessChainBuilder builder; + builder.AddCommand(command).SetWorkingDirectory(currentBinaryDir); cmsys::ofstream fout(depFile.c_str()); if (!fout) { @@ -313,22 +308,18 @@ int CLCompileAndDependencies(const std::vector<std::string>& args) CLOutputLogger errLogger(std::cerr); // Start the process. - cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger); + auto result = + cmProcessTools::RunProcess(builder, &includeParser, &errLogger); + auto const& subStatus = result.front(); int status = 0; // handle status of process - switch (cmsysProcess_GetState(cp.get())) { - case cmsysProcess_State_Exited: - status = cmsysProcess_GetExitValue(cp.get()); - break; - case cmsysProcess_State_Exception: - status = 1; - break; - case cmsysProcess_State_Error: - status = 2; - break; - default: - break; + if (subStatus.SpawnResult != 0) { + status = 2; + } else if (subStatus.TermSignal != 0) { + status = 1; + } else { + status = static_cast<int>(subStatus.ExitStatus); } if (status != 0) { @@ -1116,7 +1107,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, int ret = 0; auto time_start = std::chrono::steady_clock::now(); - cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret); + cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret, nullptr, + cmSystemTools::OUTPUT_PASSTHROUGH); auto time_finish = std::chrono::steady_clock::now(); std::chrono::duration<double> time_elapsed = time_finish - time_start; @@ -1892,21 +1884,6 @@ 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. cmsys::ifstream fin(args[2].c_str()); if (!fin) { @@ -1924,9 +1901,24 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args) 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. - const char* cmd[2] = { command.c_str(), nullptr }; - cmsysProcess_SetCommand(cp, cmd); + std::vector<std::string> args2; +#ifdef _WIN32 + 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. if (verbose) { @@ -1934,35 +1926,29 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args) } // Run the command and wait for it to exit. - cmsysProcess_Execute(cp); - cmsysProcess_WaitForExit(cp, nullptr); + auto chain = builder.Start(); + chain.Wait(); // Report failure if any. - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { - int value = cmsysProcess_GetExitValue(cp); - if (value != 0) { - result = value; + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + if (status.ExitStatus != 0) { + 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; - case cmsysProcess_State_Error: - std::cerr << "Error running link command: " - << cmsysProcess_GetErrorString(cp) << std::endl; + case cmUVProcessChain::ExceptionCode::Spawn: + std::cerr << "Error running link command: " << exception.second; result = 2; break; default: + std::cerr << "Error running link command: " << exception.second; + result = 1; break; } } - // Free the process instance. - cmsysProcess_Delete(cp); - // Return the final resulting return value. return result; } diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx b/Tests/CMakeLib/testUVProcessChainHelper.cxx index fcc45b0..b53cac4 100644 --- a/Tests/CMakeLib/testUVProcessChainHelper.cxx +++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx @@ -7,10 +7,6 @@ #include <string> #include <thread> -#ifdef _WIN32 -# include <windows.h> -#endif - #include "cmSystemTools.h" static std::string getStdin() diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index d9fa0e1..7a71c0a 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -2765,29 +2765,6 @@ if(BUILD_TESTING) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${CTestUpdateP4_DIR}") endif() - configure_file( - "${CMake_SOURCE_DIR}/Tests/CTestTestFailure/testNoBuild.cmake.in" - "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoBuild.cmake" - @ONLY ESCAPE_QUOTES) - add_test(CTestTestNoBuild ${CMAKE_CTEST_COMMAND} - -S "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoBuild.cmake" -V - --output-log "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testOut1.log" - ) - set_tests_properties(CTestTestNoBuild PROPERTIES - FAIL_REGULAR_EXPRESSION "Error" WILL_FAIL true) - - configure_file( - "${CMake_SOURCE_DIR}/Tests/CTestTestFailure/testNoExe.cmake.in" - "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoExe.cmake" - @ONLY ESCAPE_QUOTES) - add_test(CTestTestNoExe ${CMAKE_CTEST_COMMAND} - -S "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoExe.cmake" -V - --output-log "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testOut2.log" - ) - set_tests_properties(CTestTestNoExe PROPERTIES DEPENDS CTestTestNoBuild - PASS_REGULAR_EXPRESSION "Could not find executable" - FAIL_REGULAR_EXPRESSION "SegFault") - if(NOT CMake_TEST_NO_NETWORK) configure_file( "${CMake_SOURCE_DIR}/Tests/CTestTestUpload/test.cmake.in" diff --git a/Tests/CTestTestFailure/CMakeLists.txt b/Tests/CTestTestFailure/CMakeLists.txt deleted file mode 100644 index b6c1e7a..0000000 --- a/Tests/CTestTestFailure/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -cmake_minimum_required (VERSION 3.5) -project(CTestTestFailure) -include(CTest) - -add_executable (NoBuild badCode.cxx) -target_link_libraries (NoBuild ${EXTRA_LIBS}) - -add_test (TestNoExe NoBuild) diff --git a/Tests/CTestTestFailure/CTestConfig.cmake b/Tests/CTestTestFailure/CTestConfig.cmake deleted file mode 100644 index 5bc1e9e..0000000 --- a/Tests/CTestTestFailure/CTestConfig.cmake +++ /dev/null @@ -1,4 +0,0 @@ -set (CTEST_NIGHTLY_START_TIME "21:00:00 EDT") -set(CTEST_DROP_METHOD "http") -set(CTEST_DROP_SITE "open.cdash.org") -set(CTEST_DROP_LOCATION "/submit.php?project=PublicDashboard") diff --git a/Tests/CTestTestFailure/testNoBuild.cmake.in b/Tests/CTestTestFailure/testNoBuild.cmake.in deleted file mode 100644 index 505916e..0000000 --- a/Tests/CTestTestFailure/testNoBuild.cmake.in +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -# Settings: -set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") -set(CTEST_SITE "@SITE@") -set(CTEST_BUILD_NAME "CTestTest-@BUILDNAME@-NoBuild") - -set(CTEST_SOURCE_DIRECTORY "@CMake_SOURCE_DIR@/Tests/CTestTestFailure") -set(CTEST_BINARY_DIRECTORY "@CMake_BINARY_DIR@/Tests/CTestTestFailure") -set(CTEST_CVS_COMMAND "@CVSCOMMAND@") -set(CTEST_CMAKE_GENERATOR "@CMAKE_GENERATOR@") -set(CTEST_CMAKE_GENERATOR_PLATFORM "@CMAKE_GENERATOR_PLATFORM@") -set(CTEST_CMAKE_GENERATOR_TOOLSET "@CMAKE_GENERATOR_TOOLSET@") -set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}") -set(CTEST_COVERAGE_COMMAND "@COVERAGE_COMMAND@") -set(CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") - -#CTEST_EMPTY_BINARY_DIRECTORY(${CTEST_BINARY_DIRECTORY}) - -CTEST_START(Experimental) -#CTEST_UPDATE(SOURCE "${CTEST_SOURCE_DIRECTORY}" RETURN_VALUE res) -CTEST_CONFIGURE(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res) -CTEST_BUILD(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res) diff --git a/Tests/CTestTestFailure/testNoExe.cmake.in b/Tests/CTestTestFailure/testNoExe.cmake.in deleted file mode 100644 index e3d7742..0000000 --- a/Tests/CTestTestFailure/testNoExe.cmake.in +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -# Settings: -set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") -set(CTEST_SITE "@SITE@") -set(CTEST_BUILD_NAME "CTestTest-@BUILDNAME@-NoExe") - -set(CTEST_SOURCE_DIRECTORY "@CMake_SOURCE_DIR@/Tests/CTestTestFailure") -set(CTEST_BINARY_DIRECTORY "@CMake_BINARY_DIR@/Tests/CTestTestFailure") -set(CTEST_CVS_COMMAND "@CVSCOMMAND@") -set(CTEST_CMAKE_GENERATOR "@CMAKE_GENERATOR@") -set(CTEST_CMAKE_GENERATOR_PLATFORM "@CMAKE_GENERATOR_PLATFORM@") -set(CTEST_CMAKE_GENERATOR_TOOLSET "@CMAKE_GENERATOR_TOOLSET@") -set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}") -set(CTEST_COVERAGE_COMMAND "@COVERAGE_COMMAND@") -set(CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") - -#CTEST_EMPTY_BINARY_DIRECTORY(${CTEST_BINARY_DIRECTORY}) - -CTEST_START(Experimental) -CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res) diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index a4f6141..5e212bd 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -466,6 +466,7 @@ add_RunCMake_test(build_command) add_executable(exit_code exit_code.c) set(execute_process_ARGS -DEXIT_CODE_EXE=$<TARGET_FILE:exit_code> + -DPRINT_STDIN_EXE=$<TARGET_FILE:print_stdin> -DPython_EXECUTABLE=${Python_EXECUTABLE} ) if(NOT CMake_TEST_EXTERNAL_CMAKE) diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake index c90d543..223a61c 100644 --- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake @@ -472,7 +472,7 @@ add_test(test1 \"${CMAKE_COMMAND}\" -E false) add_test(test2 \"${CMAKE_COMMAND}\" -E echo \"hello world\") add_test(test3 \"${CMAKE_COMMAND}\" -E true) set_tests_properties(test3 PROPERTIES DISABLED \"ON\") -add_test(test4 \"${CMAKE_COMMAND}/doesnt_exist\") +add_test(test4 \"${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist\") add_test(test5 \"${CMAKE_COMMAND}\" -E echo \"please skip\") set_tests_properties(test5 PROPERTIES SKIP_REGULAR_EXPRESSION \"please skip\") ") diff --git a/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt b/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt index ce30dc8..c57c378 100644 --- a/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt @@ -1 +1,2 @@ -Unable to find executable: .*doesnt_exist +Unable to find executable:[^ +]*does_not_exist diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt deleted file mode 100644 index 539e5ef..0000000 --- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt +++ /dev/null @@ -1,3 +0,0 @@ --- ZOT_LIBRARIES='zot' --- ZOT_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib/prefix-zot-suffix' --- ZOT_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib;-lzot' diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-stdout.txt b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-stdout.txt new file mode 100644 index 0000000..012458d --- /dev/null +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-stdout.txt @@ -0,0 +1,4 @@ +-- ZOT_LIBRARIES='zot' +-- ZOT_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-build/root/lib/prefix-zot-suffix' +-- ZOT_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-build/root/lib;-lzot' +-- ZOT_CFLAGS='-I[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-build/root/include' diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH.cmake index 1278c49..e58cefb 100644 --- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH.cmake @@ -3,16 +3,19 @@ find_package(PkgConfig REQUIRED) set(ROOT "${CMAKE_CURRENT_BINARY_DIR}/root") string(REPLACE " " "\\ " ESCAPED_ROOT "${ROOT}") set(LIB_DIR "${ROOT}/lib") +set(INCLUDE_DIR "${ROOT}/include") set(PKGCONFIG_DIR "${LIB_DIR}/pkgconfig") file(WRITE "${PKGCONFIG_DIR}/zot.pc" " prefix=${ESCAPED_ROOT} libdir=\${prefix}/lib +includedir=\${prefix}/include Name: Zot Description: Dummy package to test LIBRARY_DIR support Version: 1.0 Libs: -L\${libdir} -lzot +Cflags: -I\${includedir} ") # Create a "library" file to find in libdir. @@ -22,9 +25,13 @@ file(WRITE "${LIB_DIR}/prefix-zot-suffix") # 'pkg-config --libs' drops -L flags in PKG_CONFIG_SYSTEM_LIBRARY_PATH by default. set(ENV{PKG_CONFIG_SYSTEM_LIBRARY_PATH} "${LIB_DIR}") +# 'pkg-config --cflags' drops -I flags in PKG_CONFIG_SYSTEM_INCLUDE_PATH by default. +set(ENV{PKG_CONFIG_SYSTEM_INCLUDE_PATH} "${INCLUDE_DIR}") # 'pkgconf --libs' also drops -L flags in LIBRARY_PATH by default. set(ENV{LIBRARY_PATH} "${LIB_DIR}") +# 'pkgconf --cflags' also drops -I flags in CPATH by default. +set(ENV{CPATH} "${INCLUDE_DIR}") set(ENV{PKG_CONFIG_PATH} "${PKGCONFIG_DIR}") pkg_check_modules(ZOT REQUIRED zot) @@ -32,3 +39,4 @@ pkg_check_modules(ZOT REQUIRED zot) message(STATUS "ZOT_LIBRARIES='${ZOT_LIBRARIES}'") message(STATUS "ZOT_LINK_LIBRARIES='${ZOT_LINK_LIBRARIES}'") message(STATUS "ZOT_LDFLAGS='${ZOT_LDFLAGS}'") +message(STATUS "ZOT_CFLAGS='${ZOT_CFLAGS}'") diff --git a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake index 6b8e884..2bfe028 100644 --- a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake @@ -59,6 +59,6 @@ Libs: -L\${libdir} run_cmake(FindPkgConfig_GET_MATCHING_MODULE_NAME) run_cmake(FindPkgConfig_empty_target) if(NOT PKG_CONFIG_DONT_SUPPORT_SPACES_IN_PATH) - run_cmake(FindPkgConfig_LIBRARY_PATH) + run_cmake(FindPkgConfig_SYSTEM_PATH) endif() endif () diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-result.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-stderr.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-stderr.txt new file mode 100644 index 0000000..61188b6 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-stderr.txt @@ -0,0 +1,10 @@ +CMake Error at CMakeLists.txt:[0-9]+ \(project\): + Generator + + Xcode + + toolset specification field + + buildsystem=1 + + is not allowed with Xcode [0-9.]+\.$ diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1.cmake b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1.cmake new file mode 100644 index 0000000..2fc38e5 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake index a742391..71cc2d4 100644 --- a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake @@ -89,7 +89,11 @@ elseif("${RunCMake_GENERATOR}" STREQUAL "Xcode") set(RunCMake_GENERATOR_TOOLSET "Test Toolset") run_cmake(TestToolsetXcodeBuildSystemDefault12) set(RunCMake_GENERATOR_TOOLSET "Test Toolset,buildsystem=1") - run_cmake(TestToolsetXcodeBuildSystem1) + if(XCODE_VERSION VERSION_GREATER_EQUAL 14) + run_cmake(BadToolsetXcodeBuildSystem1) + else() + run_cmake(TestToolsetXcodeBuildSystem1) + endif() set(RunCMake_GENERATOR_TOOLSET "Test Toolset,buildsystem=12") run_cmake(TestToolsetXcodeBuildSystem12) else() diff --git a/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake index b3ab624..abb357b 100644 --- a/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake @@ -311,9 +311,11 @@ if (XCODE_VERSION VERSION_GREATER_EQUAL 7.3) endfunction() if(XCODE_VERSION VERSION_GREATER_EQUAL 12) - xctest_add_bundle_test(Darwin macosx "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") xctest_add_bundle_test(Darwin macosx "12" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") - xctest_add_bundle_test(iOS iphonesimulator "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") + if(XCODE_VERSION VERSION_LESS 14) + xctest_add_bundle_test(Darwin macosx "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") + xctest_add_bundle_test(iOS iphonesimulator "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") + endif() if (XCODE_VERSION VERSION_LESS 12.5) xctest_add_bundle_test(iOS iphonesimulator "12" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>") else() diff --git a/Tests/CTestTestFailure/badCode.cxx b/Tests/RunCMake/ctest_build/BuildFailure.cxx index 8102883..8102883 100644 --- a/Tests/CTestTestFailure/badCode.cxx +++ b/Tests/RunCMake/ctest_build/BuildFailure.cxx diff --git a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake index 12525f2..af56ead 100644 --- a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake @@ -13,18 +13,18 @@ endfunction() run_ctest_build(BuildQuiet QUIET) run_ctest_build(ParallelLevel PARALLEL_LEVEL 1) -function(run_BuildFailure) - set(CASE_CMAKELISTS_SUFFIX_CODE [[ -add_custom_target(BuildFailure ALL COMMAND command-does-not-exist) -]]) +block() + set(LANG CXX) + configure_file("${RunCMake_SOURCE_DIR}/BuildFailure.cxx" "${RunCMake_BINARY_DIR}/BuildFailure/BuildFailure.cxx" COPYONLY) + set(CASE_CMAKELISTS_SUFFIX_CODE [=[ + add_executable(BuildFailure BuildFailure.cxx) + ]=]) set(CASE_CMAKELISTS_PREFIX_CODE [[ if(NOT CTEST_USE_LAUNCHERS) message(FATAL_ERROR "CTEST_USE_LAUNCHERS not set") endif() ]]) - set(CASE_TEST_PREFIX_CODE [[ -cmake_policy(SET CMP0061 NEW) -]]) + set(CASE_TEST_PREFIX_CODE "") set(CASE_TEST_SUFFIX_CODE [[ if (ctest_build_return_value) message("ctest_build returned non-zero") @@ -35,13 +35,16 @@ endif() run_ctest(BuildFailure) if (RunCMake_GENERATOR MATCHES "Makefiles") + set(LANG NONE) set(CASE_TEST_PREFIX_CODE [[ cmake_policy(VERSION 3.2) ]]) + set(CASE_CMAKELISTS_SUFFIX_CODE [[ +add_custom_target(BuildFailure ALL COMMAND command-does-not-exist) +]]) run_ctest(BuildFailure-CMP0061-OLD) endif() -endfunction() -run_BuildFailure() +endblock() function(run_BuildChangeId) set(CASE_TEST_PREFIX_CODE [[ diff --git a/Tests/RunCMake/ctest_test/NotRun-result.txt b/Tests/RunCMake/ctest_test/NotRun-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_test/NotRun-result.txt @@ -0,0 +1 @@ +(-1|255) diff --git a/Tests/RunCMake/ctest_test/NotRun-stderr.txt b/Tests/RunCMake/ctest_test/NotRun-stderr.txt new file mode 100644 index 0000000..85907f3 --- /dev/null +++ b/Tests/RunCMake/ctest_test/NotRun-stderr.txt @@ -0,0 +1,2 @@ +.*Unable to find executable[^ +]*does_not_exist diff --git a/Tests/RunCMake/ctest_test/NotRun-stdout.txt b/Tests/RunCMake/ctest_test/NotRun-stdout.txt new file mode 100644 index 0000000..8d60833 --- /dev/null +++ b/Tests/RunCMake/ctest_test/NotRun-stdout.txt @@ -0,0 +1,7 @@ +.*Could not find executable[^ +]*does_not_exist +.* +50% tests passed, 1 tests failed out of 2 +.* +The following tests FAILED: +.*testNotRun \(Not Run\) diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake index 242a059..d2f3da3 100644 --- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake @@ -53,6 +53,13 @@ unset(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING}) unset(CASE_CTEST_TEST_LOAD) unset(RunCTest_VERBOSE_FLAG) +block() + set(CASE_CMAKELISTS_SUFFIX_CODE [[ + add_test(NAME testNotRun COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist) + ]]) + run_ctest_test(NotRun) +endblock() + function(run_TestChangeId) set(CASE_TEST_PREFIX_CODE [[ set(CTEST_CHANGE_ID "<>1") @@ -131,8 +138,7 @@ run_TestRepeat(AfterTimeout RETURN_VALUE:0 REPEAT AFTER_TIMEOUT:3) # test repeat and not run tests interact correctly set(CASE_CMAKELISTS_SUFFIX_CODE [[ -add_test(NAME testNotRun - COMMAND ${CMAKE_COMMAND}/doesnt_exist) + add_test(NAME testNotRun COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist) set_property(TEST testNotRun PROPERTY TIMEOUT 5) ]]) run_TestRepeat(NotRun RETURN_VALUE:1 REPEAT UNTIL_PASS:3) diff --git a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt index a69932d..85907f3 100644 --- a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt +++ b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt @@ -1 +1,2 @@ -.*Unable to find executable.* +.*Unable to find executable[^ +]*does_not_exist diff --git a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt index 72c98bc..8d60833 100644 --- a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt @@ -1,5 +1,7 @@ +.*Could not find executable[^ +]*does_not_exist .* 50% tests passed, 1 tests failed out of 2 .* The following tests FAILED: -.*testNotRun.*Not Run.* +.*testNotRun \(Not Run\) diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake index c2f9144..1f89829 100644 --- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake +++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake @@ -34,6 +34,7 @@ run_cmake_command(AnyCommandGood ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/AnyC run_cmake_command(LastCommandError ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandError.cmake) run_cmake_command(LastCommandTimeout ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandTimeout.cmake) run_cmake_command(LastCommandGood ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandGood.cmake) +run_cmake_command(Stdin ${CMAKE_COMMAND} -DPRINT_STDIN_EXE=${PRINT_STDIN_EXE} -P ${RunCMake_SOURCE_DIR}/Stdin.cmake) if(UNIX AND Python_EXECUTABLE) run_cmake_command(AnyCommandAbnormalExit ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/AnyCommandAbnormalExit.cmake) diff --git a/Tests/RunCMake/execute_process/Stdin-stdin.txt b/Tests/RunCMake/execute_process/Stdin-stdin.txt new file mode 100644 index 0000000..cd08755 --- /dev/null +++ b/Tests/RunCMake/execute_process/Stdin-stdin.txt @@ -0,0 +1 @@ +Hello world! diff --git a/Tests/RunCMake/execute_process/Stdin-stdout.txt b/Tests/RunCMake/execute_process/Stdin-stdout.txt new file mode 100644 index 0000000..04bd136 --- /dev/null +++ b/Tests/RunCMake/execute_process/Stdin-stdout.txt @@ -0,0 +1 @@ +^Hello world!$ diff --git a/Tests/RunCMake/execute_process/Stdin.cmake b/Tests/RunCMake/execute_process/Stdin.cmake new file mode 100644 index 0000000..e8a2098 --- /dev/null +++ b/Tests/RunCMake/execute_process/Stdin.cmake @@ -0,0 +1 @@ +execute_process(COMMAND ${PRINT_STDIN_EXE}) diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp index e45970d..6056030 100644 --- a/Utilities/IWYU/mapping.imp +++ b/Utilities/IWYU/mapping.imp @@ -75,6 +75,10 @@ { include: [ "<ostream>", public, "\"cmsys/FStream.hxx\"", public ] }, { include: [ "<fstream>", public, "\"cmsys/FStream.hxx\"", public ] }, + { symbol: [ "mode_t", private, "\"cm_sys_stat.h\"", public ] }, + { symbol: [ "S_IWUSR", private, "\"cm_sys_stat.h\"", public ] }, + { symbol: [ "S_IWGRP", private, "\"cm_sys_stat.h\"", public ] }, + { include: [ "<filesystem>", public, "<cm/filesystem>", public ] }, { include: [ "<optional>", public, "<cm/optional>", public ] }, { include: [ "<shared_mutex>", public, "<cm/shared_mutex>", public ] }, @@ -508,6 +508,7 @@ CMAKE_CXX_SOURCES="\ cmake \ cmakemain \ cmcmd \ + cm_fileno \ " if ${cmake_system_mingw}; then |