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
This commit is contained in:
Marc Chevrier
2020-10-18 16:11:27 +02:00
parent afd0f6785d
commit 2c71d051fa
52 changed files with 1311 additions and 269 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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).

View File

@@ -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()

View File

@@ -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")

View File

@@ -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()

View File

@@ -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")

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.

View File

@@ -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()

View File

@@ -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")

View File

@@ -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()

View File

@@ -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")

View File

@@ -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")

View File

@@ -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()

View File

@@ -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()

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -224,6 +224,8 @@ set(SRCS
cmDependsJava.h
cmDependsJavaParserHelper.cxx
cmDependsJavaParserHelper.h
cmDependsCompiler.cxx
cmDependsCompiler.h
cmDocumentation.cxx
cmDocumentationFormatter.cxx
cmDocumentationSection.cxx

View File

@@ -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);
}
}

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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 (!this->Makefile->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES").empty()) {
// dependencies are managed by CMake itself
// 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"));
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"));
}
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);
}
}
// 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"));
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;
}
needRescanDirInfo = true;
// 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);
}
}
// 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);
}
// 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);
auto snapshot = mf->GetState()->CreateBaseSnapshot();
cmMakefile lmf(mf->GetGlobalGenerator(), snapshot);
lmf.ReadListFile(file);
// Clear the implicit dependency makefile.
std::string dependFile = dir + "/depend.make";
clearer.Clear(dependFile);
if (!lmf.GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES").empty()) {
std::string dir = cmSystemTools::GetFilenamePath(file);
// Remove the internal dependency check file to force
// regeneration.
std::string internalDependFile = dir + "/depend.internal";
cmSystemTools::RemoveFile(internalDependFile);
}
}
// Clear the implicit dependency makefile.
std::string dependFile = dir + "/depend.make";
clearer.Clear(dependFile);
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)
{
}
// 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;
// Remove the internal dependency check file to force
// regeneration.
std::string internalDependFile = dir + "/depend.internal";
cmSystemTools::RemoveFile(internalDependFile);
}
// 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;
};
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);
}
}
}
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;
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;
// for each file pair
for (auto const& implicitPair : implicitPairs) {
for (auto const& di : implicitPair.second) {
cmakefileStream << " \"" << di << "\" ";
cmakefileStream << "\"" << implicitPair.first << "\"\n";
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";
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";
}
// 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";
}
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";
}
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(), implicitLang.first,
defines);
if (!defines.empty()) {
/* clang-format off */
// 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)

View File

@@ -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;

View File

@@ -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

View File

@@ -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(

View File

@@ -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 "#=[]")

View File

@@ -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()
")

View File

@@ -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; }
]])

View File

@@ -0,0 +1,3 @@
file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.h" [[
#define COUNT 2
]])

View File

@@ -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()

View File

@@ -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")

View File

@@ -0,0 +1,3 @@
set (CHECK_TARGET_MESSAGES OFF)
include ("${CMAKE_CURRENT_LIST_DIR}/TargetMessages-validation.cmake")

View File

@@ -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[^ ])[^
]*)*$
.*

View File

@@ -0,0 +1,3 @@
set (CHECK_TARGET_MESSAGES ON)
include ("${CMAKE_CURRENT_LIST_DIR}/TargetMessages-validation.cmake")

View File

@@ -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[^ ])[^
]*)*$
.*

View File

@@ -0,0 +1,3 @@
set (CHECK_TARGET_MESSAGES OFF)
include ("${CMAKE_CURRENT_LIST_DIR}/TargetMessages-validation.cmake")

View File

@@ -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[^ ])[^
]*)*$
.*

View File

@@ -0,0 +1,3 @@
set (CHECK_TARGET_MESSAGES ON)
include ("${CMAKE_CURRENT_LIST_DIR}/TargetMessages-validation.cmake")

View File

@@ -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[^ ])[^
]*)*$
.*

View File

@@ -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()

View File

@@ -1054,12 +1054,14 @@ else
CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES} \
cmDepends \
cmDependsC \
cmDependsCompiler \
cmGlobalUnixMakefileGenerator3 \
cmLocalUnixMakefileGenerator3 \
cmMakefileExecutableTargetGenerator \
cmMakefileLibraryTargetGenerator \
cmMakefileTargetGenerator \
cmMakefileUtilityTargetGenerator \
cmProcessTools \
"
JSONCPP_CXX_SOURCES=