diff options
author | Marc Chevrier <marc.chevrier@gmail.com> | 2020-10-18 14:11:27 (GMT) |
---|---|---|
committer | Marc Chevrier <marc.chevrier@gmail.com> | 2020-11-29 14:25:42 (GMT) |
commit | 2c71d051facad13b0a42a57066be2489d5fff6ea (patch) | |
tree | d07f850f8abc74d9df34ad885681c08d36c14472 | |
parent | afd0f6785dc1220a07743d31699fcd9097cca46a (diff) | |
download | CMake-2c71d051facad13b0a42a57066be2489d5fff6ea.zip CMake-2c71d051facad13b0a42a57066be2489d5fff6ea.tar.gz CMake-2c71d051facad13b0a42a57066be2489d5fff6ea.tar.bz2 |
Makefiles Generators: use compiler for dependencies generation
Each source compilation generates a dependencies file. These dependencies
files are consolidated in one file per target. This consolidation is done
as part of command 'cmake -E cmake_depends` launched before evaluation of
makefile dependency graph.
The consolidation uses the same approach as `CMake` dependencies management.
Fixes: #21321
52 files changed, 1312 insertions, 270 deletions
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index f0c9d99..28cd101 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -387,6 +387,7 @@ Variables that Control the Build /variable/CMAKE_DEFAULT_BUILD_TYPE /variable/CMAKE_DEFAULT_CONFIGS /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS + /variable/CMAKE_DEPENDS_USE_COMPILER /variable/CMAKE_ENABLE_EXPORTS /variable/CMAKE_EXE_LINKER_FLAGS /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG diff --git a/Help/release/dev/makefiles-dependencies-use-compiler.rst b/Help/release/dev/makefiles-dependencies-use-compiler.rst new file mode 100644 index 0000000..8170cda --- /dev/null +++ b/Help/release/dev/makefiles-dependencies-use-compiler.rst @@ -0,0 +1,5 @@ +makefiles-dependencies-use-compiler +----------------------------------- + +* The :ref:`Makefile Generators` gained the capability, for a selection of + compilers, to use the compiler itself to generate implicit dependencies. diff --git a/Help/variable/CMAKE_DEPENDS_USE_COMPILER.rst b/Help/variable/CMAKE_DEPENDS_USE_COMPILER.rst new file mode 100644 index 0000000..bdad59e --- /dev/null +++ b/Help/variable/CMAKE_DEPENDS_USE_COMPILER.rst @@ -0,0 +1,9 @@ +CMAKE_DEPENDS_USE_COMPILER +-------------------------- + +.. versionadded:: 3.20 + +For the :ref:`Makefile Generators`, source dependencies are now, for a +selection of compilers, generated by the compiler itself. By defining this +variable with value ``FALSE``, you can restore the legacy behavior (i.e. using +``CMake`` for dependencies discovery). diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index a982d2c..33ac07e 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -152,7 +152,10 @@ function(CMAKE_DETERMINE_COMPILER_ID lang flagvar src) set(CMAKE_EXECUTABLE_FORMAT "Unknown" CACHE INTERNAL "Executable file format") endif() - if(CMAKE_GENERATOR MATCHES "^Ninja" AND MSVC_${lang}_ARCHITECTURE_ID) + if((CMAKE_GENERATOR MATCHES "^Ninja" + OR ((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake")) + AND MSVC_${lang}_ARCHITECTURE_ID) foreach(userflags "${CMAKE_${lang}_COMPILER_ID_FLAGS_LIST}" "") CMAKE_DETERMINE_MSVC_SHOWINCLUDES_PREFIX(${lang} "${userflags}") endforeach() diff --git a/Modules/Compiler/AppleClang-C.cmake b/Modules/Compiler/AppleClang-C.cmake index 2794f52..02d10d3 100644 --- a/Modules/Compiler/AppleClang-C.cmake +++ b/Modules/Compiler/AppleClang-C.cmake @@ -1,6 +1,18 @@ include(Compiler/Clang) __compiler_clang(C) + +if(NOT "x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles" + AND CMAKE_DEPFILE_FLAGS_C) + # dependencies are computed by the compiler itself + set(CMAKE_C_DEPFILE_FORMAT gcc) + set(CMAKE_C_DEPENDS_USE_COMPILER TRUE) + endif() +endif() + + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.0) set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90") set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90") diff --git a/Modules/Compiler/AppleClang-CXX.cmake b/Modules/Compiler/AppleClang-CXX.cmake index 15edc21..5282077 100644 --- a/Modules/Compiler/AppleClang-CXX.cmake +++ b/Modules/Compiler/AppleClang-CXX.cmake @@ -2,6 +2,14 @@ include(Compiler/Clang) __compiler_clang(CXX) if(NOT "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles" + AND CMAKE_DEPFILE_FLAGS_CXX) + # dependencies are computed by the compiler itself + set(CMAKE_CXX_DEPFILE_FORMAT gcc) + set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE) + endif() + set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden") endif() diff --git a/Modules/Compiler/AppleClang-OBJC.cmake b/Modules/Compiler/AppleClang-OBJC.cmake index d1f3706..d4eab4f 100644 --- a/Modules/Compiler/AppleClang-OBJC.cmake +++ b/Modules/Compiler/AppleClang-OBJC.cmake @@ -1,5 +1,14 @@ include(Compiler/Clang-OBJC) +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles" + AND CMAKE_DEPFILE_FLAGS_OBJC) + # dependencies are computed by the compiler itself + set(CMAKE_OBJC_DEPFILE_FORMAT gcc) + set(CMAKE_OBJC_DEPENDS_USE_COMPILER TRUE) +endif() + + if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 4.0) set(CMAKE_OBJC90_STANDARD_COMPILE_OPTION "-std=c90") set(CMAKE_OBJC90_EXTENSION_COMPILE_OPTION "-std=gnu90") diff --git a/Modules/Compiler/AppleClang-OBJCXX.cmake b/Modules/Compiler/AppleClang-OBJCXX.cmake index 409bd4a..172a343 100644 --- a/Modules/Compiler/AppleClang-OBJCXX.cmake +++ b/Modules/Compiler/AppleClang-OBJCXX.cmake @@ -1,5 +1,15 @@ include(Compiler/Clang-OBJCXX) + +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles" + AND CMAKE_DEPFILE_FLAGS_OBJCXX) + # dependencies are computed by the compiler itself + set(CMAKE_OBJCXX_DEPFILE_FORMAT gcc) + set(CMAKE_OBJCXX_DEPENDS_USE_COMPILER TRUE) +endif() + + set(CMAKE_OBJCXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden") if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 4.0) diff --git a/Modules/Compiler/Clang-C.cmake b/Modules/Compiler/Clang-C.cmake index 7c4a263..a631ac0 100644 --- a/Modules/Compiler/Clang-C.cmake +++ b/Modules/Compiler/Clang-C.cmake @@ -8,6 +8,19 @@ endif() if("x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC") set(CMAKE_C_CLANG_TIDY_DRIVER_MODE "cl") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_C) + set(CMAKE_C_DEPENDS_USE_COMPILER TRUE) + endif() +elseif("x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_C) + # dependencies are computed by the compiler itself + set(CMAKE_C_DEPFILE_FORMAT gcc) + set(CMAKE_C_DEPENDS_USE_COMPILER TRUE) + endif() endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.4) diff --git a/Modules/Compiler/Clang-CUDA.cmake b/Modules/Compiler/Clang-CUDA.cmake index b6155bd..cafc7dd 100644 --- a/Modules/Compiler/Clang-CUDA.cmake +++ b/Modules/Compiler/Clang-CUDA.cmake @@ -3,6 +3,12 @@ __compiler_clang(CUDA) # Set explicitly, because __compiler_clang() doesn't set this if we're simulating MSVC. set(CMAKE_DEPFILE_FLAGS_CUDA "-MD -MT <DEP_TARGET> -MF <DEP_FILE>") +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake") + # dependencies are computed by the compiler itself + set(CMAKE_CUDA_DEPFILE_FORMAT gcc) + set(CMAKE_CUDA_DEPENDS_USE_COMPILER TRUE) +endif() # C++03 isn't supported for CXX, but is for CUDA, so we need to set these manually. # Do this before __compiler_clang_cxx_standards() since that adds the feature. diff --git a/Modules/Compiler/Clang-CXX.cmake b/Modules/Compiler/Clang-CXX.cmake index 789e991..430a91c 100644 --- a/Modules/Compiler/Clang-CXX.cmake +++ b/Modules/Compiler/Clang-CXX.cmake @@ -3,6 +3,14 @@ __compiler_clang(CXX) __compiler_clang_cxx_standards(CXX) if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_CXX) + # dependencies are computed by the compiler itself + set(CMAKE_CXX_DEPFILE_FORMAT gcc) + set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE) + endif() + set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden") endif() @@ -13,4 +21,9 @@ endif() if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC") set(CMAKE_CXX_CLANG_TIDY_DRIVER_MODE "cl") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles" + AND CMAKE_DEPFILE_FLAGS_CXX) + set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE) + endif() endif() diff --git a/Modules/Compiler/Clang-OBJC.cmake b/Modules/Compiler/Clang-OBJC.cmake index c61c497..19179e3 100644 --- a/Modules/Compiler/Clang-OBJC.cmake +++ b/Modules/Compiler/Clang-OBJC.cmake @@ -1,6 +1,15 @@ include(Compiler/Clang) __compiler_clang(OBJC) +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_OBJC) + # dependencies are computed by the compiler itself + set(CMAKE_OBJC_DEPFILE_FORMAT gcc) + set(CMAKE_OBJC_DEPENDS_USE_COMPILER TRUE) +endif() + + if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 3.4) set(CMAKE_OBJC90_STANDARD_COMPILE_OPTION "-std=c90") set(CMAKE_OBJC90_EXTENSION_COMPILE_OPTION "-std=gnu90") diff --git a/Modules/Compiler/Clang-OBJCXX.cmake b/Modules/Compiler/Clang-OBJCXX.cmake index 453b5fd..9bdff66 100644 --- a/Modules/Compiler/Clang-OBJCXX.cmake +++ b/Modules/Compiler/Clang-OBJCXX.cmake @@ -1,3 +1,11 @@ include(Compiler/Clang) __compiler_clang(OBJCXX) __compiler_clang_cxx_standards(OBJCXX) + +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_OBJCXX) + # dependencies are computed by the compiler itself + set(CMAKE_OBJCXX_DEPFILE_FORMAT gcc) + set(CMAKE_OBJCXX_DEPENDS_USE_COMPILER TRUE) +endif() diff --git a/Modules/Compiler/GNU-C.cmake b/Modules/Compiler/GNU-C.cmake index ca286b3..1e6b61d 100644 --- a/Modules/Compiler/GNU-C.cmake +++ b/Modules/Compiler/GNU-C.cmake @@ -1,6 +1,16 @@ include(Compiler/GNU) __compiler_gnu(C) + +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_C) + # dependencies are computed by the compiler itself + set(CMAKE_C_DEPFILE_FORMAT gcc) + set(CMAKE_C_DEPENDS_USE_COMPILER TRUE) +endif() + + if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5) set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90") set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90") diff --git a/Modules/Compiler/GNU-CXX.cmake b/Modules/Compiler/GNU-CXX.cmake index fcaaeab..24be2d7 100644 --- a/Modules/Compiler/GNU-CXX.cmake +++ b/Modules/Compiler/GNU-CXX.cmake @@ -1,6 +1,16 @@ include(Compiler/GNU) __compiler_gnu(CXX) + +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_CXX) + # dependencies are computed by the compiler itself + set(CMAKE_CXX_DEPFILE_FORMAT gcc) + set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE) +endif() + + if (WIN32) if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fno-keep-inline-dllexport") diff --git a/Modules/Compiler/GNU-OBJC.cmake b/Modules/Compiler/GNU-OBJC.cmake index fb9b0b2..7eeed83 100644 --- a/Modules/Compiler/GNU-OBJC.cmake +++ b/Modules/Compiler/GNU-OBJC.cmake @@ -1,2 +1,11 @@ include(Compiler/GNU) __compiler_gnu(OBJC) + + +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_OBJC) + # dependencies are computed by the compiler itself + set(CMAKE_OBJC_DEPFILE_FORMAT gcc) + set(CMAKE_OBJC_DEPENDS_USE_COMPILER TRUE) +endif() diff --git a/Modules/Compiler/GNU-OBJCXX.cmake b/Modules/Compiler/GNU-OBJCXX.cmake index 06f0244..1047b5d 100644 --- a/Modules/Compiler/GNU-OBJCXX.cmake +++ b/Modules/Compiler/GNU-OBJCXX.cmake @@ -1,6 +1,15 @@ include(Compiler/GNU) __compiler_gnu(OBJCXX) +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_OBJCXX) + # dependencies are computed by the compiler itself + set(CMAKE_OBJCXX_DEPFILE_FORMAT gcc) + set(CMAKE_OBJCXX_DEPENDS_USE_COMPILER TRUE) +endif() + + if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 4.2) set(CMAKE_OBJCXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden") endif() diff --git a/Modules/Compiler/Intel-C.cmake b/Modules/Compiler/Intel-C.cmake index 0a3ab54..a7c71bd 100644 --- a/Modules/Compiler/Intel-C.cmake +++ b/Modules/Compiler/Intel-C.cmake @@ -6,6 +6,12 @@ string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " -DNDEBUG") string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO_INIT " -DNDEBUG") set(CMAKE_DEPFILE_FLAGS_C "-MD -MT <DEP_TARGET> -MF <DEP_FILE>") +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake") + # dependencies are computed by the compiler itself + set(CMAKE_C_DEPFILE_FORMAT gcc) + set(CMAKE_C_DEPENDS_USE_COMPILER TRUE) +endif() if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC") diff --git a/Modules/Compiler/Intel-CXX.cmake b/Modules/Compiler/Intel-CXX.cmake index 76d0aec..dd9681e 100644 --- a/Modules/Compiler/Intel-CXX.cmake +++ b/Modules/Compiler/Intel-CXX.cmake @@ -6,6 +6,12 @@ string(APPEND CMAKE_CXX_FLAGS_RELEASE_INIT " -DNDEBUG") string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT " -DNDEBUG") set(CMAKE_DEPFILE_FLAGS_CXX "-MD -MT <DEP_TARGET> -MF <DEP_FILE>") +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake") + # dependencies are computed by the compiler itself + set(CMAKE_CXX_DEPFILE_FORMAT gcc) + set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE) +endif() if("x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC") diff --git a/Modules/Compiler/Intel-ISPC.cmake b/Modules/Compiler/Intel-ISPC.cmake index 66b26d7..e4d5c97 100644 --- a/Modules/Compiler/Intel-ISPC.cmake +++ b/Modules/Compiler/Intel-ISPC.cmake @@ -4,6 +4,12 @@ include(Compiler/CMakeCommonCompilerMacros) #set(CMAKE_ISPC_VERBOSE_FLAG ) set(CMAKE_DEPFILE_FLAGS_ISPC "-M -MT <DEP_TARGET> -MF <DEP_FILE>") +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake") + # dependencies are computed by the compiler itself + set(CMAKE_ISPC_DEPFILE_FORMAT gcc) + set(CMAKE_ISPC_DEPENDS_USE_COMPILER TRUE) +endif() string(APPEND CMAKE_ISPC_FLAGS_INIT " ") string(APPEND CMAKE_ISPC_FLAGS_DEBUG_INIT "-O0 -g") diff --git a/Modules/Compiler/NVIDIA-CUDA.cmake b/Modules/Compiler/NVIDIA-CUDA.cmake index 464c208..3e8d4eb 100644 --- a/Modules/Compiler/NVIDIA-CUDA.cmake +++ b/Modules/Compiler/NVIDIA-CUDA.cmake @@ -30,6 +30,14 @@ if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89) # to get header dependency information set(CMAKE_DEPFILE_FLAGS_CUDA "-MD -MT <DEP_TARGET> -MF <DEP_FILE>") endif() +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake") + if (NOT CMAKE_DEPFILE_FLAGS_CUDA) + set(CMAKE_CUDA_DEPENDS_EXTRA_COMMANDS "<CMAKE_CUDA_COMPILER> ${_CMAKE_CUDA_EXTRA_FLAGS} <DEFINES> <INCLUDES> <FLAGS> ${_CMAKE_COMPILE_AS_CUDA_FLAG} -M <SOURCE> -MT <OBJECT> -o <DEP_FILE>") + endif() + set(CMAKE_CUDA_DEPFILE_FORMAT gcc) + set(CMAKE_CUDA_DEPENDS_USE_COMPILER TRUE) +endif() if(NOT "x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC") set(CMAKE_CUDA_COMPILE_OPTIONS_PIE -Xcompiler=-fPIE) diff --git a/Modules/Platform/Windows-Clang-C.cmake b/Modules/Platform/Windows-Clang-C.cmake index d007105..322e3fb 100644 --- a/Modules/Platform/Windows-Clang-C.cmake +++ b/Modules/Platform/Windows-Clang-C.cmake @@ -1,2 +1,18 @@ include(Platform/Windows-Clang) __windows_compiler_clang(C) + +if("x${MAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_C) + set(CMAKE_C_DEPENDS_USE_COMPILER TRUE) + endif() +elseif("x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_C) + # dependencies are computed by the compiler itself + set(CMAKE_C_DEPFILE_FORMAT gcc) + set(CMAKE_C_DEPENDS_USE_COMPILER TRUE) + endif() +endif() diff --git a/Modules/Platform/Windows-Clang-CXX.cmake b/Modules/Platform/Windows-Clang-CXX.cmake index f1d40f2..b4aaf1e 100644 --- a/Modules/Platform/Windows-Clang-CXX.cmake +++ b/Modules/Platform/Windows-Clang-CXX.cmake @@ -1,3 +1,19 @@ include(Platform/Windows-Clang) set(_COMPILE_CXX_MSVC " -TP") __windows_compiler_clang(CXX) + +if("x${MAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_CXX) + set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE) + endif() +elseif("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU") + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_CXX) + # dependencies are computed by the compiler itself + set(CMAKE_CXX_DEPFILE_FORMAT gcc) + set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE) + endif() +endif() diff --git a/Modules/Platform/Windows-Intel-C.cmake b/Modules/Platform/Windows-Intel-C.cmake index f3aa851..3cbc7a5 100644 --- a/Modules/Platform/Windows-Intel-C.cmake +++ b/Modules/Platform/Windows-Intel-C.cmake @@ -2,3 +2,10 @@ include(Platform/Windows-Intel) __windows_compiler_intel(C) set(CMAKE_NINJA_DEPTYPE_C intel) # special value handled by CMake set(CMAKE_DEPFILE_FLAGS_C "-QMMD -QMT <DEP_TARGET> -QMF <DEP_FILE>") + +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake") + # dependencies are computed by the compiler itself + set(CMAKE_C_DEPFILE_FORMAT gcc) + set(CMAKE_C_DEPENDS_USE_COMPILER TRUE) +endif() diff --git a/Modules/Platform/Windows-Intel-CXX.cmake b/Modules/Platform/Windows-Intel-CXX.cmake index f0b15b1..6539f64 100644 --- a/Modules/Platform/Windows-Intel-CXX.cmake +++ b/Modules/Platform/Windows-Intel-CXX.cmake @@ -3,3 +3,10 @@ set(_COMPILE_CXX " /TP") __windows_compiler_intel(CXX) set(CMAKE_NINJA_DEPTYPE_CXX intel) # special value handled by CMake set(CMAKE_DEPFILE_FLAGS_CXX "-QMMD -QMT <DEP_TARGET> -QMF <DEP_FILE>") + +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake") + # dependencies are computed by the compiler itself + set(CMAKE_CXX_DEPFILE_FORMAT gcc) + set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE) +endif() diff --git a/Modules/Platform/Windows-MSVC-C.cmake b/Modules/Platform/Windows-MSVC-C.cmake index cbe1586..67b6827 100644 --- a/Modules/Platform/Windows-MSVC-C.cmake +++ b/Modules/Platform/Windows-MSVC-C.cmake @@ -3,3 +3,10 @@ if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 18.0) set(_FS_C " /FS") endif() __windows_compiler_msvc(C) + +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_C) + # dependencies are computed by the compiler itself + set(CMAKE_C_DEPENDS_USE_COMPILER TRUE) +endif() diff --git a/Modules/Platform/Windows-MSVC-CXX.cmake b/Modules/Platform/Windows-MSVC-CXX.cmake index 0e85005..6fea617 100644 --- a/Modules/Platform/Windows-MSVC-CXX.cmake +++ b/Modules/Platform/Windows-MSVC-CXX.cmake @@ -4,3 +4,10 @@ if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0) set(_FS_CXX " /FS") endif() __windows_compiler_msvc(CXX) + +if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake" + AND CMAKE_DEPFILE_FLAGS_CXX) + # dependencies are computed by the compiler itself + set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE) +endif() diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index 3f65475..ec5758c 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake @@ -437,6 +437,13 @@ macro(__windows_compiler_msvc lang) set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON) set(CMAKE_NINJA_DEPTYPE_${lang} msvc) __windows_compiler_msvc_enable_rc("${_PLATFORM_DEFINES} ${_PLATFORM_DEFINES_${lang}}") + + # define generic information about compiler dependencies + # activation is done on per language platform configuration basis + if (MSVC_VERSION GREATER 1300) + set(CMAKE_DEPFILE_FLAGS_${lang} "/showIncludes") + set(CMAKE_${lang}_DEPFILE_FORMAT msvc) + endif() endmacro() macro(__windows_compiler_msvc_enable_rc flags) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index d1616ad..8d022eb 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -224,6 +224,8 @@ set(SRCS cmDependsJava.h cmDependsJavaParserHelper.cxx cmDependsJavaParserHelper.h + cmDependsCompiler.cxx + cmDependsCompiler.h cmDocumentation.cxx cmDocumentationFormatter.cxx cmDocumentationSection.cxx diff --git a/Source/cmDependsCompiler.cxx b/Source/cmDependsCompiler.cxx new file mode 100644 index 0000000..eb0f1d5 --- /dev/null +++ b/Source/cmDependsCompiler.cxx @@ -0,0 +1,267 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmDependsCompiler.h" + +#include <algorithm> +#include <map> +#include <string> +#include <unordered_set> +#include <utility> + +#include <cm/string_view> +#include <cm/vector> +#include <cmext/string_view> + +#include "cmsys/FStream.hxx" + +#include "cmFileTime.h" +#include "cmGlobalUnixMakefileGenerator3.h" +#include "cmLocalUnixMakefileGenerator3.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +namespace { +std::string& ReplaceAll(std::string& data, const std::string& toSearch, + const std::string& replaceStr) +{ + // Get the first occurrence + auto pos = data.find(toSearch); + // Repeat until the end is reached + while (pos != std::string::npos) { + // Replace this occurrence of Sub String + data.replace(pos, toSearch.size(), replaceStr); + // Get the next occurrence from the current position + pos = data.find(toSearch, pos + replaceStr.size()); + } + + return data; +} + +std::string& NormalizePath(std::string& item) +{ + ReplaceAll(item, "$$", "$"); + ReplaceAll(item, "\\ ", " "); + ReplaceAll(item, "\\#", "#"); + ReplaceAll(item, "\\", "/"); + + return item; +} + +void ParseLine(const std::string& line, std::vector<std::string>& depends) +{ + auto start = line.find_first_not_of(' '); + if (start == std::string::npos || line[start] == '#') { + return; + } + + auto index = start; + while ((index = line.find(' ', index)) != std::string::npos) { + if (line[index - 1] == '\\') { + index += 1; + continue; + } + + auto item = line.substr(start, index - start); + if (item.back() != ':') { + // check that ':' is not present after some spaces + auto index2 = line.find_first_not_of(' ', index + 1); + if (index2 == std::string::npos || line[index2] != ':') { + // this is a dependency, add it + depends.emplace_back(std::move(NormalizePath(item))); + } else { + index = index2; + } + } + + start = line.find_first_not_of(' ', index + 1); + index = start; + } + if (start != std::string::npos) { + auto item = line.substr(start); + if (line.back() != ':') { + // this is a dependency, add it + depends.emplace_back(std::move(NormalizePath(item))); + } + } +} +} + +bool cmDependsCompiler::CheckDependencies( + const std::string& internalDepFile, const std::vector<std::string>& depFiles, + cmDepends::DependencyMap& dependencies, + const std::function<bool(const std::string&)>& isValidPath) +{ + bool status = true; + bool forceReadDeps = true; + + cmFileTime internalDepFileTime; + // read cached dependencies stored in internal file + if (cmSystemTools::FileExists(internalDepFile)) { + internalDepFileTime.Load(internalDepFile); + forceReadDeps = false; + + // read current dependencies + cmsys::ifstream fin(internalDepFile.c_str()); + if (fin) { + std::string line; + std::string depender; + std::vector<std::string>* currentDependencies = nullptr; + while (std::getline(fin, line)) { + if (line.empty() || line.front() == '#') { + continue; + } + // Drop carriage return character at the end + if (line.back() == '\r') { + line.pop_back(); + if (line.empty()) { + continue; + } + } + // Check if this a depender line + if (line.front() != ' ') { + depender = std::move(line); + currentDependencies = &dependencies[depender]; + continue; + } + // This is a dependee line + if (currentDependencies != nullptr) { + currentDependencies->emplace_back(line.substr(1)); + } + } + fin.close(); + } + } + + // Now, update dependencies map with all new compiler generated + // dependencies files + cmFileTime depFileTime; + for (auto dep = depFiles.begin(); dep != depFiles.end(); dep++) { + const auto& source = *dep++; + const auto& target = *dep++; + const auto& format = *dep++; + const auto& depFile = *dep; + + if (!cmSystemTools::FileExists(depFile)) { + continue; + } + + if (!forceReadDeps) { + depFileTime.Load(depFile); + } + if (forceReadDeps || depFileTime.Newer(internalDepFileTime)) { + status = false; + if (this->Verbose) { + cmSystemTools::Stdout(cmStrCat("Dependencies file \"", depFile, + "\" is newer than depends file \"", + internalDepFile, "\".\n")); + } + cmsys::ifstream fin(depFile.c_str()); + if (!fin) { + continue; + } + + std::vector<std::string> depends; + std::string line; + if (format == "msvc"_s) { + if (!isValidPath) { + // insert source as first dependency + depends.push_back(source); + } + while (cmSystemTools::GetLineFromStream(fin, line)) { + depends.emplace_back(std::move(line)); + } + } else { + while (cmSystemTools::GetLineFromStream(fin, line)) { + if (line.empty()) { + continue; + } + if (line.back() == '\\') { + line.pop_back(); + } + ParseLine(line, depends); + } + + // depending of the effective format of the dependencies file generated + // by the compiler, the target can be wrongly identified as a + // dependency so remove it from the list + if (depends.front() == target) { + depends.erase(depends.begin()); + } + + if (isValidPath) { + // remove first dependency because it must not be filtered out + depends.erase(depends.begin()); + } + } + + if (isValidPath) { + cm::erase_if(depends, isValidPath); + // insert source as first dependency + depends.insert(depends.begin(), source); + } + + dependencies[target] = std::move(depends); + } + } + + return status; +} + +void cmDependsCompiler::WriteDependencies( + const cmDepends::DependencyMap& dependencies, std::ostream& makeDepends, + std::ostream& internalDepends) +{ + // dependencies file consumed by make tool + const auto& lineContinue = static_cast<cmGlobalUnixMakefileGenerator3*>( + this->LocalGenerator->GetGlobalGenerator()) + ->LineContinueDirective; + const auto& binDir = this->LocalGenerator->GetBinaryDirectory(); + cmDepends::DependencyMap makeDependencies(dependencies); + std::unordered_set<cm::string_view> phonyTargets; + + // external dependencies file + for (auto& node : makeDependencies) { + auto& deps = node.second; + std::transform( + deps.cbegin(), deps.cend(), deps.begin(), + [this, &binDir](const std::string& dep) { + return LocalGenerator->ConvertToMakefilePath( + this->LocalGenerator->MaybeConvertToRelativePath(binDir, dep)); + }); + + makeDepends << this->LocalGenerator->ConvertToMakefilePath(node.first) + << ": " << deps.front(); + // first dependency is the source, remove it because should not be declared + // as phony target + deps.erase(deps.begin()); + for (const auto& dep : deps) { + makeDepends << ' ' << lineContinue << " " << dep; + phonyTargets.emplace(dep.data(), dep.length()); + } + makeDepends << std::endl << std::endl; + } + + // add phony targets + for (const auto& target : phonyTargets) { + makeDepends << std::endl << target << ':' << std::endl; + } + + // internal dependencies file + for (const auto& node : dependencies) { + internalDepends << node.first << std::endl; + for (const auto& dep : node.second) { + internalDepends << ' ' << dep << std::endl; + } + internalDepends << std::endl; + } +} + +void cmDependsCompiler::ClearDependencies( + const std::vector<std::string>& depFiles) +{ + for (auto dep = depFiles.begin(); dep != depFiles.end(); dep++) { + dep += 3; + cmSystemTools::RemoveFile(*dep); + } +} diff --git a/Source/cmDependsCompiler.h b/Source/cmDependsCompiler.h new file mode 100644 index 0000000..838156d --- /dev/null +++ b/Source/cmDependsCompiler.h @@ -0,0 +1,60 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <functional> +#include <iosfwd> +#include <string> +#include <vector> + +#include "cmDepends.h" + +class cmLocalUnixMakefileGenerator3; + +/** \class cmDepends + * \brief Dependencies files manager. + * + * This class is responsible for maintaining a compiler_depends.make file in + * the build tree corresponding to an object file. + */ +class cmDependsCompiler +{ +public: + cmDependsCompiler() = default; + ~cmDependsCompiler() = default; + + /** should this be verbose in its output */ + void SetVerbose(bool verb) { this->Verbose = verb; } + + /** Set the local generator for the directory in which we are + scanning dependencies. This is not a full local generator; it + has been setup to do relative path conversions for the current + directory. */ + void SetLocalGenerator(cmLocalUnixMakefileGenerator3* lg) + { + this->LocalGenerator = lg; + } + + /** Read dependencies for the target file. Return true if + dependencies didn't changed and false if not. + Up-to-date Dependencies will be stored in deps. */ + bool CheckDependencies( + const std::string& internalDepFile, + const std::vector<std::string>& depFiles, + cmDepends::DependencyMap& dependencies, + const std::function<bool(const std::string&)>& isValidPath); + + /** Write dependencies for the target file. */ + void WriteDependencies(const cmDepends::DependencyMap& dependencies, + std::ostream& makeDepends, + std::ostream& internalDepends); + + /** Clear dependencies for the target so they will be regenerated. */ + void ClearDependencies(const std::vector<std::string>& depFiles); + +private: + bool Verbose = false; + cmLocalUnixMakefileGenerator3* LocalGenerator = nullptr; +}; diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h index 5d12831..6459771 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.h +++ b/Source/cmGlobalUnixMakefileGenerator3.h @@ -127,6 +127,12 @@ public: void WriteConvenienceRules(std::ostream& ruleFileStream, std::set<std::string>& emitted); + // Make tool supports dependency files generated by compiler + bool SupportsCompilerDependencies() + { + return this->ToolSupportsCompilerDependencies; + } + /** Get the command to use for a target that has no rule. This is used for multiple output dependencies and for cmake_force. */ std::string GetEmptyRuleHackCommand() { return this->EmptyRuleHackCommand; } @@ -219,6 +225,10 @@ protected: bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const override { return true; } + // Specify if the make tool is able to consume dependency files + // generated by the compiler + bool ToolSupportsCompilerDependencies = true; + // Some make programs (Borland) do not keep a rule if there are no // dependencies or commands. This is a problem for creating rules // that might not do anything but might have other dependencies diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 3654e8f..08cefb7 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -5,6 +5,7 @@ #include <algorithm> #include <cassert> #include <cstdio> +#include <functional> #include <sstream> #include <utility> @@ -19,6 +20,7 @@ #include "cmCMakePath.h" #include "cmCustomCommand.h" // IWYU pragma: keep #include "cmCustomCommandGenerator.h" +#include "cmDependsCompiler.h" #include "cmFileTimeCache.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" @@ -52,8 +54,9 @@ # include "cmDependsJava.h" #endif +namespace { // Helper function used below. -static std::string cmSplitExtension(std::string const& in, std::string& base) +std::string cmSplitExtension(std::string const& in, std::string& base) { std::string ext; std::string::size_type dot_pos = in.rfind('.'); @@ -67,6 +70,43 @@ static std::string cmSplitExtension(std::string const& in, std::string& base) return ext; } +// Helper predicate for removing absolute paths that don't point to the +// source or binary directory. It is used when CMAKE_DEPENDS_IN_PROJECT_ONLY +// is set ON, to only consider in-project dependencies during the build. +class NotInProjectDir +{ +public: + // Constructor with the source and binary directory's path + NotInProjectDir(cm::string_view sourceDir, cm::string_view binaryDir) + : SourceDir(sourceDir) + , BinaryDir(binaryDir) + { + } + + // Operator evaluating the predicate + bool operator()(const std::string& p) const + { + auto path = cmCMakePath(p).Normal(); + + // Keep all relative paths: + if (path.IsRelative()) { + return false; + } + + // If it's an absolute path, check if it starts with the source + // directory: + return !(cmCMakePath(SourceDir).IsPrefix(path) || + cmCMakePath(BinaryDir).IsPrefix(path)); + } + +private: + // The path to the source directory + cm::string_view SourceDir; + // The path to the binary directory + cm::string_view BinaryDir; +}; +} + cmLocalUnixMakefileGenerator3::cmLocalUnixMakefileGenerator3( cmGlobalGenerator* gg, cmMakefile* mf) : cmLocalCommonGenerator(gg, mf, mf->GetCurrentBinaryDirectory()) @@ -554,8 +594,10 @@ void cmLocalUnixMakefileGenerator3::WriteMakeRule( } } - // Write the list of commands. - os << cmWrap("\t", commands, "", "\n") << "\n"; + if (!commands.empty()) { + // Write the list of commands. + os << cmWrap("\t", commands, "", "\n") << "\n"; + } if (symbolic && !this->IsWatcomWMake()) { os << ".PHONY : " << tgt << "\n"; } @@ -1300,91 +1342,153 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( cmSystemTools::Error("Target DependInfo.cmake file not found"); } + bool status = true; + // Check if any multiple output pairs have a missing file. this->CheckMultipleOutputs(verbose); std::string const targetDir = cmSystemTools::GetFilenamePath(tgtInfo); - std::string const internalDependFile = targetDir + "/depend.internal"; - std::string const dependFile = targetDir + "/depend.make"; - - // If the target DependInfo.cmake file has changed since the last - // time dependencies were scanned then force rescanning. This may - // happen when a new source file is added and CMake regenerates the - // project but no other sources were touched. - bool needRescanDependInfo = false; - cmFileTimeCache* ftc = - this->GlobalGenerator->GetCMakeInstance()->GetFileTimeCache(); - { - int result; - if (!ftc->Compare(internalDependFile, tgtInfo, &result) || result < 0) { - if (verbose) { - cmSystemTools::Stdout(cmStrCat("Dependee \"", tgtInfo, - "\" is newer than depender \"", - internalDependFile, "\".\n")); + if (!this->Makefile->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES").empty()) { + // dependencies are managed by CMake itself + + std::string const internalDependFile = targetDir + "/depend.internal"; + std::string const dependFile = targetDir + "/depend.make"; + + // If the target DependInfo.cmake file has changed since the last + // time dependencies were scanned then force rescanning. This may + // happen when a new source file is added and CMake regenerates the + // project but no other sources were touched. + bool needRescanDependInfo = false; + cmFileTimeCache* ftc = + this->GlobalGenerator->GetCMakeInstance()->GetFileTimeCache(); + { + int result; + if (!ftc->Compare(internalDependFile, tgtInfo, &result) || result < 0) { + if (verbose) { + cmSystemTools::Stdout(cmStrCat("Dependee \"", tgtInfo, + "\" is newer than depender \"", + internalDependFile, "\".\n")); + } + needRescanDependInfo = true; } - needRescanDependInfo = true; } - } - // If the directory information is newer than depend.internal, include dirs - // may have changed. In this case discard all old dependencies. - bool needRescanDirInfo = false; - { - std::string dirInfoFile = - cmStrCat(this->GetCurrentBinaryDirectory(), - "/CMakeFiles/CMakeDirectoryInformation.cmake"); - int result; - if (!ftc->Compare(internalDependFile, dirInfoFile, &result) || - result < 0) { - if (verbose) { - cmSystemTools::Stdout(cmStrCat("Dependee \"", dirInfoFile, - "\" is newer than depender \"", - internalDependFile, "\".\n")); + // If the directory information is newer than depend.internal, include + // dirs may have changed. In this case discard all old dependencies. + bool needRescanDirInfo = false; + { + std::string dirInfoFile = + cmStrCat(this->GetCurrentBinaryDirectory(), + "/CMakeFiles/CMakeDirectoryInformation.cmake"); + int result; + if (!ftc->Compare(internalDependFile, dirInfoFile, &result) || + result < 0) { + if (verbose) { + cmSystemTools::Stdout(cmStrCat("Dependee \"", dirInfoFile, + "\" is newer than depender \"", + internalDependFile, "\".\n")); + } + needRescanDirInfo = true; } - needRescanDirInfo = true; + } + + // Check the implicit dependencies to see if they are up to date. + // The build.make file may have explicit dependencies for the object + // files but these will not affect the scanning process so they need + // not be considered. + cmDepends::DependencyMap validDependencies; + bool needRescanDependencies = false; + if (!needRescanDirInfo) { + cmDependsC checker; + checker.SetVerbose(verbose); + checker.SetFileTimeCache(ftc); + // cmDependsC::Check() fills the vector validDependencies() with the + // dependencies for those files where they are still valid, i.e. + // neither the files themselves nor any files they depend on have + // changed. We don't do that if the CMakeDirectoryInformation.cmake + // file has changed, because then potentially all dependencies have + // changed. This information is given later on to cmDependsC, which + // then only rescans the files where it did not get valid dependencies + // via this dependency vector. This means that in the normal case, when + // only few or one file have been edited, then also only this one file + // is actually scanned again, instead of all files for this target. + needRescanDependencies = + !checker.Check(dependFile, internalDependFile, validDependencies); + } + + if (needRescanDependInfo || needRescanDirInfo || needRescanDependencies) { + // The dependencies must be regenerated. + std::string targetName = cmSystemTools::GetFilenameName(targetDir); + targetName = targetName.substr(0, targetName.length() - 4); + std::string message = + cmStrCat("Scanning dependencies of target ", targetName); + cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, color); + + status = this->ScanDependencies(targetDir, dependFile, + internalDependFile, validDependencies); } } - // Check the implicit dependencies to see if they are up to date. - // The build.make file may have explicit dependencies for the object - // files but these will not affect the scanning process so they need - // not be considered. - cmDepends::DependencyMap validDependencies; - bool needRescanDependencies = false; - if (!needRescanDirInfo) { - cmDependsC checker; - checker.SetVerbose(verbose); - checker.SetFileTimeCache(ftc); - // cmDependsC::Check() fills the vector validDependencies() with the - // dependencies for those files where they are still valid, i.e. neither - // the files themselves nor any files they depend on have changed. - // We don't do that if the CMakeDirectoryInformation.cmake file has - // changed, because then potentially all dependencies have changed. - // This information is given later on to cmDependsC, which then only - // rescans the files where it did not get valid dependencies via this - // dependency vector. This means that in the normal case, when only - // few or one file have been edited, then also only this one file is - // actually scanned again, instead of all files for this target. - needRescanDependencies = - !checker.Check(dependFile, internalDependFile, validDependencies); - } - - if (needRescanDependInfo || needRescanDirInfo || needRescanDependencies) { - // The dependencies must be regenerated. - std::string targetName = cmSystemTools::GetFilenameName(targetDir); - targetName = targetName.substr(0, targetName.length() - 4); - std::string message = - cmStrCat("Scanning dependencies of target ", targetName); - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, color); - - return this->ScanDependencies(targetDir, dependFile, internalDependFile, - validDependencies); + auto depends = + this->Makefile->GetSafeDefinition("CMAKE_DEPENDS_DEPENDENCY_FILES"); + if (!depends.empty()) { + // dependencies are managed by compiler + auto depFiles = cmExpandedList(depends); + std::string const internalDepFile = + targetDir + "/compiler_depend.internal"; + std::string const depFile = targetDir + "/compiler_depend.make"; + cmDepends::DependencyMap dependencies; + cmDependsCompiler depsManager; + bool projectOnly = cmIsOn( + this->Makefile->GetSafeDefinition("CMAKE_DEPENDS_IN_PROJECT_ONLY")); + + depsManager.SetVerbose(verbose); + depsManager.SetLocalGenerator(this); + + if (!depsManager.CheckDependencies( + internalDepFile, depFiles, dependencies, + projectOnly ? NotInProjectDir(this->GetSourceDirectory(), + this->GetBinaryDirectory()) + : std::function<bool(const std::string&)>())) { + // regenerate dependencies files + std::string targetName = + cmCMakePath(targetDir).GetFileName().RemoveExtension().GenericString(); + auto message = cmStrCat( + "Consolidate compiler generated dependencies of target ", targetName); + cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, color); + + // Open the make depends file. This should be copy-if-different + // because the make tool may try to reload it needlessly otherwise. + cmGeneratedFileStream ruleFileStream( + depFile, false, this->GlobalGenerator->GetMakefileEncoding()); + ruleFileStream.SetCopyIfDifferent(true); + if (!ruleFileStream) { + return false; + } + + // Open the cmake dependency tracking file. This should not be + // copy-if-different because dependencies are re-scanned when it is + // older than the DependInfo.cmake. + cmGeneratedFileStream internalRuleFileStream( + internalDepFile, false, this->GlobalGenerator->GetMakefileEncoding()); + if (!internalRuleFileStream) { + return false; + } + + this->WriteDisclaimer(ruleFileStream); + this->WriteDisclaimer(internalRuleFileStream); + + depsManager.WriteDependencies(dependencies, ruleFileStream, + internalRuleFileStream); + } } // The dependencies are already up-to-date. - return true; + return status; } bool cmLocalUnixMakefileGenerator3::ScanDependencies( @@ -1723,166 +1827,193 @@ void cmLocalUnixMakefileGenerator3::ClearDependencies(cmMakefile* mf, cmDepends clearer; clearer.SetVerbose(verbose); for (std::string const& file : files) { - std::string dir = cmSystemTools::GetFilenamePath(file); - - // Clear the implicit dependency makefile. - std::string dependFile = dir + "/depend.make"; - clearer.Clear(dependFile); + auto snapshot = mf->GetState()->CreateBaseSnapshot(); + cmMakefile lmf(mf->GetGlobalGenerator(), snapshot); + lmf.ReadListFile(file); - // Remove the internal dependency check file to force - // regeneration. - std::string internalDependFile = dir + "/depend.internal"; - cmSystemTools::RemoveFile(internalDependFile); - } -} + if (!lmf.GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES").empty()) { + std::string dir = cmSystemTools::GetFilenamePath(file); -namespace { -// Helper predicate for removing absolute paths that don't point to the -// source or binary directory. It is used when CMAKE_DEPENDS_IN_PROJECT_ONLY -// is set ON, to only consider in-project dependencies during the build. -class NotInProjectDir -{ -public: - // Constructor with the source and binary directory's path - NotInProjectDir(cm::string_view sourceDir, cm::string_view binaryDir) - : SourceDir(sourceDir) - , BinaryDir(binaryDir) - { - } + // Clear the implicit dependency makefile. + std::string dependFile = dir + "/depend.make"; + clearer.Clear(dependFile); - // Operator evaluating the predicate - bool operator()(const std::string& p) const - { - auto path = cmCMakePath(p).Normal(); + // Remove the internal dependency check file to force + // regeneration. + std::string internalDependFile = dir + "/depend.internal"; + cmSystemTools::RemoveFile(internalDependFile); + } - // Keep all relative paths: - if (path.IsRelative()) { - return false; + auto depsFiles = lmf.GetSafeDefinition("CMAKE_DEPENDS_DEPENDENCY_FILES"); + if (!depsFiles.empty()) { + auto dir = cmCMakePath(file).GetParentPath(); + // Clear the implicit dependency makefile. + auto depFile = cmCMakePath(dir).Append("compiler_depend.make"); + clearer.Clear(depFile.GenericString()); + + // Remove the internal dependency check file + auto internalDepFile = + cmCMakePath(dir).Append("compiler_depend.internal"); + cmSystemTools::RemoveFile(internalDepFile.GenericString()); + + // Touch timestamp file to force dependencies regeneration + auto DepTimestamp = cmCMakePath(dir).Append("compiler_depend.ts"); + cmSystemTools::Touch(DepTimestamp.GenericString(), true); + + // clear the dependencies files generated by the compiler + std::vector<std::string> dependencies = cmExpandedList(depsFiles); + cmDependsCompiler depsManager; + depsManager.SetVerbose(verbose); + depsManager.ClearDependencies(dependencies); } - // If it's an absolute path, check if it starts with the source - // directory: - return !(cmCMakePath(SourceDir).IsPrefix(path) || - cmCMakePath(BinaryDir).IsPrefix(path)); } - -private: - // The path to the source directory - cm::string_view SourceDir; - // The path to the binary directory - cm::string_view BinaryDir; -}; } void cmLocalUnixMakefileGenerator3::WriteDependLanguageInfo( std::ostream& cmakefileStream, cmGeneratorTarget* target) { - ImplicitDependLanguageMap const& implicitLangs = - this->GetImplicitDepends(target); + // To enable dependencies filtering + cmakefileStream << "\n" + << "# Consider dependencies only in project.\n" + << "set(CMAKE_DEPENDS_IN_PROJECT_ONLY " + << (cmIsOn(this->Makefile->GetSafeDefinition( + "CMAKE_DEPENDS_IN_PROJECT_ONLY")) + ? "ON" + : "OFF") + << ")\n\n"; + + auto const& implicitLangs = + this->GetImplicitDepends(target, cmDependencyScannerKind::CMake); // list the languages - cmakefileStream - << "# The set of languages for which implicit dependencies are needed:\n"; + cmakefileStream << "# The set of languages for which implicit " + "dependencies are needed:\n"; cmakefileStream << "set(CMAKE_DEPENDS_LANGUAGES\n"; for (auto const& implicitLang : implicitLangs) { cmakefileStream << " \"" << implicitLang.first << "\"\n"; } cmakefileStream << " )\n"; - // now list the files for each language - cmakefileStream - << "# The set of files for implicit dependencies of each language:\n"; - for (auto const& implicitLang : implicitLangs) { - cmakefileStream << "set(CMAKE_DEPENDS_CHECK_" << implicitLang.first - << "\n"; - ImplicitDependFileMap const& implicitPairs = implicitLang.second; - - // for each file pair - for (auto const& implicitPair : implicitPairs) { - for (auto const& di : implicitPair.second) { - cmakefileStream << " \"" << di << "\" "; - cmakefileStream << "\"" << implicitPair.first << "\"\n"; + if (!implicitLangs.empty()) { + // now list the files for each language + cmakefileStream + << "# The set of files for implicit dependencies of each language:\n"; + for (auto const& implicitLang : implicitLangs) { + const auto& lang = implicitLang.first; + + cmakefileStream << "set(CMAKE_DEPENDS_CHECK_" << lang << "\n"; + auto const& implicitPairs = implicitLang.second; + + // for each file pair + for (auto const& implicitPair : implicitPairs) { + for (auto const& di : implicitPair.second) { + cmakefileStream << " \"" << di << "\" "; + cmakefileStream << "\"" << implicitPair.first << "\"\n"; + } } - } - cmakefileStream << " )\n"; - - // Tell the dependency scanner what compiler is used. - std::string cidVar = - cmStrCat("CMAKE_", implicitLang.first, "_COMPILER_ID"); - cmProp cid = this->Makefile->GetDefinition(cidVar); - if (cmNonempty(cid)) { - cmakefileStream << "set(CMAKE_" << implicitLang.first - << "_COMPILER_ID \"" << *cid << "\")\n"; - } + cmakefileStream << " )\n"; - if (implicitLang.first == "Fortran") { - std::string smodSep = - this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP"); - std::string smodExt = - this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT"); - cmakefileStream << "set(CMAKE_Fortran_SUBMODULE_SEP \"" << smodSep - << "\")\n"; - cmakefileStream << "set(CMAKE_Fortran_SUBMODULE_EXT \"" << smodExt - << "\")\n"; - } + // Tell the dependency scanner what compiler is used. + std::string cidVar = cmStrCat("CMAKE_", lang, "_COMPILER_ID"); + cmProp cid = this->Makefile->GetDefinition(cidVar); + if (cmNonempty(cid)) { + cmakefileStream << "set(CMAKE_" << lang << "_COMPILER_ID \"" << *cid + << "\")\n"; + } - // Build a list of preprocessor definitions for the target. - std::set<std::string> defines; - this->GetTargetDefines(target, this->GetConfigName(), implicitLang.first, - defines); - if (!defines.empty()) { - /* clang-format off */ + if (lang == "Fortran") { + std::string smodSep = + this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP"); + std::string smodExt = + this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT"); + cmakefileStream << "set(CMAKE_Fortran_SUBMODULE_SEP \"" << smodSep + << "\")\n"; + cmakefileStream << "set(CMAKE_Fortran_SUBMODULE_EXT \"" << smodExt + << "\")\n"; + } + + // Build a list of preprocessor definitions for the target. + std::set<std::string> defines; + this->GetTargetDefines(target, this->GetConfigName(), lang, defines); + if (!defines.empty()) { + /* clang-format off */ cmakefileStream << "\n" << "# Preprocessor definitions for this target.\n" - << "set(CMAKE_TARGET_DEFINITIONS_" << implicitLang.first << "\n"; - /* clang-format on */ - for (std::string const& define : defines) { - cmakefileStream << " " << cmOutputConverter::EscapeForCMake(define) - << "\n"; + << "set(CMAKE_TARGET_DEFINITIONS_" << lang << "\n"; + /* clang-format on */ + for (std::string const& define : defines) { + cmakefileStream << " " << cmOutputConverter::EscapeForCMake(define) + << "\n"; + } + cmakefileStream << " )\n"; + } + + // Target-specific include directories: + cmakefileStream << "\n" + << "# The include file search paths:\n"; + cmakefileStream << "set(CMAKE_" << lang << "_TARGET_INCLUDE_PATH\n"; + std::vector<std::string> includes; + + this->GetIncludeDirectories(includes, target, lang, + this->GetConfigName()); + std::string const& binaryDir = this->GetState()->GetBinaryDirectory(); + if (this->Makefile->IsOn("CMAKE_DEPENDS_IN_PROJECT_ONLY")) { + std::string const& sourceDir = this->GetState()->GetSourceDirectory(); + cm::erase_if(includes, ::NotInProjectDir(sourceDir, binaryDir)); + } + for (std::string const& include : includes) { + cmakefileStream << " \"" + << this->MaybeConvertToRelativePath(binaryDir, include) + << "\"\n"; } cmakefileStream << " )\n"; } - // Target-specific include directories: - cmakefileStream << "\n" - << "# The include file search paths:\n"; - cmakefileStream << "set(CMAKE_" << implicitLang.first - << "_TARGET_INCLUDE_PATH\n"; - std::vector<std::string> includes; - - this->GetIncludeDirectories(includes, target, implicitLang.first, - this->GetConfigName()); - std::string const& binaryDir = this->GetState()->GetBinaryDirectory(); - if (this->Makefile->IsOn("CMAKE_DEPENDS_IN_PROJECT_ONLY")) { - std::string const& sourceDir = this->GetState()->GetSourceDirectory(); - cm::erase_if(includes, ::NotInProjectDir(sourceDir, binaryDir)); + // Store include transform rule properties. Write the directory + // rules first because they may be overridden by later target rules. + std::vector<std::string> transformRules; + if (cmProp xform = + this->Makefile->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) { + cmExpandList(*xform, transformRules); } - for (std::string const& include : includes) { - cmakefileStream << " \"" - << this->MaybeConvertToRelativePath(binaryDir, include) - << "\"\n"; + if (cmProp xform = + target->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) { + cmExpandList(*xform, transformRules); + } + if (!transformRules.empty()) { + cmakefileStream << "\nset(CMAKE_INCLUDE_TRANSFORMS\n"; + for (std::string const& tr : transformRules) { + cmakefileStream << " " << cmOutputConverter::EscapeForCMake(tr) + << "\n"; + } + cmakefileStream << " )\n"; } - cmakefileStream << " )\n"; } - // Store include transform rule properties. Write the directory - // rules first because they may be overridden by later target rules. - std::vector<std::string> transformRules; - if (cmProp xform = - this->Makefile->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) { - cmExpandList(*xform, transformRules); - } - if (cmProp xform = - target->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) { - cmExpandList(*xform, transformRules); - } - if (!transformRules.empty()) { - cmakefileStream << "set(CMAKE_INCLUDE_TRANSFORMS\n"; - for (std::string const& tr : transformRules) { - cmakefileStream << " " << cmOutputConverter::EscapeForCMake(tr) << "\n"; + auto const& compilerLangs = + this->GetImplicitDepends(target, cmDependencyScannerKind::Compiler); + + // list the dependency files managed by the compiler + cmakefileStream << "\n# The set of dependency files which are needed:\n"; + cmakefileStream << "set(CMAKE_DEPENDS_DEPENDENCY_FILES\n"; + for (auto const& compilerLang : compilerLangs) { + auto depFormat = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", compilerLang.first, "_DEPFILE_FORMAT")); + auto const& compilerPairs = compilerLang.second; + for (auto const& compilerPair : compilerPairs) { + for (auto const& src : compilerPair.second) { + cmakefileStream << " \"" << src << "\" \"" + << this->MaybeConvertToRelativePath( + this->GetBinaryDirectory(), compilerPair.first) + << "\" \"" << depFormat << "\" \"" + << this->MaybeConvertToRelativePath( + this->GetBinaryDirectory(), compilerPair.first) + << ".d\"\n"; + } } - cmakefileStream << " )\n"; } + cmakefileStream << " )\n"; } void cmLocalUnixMakefileGenerator3::WriteDisclaimer(std::ostream& os) diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 6bc915d..155a097 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -12,7 +12,9 @@ #include <utility> #include <cm/memory> +#include <cm/string_view> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmComputeLinkInformation.h" #include "cmCustomCommand.h" @@ -326,7 +328,45 @@ void cmMakefileTargetGenerator::WriteCommonCodeRules() << cmSystemTools::ConvertToOutputPath( this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetBinaryDirectory(), dependFileNameFull)) - << "\n\n"; + << "\n"; + + std::string depsUseCompiler = "CMAKE_DEPENDS_USE_COMPILER"; + if (!this->Makefile->IsDefinitionSet(depsUseCompiler) || + this->Makefile->IsOn(depsUseCompiler)) { + std::string compilerDependFile = + cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make"); + *this->BuildFileStream + << "# Include any dependencies generated by the " + "compiler for this target.\n" + << this->GlobalGenerator->IncludeDirective << " " << root + << cmSystemTools::ConvertToOutputPath( + this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetBinaryDirectory(), compilerDependFile)) + << "\n\n"; + + if (!cmSystemTools::FileExists(compilerDependFile)) { + // Write an empty dependency file. + cmGeneratedFileStream depFileStream( + compilerDependFile, false, + this->GlobalGenerator->GetMakefileEncoding()); + depFileStream << "# Empty compiler generated dependencies file for " + << this->GeneratorTarget->GetName() << ".\n" + << "# This may be replaced when dependencies are built.\n"; + } + + std::string compilerDependTimestamp = + cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts"); + if (!cmSystemTools::FileExists(compilerDependTimestamp)) { + // Write a dependency timestamp file. + cmGeneratedFileStream depFileStream( + compilerDependTimestamp, false, + this->GlobalGenerator->GetMakefileEncoding()); + depFileStream << "# CMAKE generated file: DO NOT EDIT!\n" + << "# Timestamp file for compiler generated dependencies " + "management for " + << this->GeneratorTarget->GetName() << ".\n"; + } + } if (!this->NoRuleMessages) { // Include the progress variables for the target. @@ -473,6 +513,14 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( return; } + // Use compiler to generate dependencies, if supported. + bool compilerGenerateDeps = + this->GlobalGenerator->SupportsCompilerDependencies() && + cmIsOn(this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, "_DEPENDS_USE_COMPILER"))); + auto scanner = compilerGenerateDeps ? cmDependencyScannerKind::Compiler + : cmDependencyScannerKind::CMake; + // Get the full path name of the object file. std::string const& objectName = this->GeneratorTarget->GetObjectName(&source); @@ -512,7 +560,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( std::string srcFullPath = cmSystemTools::CollapseFullPath(source.GetFullPath()); this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, lang, - objFullPath, srcFullPath); + objFullPath, srcFullPath, scanner); this->LocalGenerator->AppendRuleDepend(depends, this->FlagFileNameFull.c_str()); @@ -554,8 +602,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( depends.push_back( this->GeneratorTarget->GetPchFile(config, lang, arch)); } - this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, lang, - objFullPath, pchHeader); + this->LocalGenerator->AddImplicitDepends( + this->GeneratorTarget, lang, objFullPath, pchHeader, scanner); } } @@ -689,7 +737,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( source.GetFullPath(), cmOutputConverter::SHELL); // Construct the build message. - std::vector<std::string> no_commands; + std::vector<std::string> no_depends; std::vector<std::string> commands; // add in a progress call if needed @@ -783,6 +831,26 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( "$(" + lang + "_INCLUDES)"); vars.Includes = includesString.c_str(); + std::string dependencyTarget; + std::string shellDependencyFile; + std::string dependencyTimestamp; + if (compilerGenerateDeps) { + dependencyTarget = this->LocalGenerator->EscapeForShell( + this->LocalGenerator->ConvertToMakefilePath( + this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetBinaryDirectory(), relativeObj))); + vars.DependencyTarget = dependencyTarget.c_str(); + + auto depFile = cmStrCat(obj, ".d"); + shellDependencyFile = this->LocalGenerator->ConvertToOutputFormat( + depFile, cmOutputConverter::SHELL); + vars.DependencyFile = shellDependencyFile.c_str(); + + dependencyTimestamp = this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetBinaryDirectory(), + cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")); + } + // At the moment, it is assumed that C, C++, Fortran, and CUDA have both // assembly and preprocessor capabilities. The same is true for the // ability to export compile commands @@ -954,6 +1022,53 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( } } + std::string flagsWithDeps(flags); + + if (compilerGenerateDeps) { + // Injects dependency computation + auto depFlags = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_DEPFILE_FLAGS_", lang)); + + if (!depFlags.empty()) { + // Add dependency flags + rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, + depFlags, vars); + flagsWithDeps.append(1, ' '); + flagsWithDeps.append(depFlags); + } + vars.Flags = flagsWithDeps.c_str(); + + const auto& extraCommands = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS")); + if (!extraCommands.empty()) { + auto commandList = cmExpandedList(extraCommands); + compileCommands.insert(compileCommands.end(), commandList.cbegin(), + commandList.cend()); + } + + const auto& depFormat = this->Makefile->GetRequiredDefinition( + cmStrCat("CMAKE_", lang, "_DEPFILE_FORMAT")); + + if (depFormat == "msvc"_s) { + // compiler must be launched through a wrapper to pick-up dependencies + std::string depFilter = + "$(CMAKE_COMMAND) -E cmake_cl_compile_depends "; + depFilter += cmStrCat("--dep-file=", shellDependencyFile); + depFilter += + cmStrCat(" --working-dir=", + this->LocalGenerator->ConvertToOutputFormat( + this->LocalGenerator->GetCurrentBinaryDirectory(), + cmOutputConverter::SHELL)); + const auto& prefix = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_CL_SHOWINCLUDES_PREFIX")); + depFilter += cmStrCat(" --filter-prefix=", + this->LocalGenerator->ConvertToOutputFormat( + prefix, cmOutputConverter::SHELL)); + depFilter += " -- "; + compileCommands.front().insert(0, depFilter); + } + } + // Expand placeholders in the commands. for (std::string& compileCommand : compileCommands) { compileCommand = cmStrCat(launcher, compileCommand); @@ -979,8 +1094,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( cmExpandList(evaluated_outputs, outputs); } } - if (!ispcHeaderRelative - .empty()) { // can't move ispcHeader as vars is using it + if (!ispcHeaderRelative.empty()) { + // can't move ispcHeader as vars is using it outputs.emplace_back(ispcHeaderRelative); } @@ -988,10 +1103,19 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( this->CleanFiles.insert(outputs.begin() + 1, outputs.end()); } + if (compilerGenerateDeps) { + depends.push_back(dependencyTimestamp); + } + // Write the rule. this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends, commands); + if (compilerGenerateDeps) { + // set back flags without dependency generation + vars.Flags = flags.c_str(); + } + bool do_preprocess_rules = lang_has_preprocessor && this->LocalGenerator->GetCreatePreprocessedSourceRules(); bool do_assembly_rules = @@ -1388,10 +1512,10 @@ void cmMakefileTargetGenerator::WriteDeviceLinkRule( std::string registerFileCmd; - // The generated register file contains macros that when expanded register - // the device routines. Because the routines are the same for all - // architectures the register file will be the same too. Thus generate it - // only on the first invocation to reduce overhead. + // The generated register file contains macros that when expanded + // register the device routines. Because the routines are the same for + // all architectures the register file will be the same too. Thus + // generate it only on the first invocation to reduce overhead. if (fatbinaryDepends.size() == 1) { std::string registerFileRel = this->LocalGenerator->MaybeConvertToRelativePath( @@ -1426,7 +1550,8 @@ void cmMakefileTargetGenerator::WriteDeviceLinkRule( fatbinaryOutputRel, fatbinaryDepends, { fatbinaryCommand }, false); - // Compile the stub that registers the kernels and contains the fatbinaries. + // Compile the stub that registers the kernels and contains the + // fatbinaries. cmRulePlaceholderExpander::RuleVariables vars; vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); vars.CMTargetType = @@ -1839,9 +1964,9 @@ bool cmMakefileTargetGenerator::CheckUseResponseFileForObjects( if (size_t const limit = cmSystemTools::CalculateCommandLineLengthLimit()) { // Compute the total length of our list of object files with room // for argument separation and quoting. This does not convert paths - // relative to CMAKE_CURRENT_BINARY_DIR like the final list will be, so the - // actual list will likely be much shorter than this. However, in the - // worst case all objects will remain as absolute paths. + // relative to CMAKE_CURRENT_BINARY_DIR like the final list will be, so + // the actual list will likely be much shorter than this. However, in + // the worst case all objects will remain as absolute paths. size_t length = 0; for (std::string const& obj : this->Objects) { length += obj.size() + 3; diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 81374a1..b8464f2 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -33,6 +33,13 @@ # include "bindexplib.h" #endif +#if !defined(CMAKE_BOOTSTRAP) || defined(CMAKE_BOOTSTRAP_MAKEFILES) +# include <algorithm> + +# include "cmCMakePath.h" +# include "cmProcessTools.h" +#endif + #if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32) && !defined(__CYGWIN__) # include "cmVisualStudioWCEPlatformParser.h" #endif @@ -66,6 +73,7 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, std::vector<std::string>::const_iterator argEnd); +namespace { void CMakeCommandUsage(const char* program) { std::ostringstream errorStream; @@ -144,8 +152,7 @@ void CMakeCommandUsage(const char* program) cmSystemTools::Error(errorStream.str()); } -static bool cmTarFilesFrom(std::string const& file, - std::vector<std::string>& files) +bool cmTarFilesFrom(std::string const& file, std::vector<std::string>& files) { if (cmSystemTools::FileIsDirectory(file)) { std::ostringstream e; @@ -180,7 +187,7 @@ static bool cmTarFilesFrom(std::string const& file, return true; } -static void cmCatFile(const std::string& fileToAppend) +void cmCatFile(const std::string& fileToAppend) { #ifdef _WIN32 _setmode(fileno(stdout), _O_BINARY); @@ -190,7 +197,7 @@ static void cmCatFile(const std::string& fileToAppend) std::cout << source.rdbuf(); } -static bool cmRemoveDirectory(const std::string& dir, bool recursive = true) +bool cmRemoveDirectory(const std::string& dir, bool recursive = true) { if (cmSystemTools::FileIsSymlink(dir)) { if (!cmSystemTools::RemoveFile(dir)) { @@ -208,9 +215,123 @@ static bool cmRemoveDirectory(const std::string& dir, bool recursive = true) return true; } -static int HandleIWYU(const std::string& runCmd, - const std::string& /* sourceFile */, - const std::vector<std::string>& orig_cmd) +#if !defined(CMAKE_BOOTSTRAP) || defined(CMAKE_BOOTSTRAP_MAKEFILES) +class CLIncludeParser : public cmProcessTools::LineParser +{ +public: + CLIncludeParser(cm::string_view includePrefix, cmsys::ofstream& depFile, + std::ostream& output) + : IncludePrefix(includePrefix) + , DepFile(depFile) + , Output(output) + { + } + +private: + bool ProcessLine() override + { + if (cmHasPrefix(this->Line, this->IncludePrefix)) { + this->DepFile << cmCMakePath( + cmTrimWhitespace(this->Line.c_str() + + this->IncludePrefix.size())) + .GenericString() + << std::endl; + } else { + this->Output << this->Line << std::endl << std::flush; + } + + return true; + } + + cm::string_view IncludePrefix; + cmsys::ofstream& DepFile; + std::ostream& Output; +}; + +class CLOutputLogger : public cmProcessTools::OutputLogger +{ +public: + CLOutputLogger(std::ostream& log) + : cmProcessTools::OutputLogger(log) + { + } + + bool ProcessLine() override + { + *this->Log << std::flush; + return true; + } +}; + +int CLCompileAndDependencies(const std::vector<std::string>& args) +{ + std::string depFile; + std::string currentBinaryDir; + std::string filterPrefix; + std::vector<std::string> command; + for (auto it = args.cbegin() + 2; it != args.cend(); it++) { + if (cmHasLiteralPrefix(*it, "--dep-file=")) { + depFile = it->substr(11); + } else if (cmHasLiteralPrefix(*it, "--working-dir=")) { + currentBinaryDir = it->substr(14); + } else if (cmHasLiteralPrefix(*it, "--filter-prefix=")) { + filterPrefix = it->substr(16); + } else if (*it == "--") { + command.insert(command.begin(), ++it, args.cend()); + break; + } else { + return 1; + } + } + + 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()); + + cmsys::ofstream fout(depFile.c_str()); + if (!fout) { + return 3; + } + + CLIncludeParser includeParser(filterPrefix, fout, std::cout); + CLOutputLogger errLogger(std::cerr); + + // Start the process. + cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger); + + 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 (status != 0) { + // remove the dependencies file because potentially invalid + fout.close(); + cmSystemTools::RemoveFile(depFile); + } + + return status; +} +#endif + +int HandleIWYU(const std::string& runCmd, const std::string& /* sourceFile */, + const std::vector<std::string>& orig_cmd) { // Construct the iwyu command line by taking what was given // and adding all the arguments we give to the compiler. @@ -235,8 +356,8 @@ static int HandleIWYU(const std::string& runCmd, return 0; } -static int HandleTidy(const std::string& runCmd, const std::string& sourceFile, - const std::vector<std::string>& orig_cmd) +int HandleTidy(const std::string& runCmd, const std::string& sourceFile, + const std::vector<std::string>& orig_cmd) { // Construct the clang-tidy command line by taking what was given // and adding our compiler command line. The clang-tidy tool will @@ -265,9 +386,8 @@ static int HandleTidy(const std::string& runCmd, const std::string& sourceFile, return ret; } -static int HandleLWYU(const std::string& runCmd, - const std::string& /* sourceFile */, - const std::vector<std::string>&) +int HandleLWYU(const std::string& runCmd, const std::string& /* sourceFile */, + const std::vector<std::string>&) { // Construct the ldd -r -u (link what you use lwyu) command line // ldd -u -r lwuy target @@ -298,9 +418,8 @@ static int HandleLWYU(const std::string& runCmd, return 0; } -static int HandleCppLint(const std::string& runCmd, - const std::string& sourceFile, - const std::vector<std::string>&) +int HandleCppLint(const std::string& runCmd, const std::string& sourceFile, + const std::vector<std::string>&) { // Construct the cpplint command line. std::vector<std::string> cpplint_cmd = cmExpandedList(runCmd, true); @@ -326,9 +445,8 @@ static int HandleCppLint(const std::string& runCmd, return 0; } -static int HandleCppCheck(const std::string& runCmd, - const std::string& sourceFile, - const std::vector<std::string>& orig_cmd) +int HandleCppCheck(const std::string& runCmd, const std::string& sourceFile, + const std::vector<std::string>& orig_cmd) { // Construct the cpplint command line. std::vector<std::string> cppcheck_cmd = cmExpandedList(runCmd, true); @@ -391,7 +509,7 @@ struct CoCompiler bool NoOriginalCommand; }; -static const std::array<CoCompiler, 5> CoCompilers = { +const std::array<CoCompiler, 5> CoCompilers = { { // Table of options and handlers. { "--cppcheck=", HandleCppCheck, false }, { "--cpplint=", HandleCppLint, false }, @@ -405,6 +523,7 @@ struct CoCompileJob std::string Command; CoCompileHandler Handler; }; +} // called when args[0] == "__run_co_compile" int cmcmd::HandleCoCompileCommands(std::vector<std::string> const& args) @@ -586,7 +705,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } else if (args[2] == "--ignore-eol") { filesDiffer = cmsys::SystemTools::TextFilesDiffer(args[3], args[4]); } else { - ::CMakeCommandUsage(args[0].c_str()); + CMakeCommandUsage(args[0].c_str()); return 2; } @@ -621,8 +740,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } } if (outValid) { - // The def file already exists and all input files are older than the - // existing def file. + // The def file already exists and all input files are older than + // the existing def file. return 0; } } @@ -1162,6 +1281,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, return 1; } +#if !defined(CMAKE_BOOTSTRAP) || defined(CMAKE_BOOTSTRAP_MAKEFILES) + // Internal CMake compiler dependencies filtering + if (args[1] == "cmake_cl_compile_depends") { + return CLCompileAndDependencies(args); + } +#endif + // Internal CMake link script support. if (args[1] == "cmake_link_script" && args.size() >= 3) { return cmcmd::ExecuteLinkScript(args); @@ -1412,7 +1538,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } } - ::CMakeCommandUsage(args[0].c_str()); + CMakeCommandUsage(args[0].c_str()); return 1; } @@ -1779,8 +1905,8 @@ int cmcmd::RunLLVMRC(std::vector<std::string> const& args) skipNextArg = false; continue; } - // We use ++ as seperator between the preprocessing step definition and the - // rc compilation step becase we need to prepend a -- to seperate the + // We use ++ as seperator between the preprocessing step definition and + // the rc compilation step becase we need to prepend a -- to seperate the // source file properly from other options when using clang-cl for // preprocessing. if (arg == "++") { @@ -1830,7 +1956,8 @@ int cmcmd::RunLLVMRC(std::vector<std::string> const& args) return 1; } // Since we might have skipped the last argument to llvm-rc - // we need to make sure the llvm-rc source file is present in the commandline + // we need to make sure the llvm-rc source file is present in the + // commandline if (resource_compile.back() != intermediate_file) { resource_compile.push_back(intermediate_file); } @@ -2123,8 +2250,8 @@ int cmVSLink::LinkIncremental() // http://blogs.msdn.com/zakramer/archive/2006/05/22/603558.aspx // 1. Compiler compiles the application and generates the *.obj files. - // 2. An empty manifest file is generated if this is a clean build and if - // not the previous one is reused. + // 2. An empty manifest file is generated if this is a clean build and + // if not the previous one is reused. // 3. The resource compiler (rc.exe) compiles the *.manifest file to a // *.res file. // 4. Linker generates the binary (EXE or DLL) with the /incremental diff --git a/Tests/BuildDepends/Project/CMakeLists.txt b/Tests/BuildDepends/Project/CMakeLists.txt index c438e1d..c2576f3 100644 --- a/Tests/BuildDepends/Project/CMakeLists.txt +++ b/Tests/BuildDepends/Project/CMakeLists.txt @@ -102,6 +102,12 @@ target_link_libraries(zot zot_pch) if(NOT CMAKE_OSX_ARCHITECTURES MATCHES "[;$]") target_precompile_headers(zot_pch PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/zot_pch.hxx) endif() +if (CMAKE_CXX_DEPENDS_USE_COMPILER AND + CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.4") + # Mixing pre-compile headers and flags to generate dependencies (-M options family) + # causes the compiler to crash + set_property(TARGET zot_pch PROPERTY DISABLE_PRECOMPILE_HEADERS ON) +endif() # Test the #include line macro transformation rule support. set_property( diff --git a/Tests/IncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/CMakeLists.txt index 1f5b664..d4c19c7 100644 --- a/Tests/IncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/CMakeLists.txt @@ -67,7 +67,11 @@ else() endif() # Test escaping of special characters in include directory paths. -set(special_chars "~@%&{}()!'") +set(special_chars "~@&{}()!'") +if(NOT CMAKE_GENERATOR MATCHES "(Unix|MinGW|MSYS) Makefiles") + # when compiler is used for dependencies, special characters for make are not escaped + string(APPEND special_chars "%") +endif() if(NOT CMAKE_GENERATOR STREQUAL "Watcom WMake") # Watcom seems to have no way to encode these characters. string(APPEND special_chars "#=[]") diff --git a/Tests/RunCMake/BuildDepends/CompilerDependencies.cmake b/Tests/RunCMake/BuildDepends/CompilerDependencies.cmake new file mode 100644 index 0000000..8a9e600 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/CompilerDependencies.cmake @@ -0,0 +1,46 @@ +enable_language(C) + +add_executable(main ${CMAKE_CURRENT_BINARY_DIR}/main.c) + +file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT " +set(check_pairs + \"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/main.c\" + \"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/main.h\" + ) +set(check_exes + \"$<TARGET_FILE:main>\" + ) + +if (check_step EQUAL 2) + include(\"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Makefile.cmake\") + if (NOT CMAKE_DEPEND_INFO_FILES) + set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPEND_INFO_FILES not found.\") + else() + include(\"${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_DEPEND_INFO_FILES}\") + if (NOT CMAKE_DEPENDS_DEPENDENCY_FILES) + set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPENDS_DEPENDENCY_FILES not found.\") + else() + list(GET CMAKE_DEPENDS_DEPENDENCY_FILES 1 OBJECT_FILE) + list(GET CMAKE_DEPENDS_DEPENDENCY_FILES 3 DEP_FILE) + if (NOT EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/\${DEP_FILE}\") + set(RunCMake_TEST_FAILED \"File \${DEP_FILE} not found.\") + else() + set (TARGET_DEP_FILE \"${CMAKE_CURRENT_BINARY_DIR}/\${DEP_FILE}\") + cmake_path(REPLACE_FILENAME TARGET_DEP_FILE \"compiler_depend.make\") + file(READ \"\${TARGET_DEP_FILE}\" DEPENDS_CONTENT) + if (WIN32) + string (REPLACE \"\\\\\" \"/\" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\") + string (TOLOWER \"\${DEPENDS_CONTENT}\" DEPENDS_CONTENT) + string (TOLOWER \"\${OBJECT_FILE}\" OBJECT_FILE) + else() + string(REPLACE \"\\\\ \" \" \" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\") + endif() + if(NOT DEPENDS_CONTENT MATCHES \"\${OBJECT_FILE} *:.+main.c\" + OR NOT DEPENDS_CONTENT MATCHES \"main.h\") + set(RunCMake_TEST_FAILED \"Dependency file '\${TARGET_DEP_FILE}' badly generated.\") + endif() + endif() + endif() + endif() +endif() +") diff --git a/Tests/RunCMake/BuildDepends/CompilerDependencies.step1.cmake b/Tests/RunCMake/BuildDepends/CompilerDependencies.step1.cmake new file mode 100644 index 0000000..1da2593 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/CompilerDependencies.step1.cmake @@ -0,0 +1,9 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.h" [[ +#define COUNT 1 +]]) + +file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.c" [[ +#include "main.h" + +int main(void) { return COUNT; } +]]) diff --git a/Tests/RunCMake/BuildDepends/CompilerDependencies.step2.cmake b/Tests/RunCMake/BuildDepends/CompilerDependencies.step2.cmake new file mode 100644 index 0000000..e983665 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/CompilerDependencies.step2.cmake @@ -0,0 +1,3 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.h" [[ +#define COUNT 2 +]]) diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake index 7a68c4b..23e222a 100644 --- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake +++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake @@ -113,3 +113,13 @@ if(CMake_TEST_BuildDepends_GNU_AS) set(ENV{ASM} "${CMake_TEST_BuildDepends_GNU_AS}") run_BuildDepends(GNU-AS) endif() + +if ((RunCMake_GENERATOR STREQUAL "Unix Makefiles" + AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" + OR CMAKE_C_COMPILER_ID STREQUAL "Clang" + OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang")) + OR (RunCMake_GENERATOR STREQUAL "NMake Makefiles" + AND MSVC_VERSION GREATER 1300 + AND CMAKE_C_COMPILER_ID STREQUAL "MSVC")) + run_BuildDepends(CompilerDependencies) +endif() diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 5b0b055..f094b42 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -203,7 +203,10 @@ if(NOT DEFINED CMake_TEST_BuildDepends_GNU_AS set(CMake_TEST_BuildDepends_GNU_AS "${_gnu_as}") endif() endif() + add_RunCMake_test(BuildDepends + -DMSVC_VERSION=${MSVC_VERSION} + -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} -DCMake_TEST_BuildDepends_GNU_AS=${CMake_TEST_BuildDepends_GNU_AS} ) if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja") diff --git a/Tests/RunCMake/Make/TargetMessages-OFF-build-check.cmake b/Tests/RunCMake/Make/TargetMessages-OFF-build-check.cmake new file mode 100644 index 0000000..74a0564 --- /dev/null +++ b/Tests/RunCMake/Make/TargetMessages-OFF-build-check.cmake @@ -0,0 +1,3 @@ + +set (CHECK_TARGET_MESSAGES OFF) +include ("${CMAKE_CURRENT_LIST_DIR}/TargetMessages-validation.cmake") diff --git a/Tests/RunCMake/Make/TargetMessages-OFF-build-stdout.txt b/Tests/RunCMake/Make/TargetMessages-OFF-build-stdout.txt index 77a582a..8d98f9d 100644 --- a/Tests/RunCMake/Make/TargetMessages-OFF-build-stdout.txt +++ b/Tests/RunCMake/Make/TargetMessages-OFF-build-stdout.txt @@ -1,5 +1 @@ -^(([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]* -)*Scanning dependencies of target CustomTarget( -([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]*)*$ +.* diff --git a/Tests/RunCMake/Make/TargetMessages-ON-build-check.cmake b/Tests/RunCMake/Make/TargetMessages-ON-build-check.cmake new file mode 100644 index 0000000..afd8efb --- /dev/null +++ b/Tests/RunCMake/Make/TargetMessages-ON-build-check.cmake @@ -0,0 +1,3 @@ + +set (CHECK_TARGET_MESSAGES ON) +include ("${CMAKE_CURRENT_LIST_DIR}/TargetMessages-validation.cmake") diff --git a/Tests/RunCMake/Make/TargetMessages-ON-build-stdout.txt b/Tests/RunCMake/Make/TargetMessages-ON-build-stdout.txt index a827624..8d98f9d 100644 --- a/Tests/RunCMake/Make/TargetMessages-ON-build-stdout.txt +++ b/Tests/RunCMake/Make/TargetMessages-ON-build-stdout.txt @@ -1,8 +1 @@ -^(([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]* -)*Scanning dependencies of target CustomTarget( -([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]*)* -Built target CustomTarget( -([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]*)*$ +.* diff --git a/Tests/RunCMake/Make/TargetMessages-VAR-OFF-build-check.cmake b/Tests/RunCMake/Make/TargetMessages-VAR-OFF-build-check.cmake new file mode 100644 index 0000000..74a0564 --- /dev/null +++ b/Tests/RunCMake/Make/TargetMessages-VAR-OFF-build-check.cmake @@ -0,0 +1,3 @@ + +set (CHECK_TARGET_MESSAGES OFF) +include ("${CMAKE_CURRENT_LIST_DIR}/TargetMessages-validation.cmake") diff --git a/Tests/RunCMake/Make/TargetMessages-VAR-OFF-build-stdout.txt b/Tests/RunCMake/Make/TargetMessages-VAR-OFF-build-stdout.txt index 77a582a..8d98f9d 100644 --- a/Tests/RunCMake/Make/TargetMessages-VAR-OFF-build-stdout.txt +++ b/Tests/RunCMake/Make/TargetMessages-VAR-OFF-build-stdout.txt @@ -1,5 +1 @@ -^(([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]* -)*Scanning dependencies of target CustomTarget( -([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]*)*$ +.* diff --git a/Tests/RunCMake/Make/TargetMessages-VAR-ON-build-check.cmake b/Tests/RunCMake/Make/TargetMessages-VAR-ON-build-check.cmake new file mode 100644 index 0000000..afd8efb --- /dev/null +++ b/Tests/RunCMake/Make/TargetMessages-VAR-ON-build-check.cmake @@ -0,0 +1,3 @@ + +set (CHECK_TARGET_MESSAGES ON) +include ("${CMAKE_CURRENT_LIST_DIR}/TargetMessages-validation.cmake") diff --git a/Tests/RunCMake/Make/TargetMessages-VAR-ON-build-stdout.txt b/Tests/RunCMake/Make/TargetMessages-VAR-ON-build-stdout.txt index a827624..8d98f9d 100644 --- a/Tests/RunCMake/Make/TargetMessages-VAR-ON-build-stdout.txt +++ b/Tests/RunCMake/Make/TargetMessages-VAR-ON-build-stdout.txt @@ -1,8 +1 @@ -^(([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]* -)*Scanning dependencies of target CustomTarget( -([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]*)* -Built target CustomTarget( -([^B]|B[^u]|Bu[^i]|Bui[^l]|Buil[^t]|Built[^ ])[^ -]*)*$ +.* diff --git a/Tests/RunCMake/Make/TargetMessages-validation.cmake b/Tests/RunCMake/Make/TargetMessages-validation.cmake new file mode 100644 index 0000000..f3d7af1 --- /dev/null +++ b/Tests/RunCMake/Make/TargetMessages-validation.cmake @@ -0,0 +1,10 @@ + +if (CHECK_TARGET_MESSAGES) + if (NOT actual_stdout MATCHES "Built target CustomTarget") + set (RunCMake_TEST_FAILED "Not found expected 'Built target' message.") + endif() +else() + if (actual_stdout MATCHES "Built target CustomTarget") + set (RunCMake_TEST_FAILED "Found unexpected 'Built target' message.") + endif() +endif() @@ -1054,12 +1054,14 @@ else CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES} \ cmDepends \ cmDependsC \ + cmDependsCompiler \ cmGlobalUnixMakefileGenerator3 \ cmLocalUnixMakefileGenerator3 \ cmMakefileExecutableTargetGenerator \ cmMakefileLibraryTargetGenerator \ cmMakefileTargetGenerator \ cmMakefileUtilityTargetGenerator \ + cmProcessTools \ " JSONCPP_CXX_SOURCES= |