diff options
author | Marc Chevrier <marc.chevrier@gmail.com> | 2023-04-19 14:01:10 (GMT) |
---|---|---|
committer | Marc Chevrier <marc.chevrier@gmail.com> | 2023-05-03 15:08:07 (GMT) |
commit | 375e6fdbbe398921de321216125765ba1917a325 (patch) | |
tree | 43d934f18f4d28a82d5264a0d0fb0301bc0bf7d9 | |
parent | 24a3e5cda049138ca178216e0d7b02262e4035ed (diff) | |
download | CMake-375e6fdbbe398921de321216125765ba1917a325.zip CMake-375e6fdbbe398921de321216125765ba1917a325.tar.gz CMake-375e6fdbbe398921de321216125765ba1917a325.tar.bz2 |
Link step: use linker dependency linker file
Based on work done by @ben.boeckel (!8051)
Fixes: #22217
43 files changed, 385 insertions, 39 deletions
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 34d2c50..6241f7d 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -479,6 +479,7 @@ Variables that Control the Build /variable/CMAKE_LIBRARY_PATH_FLAG /variable/CMAKE_LINK_DEF_FILE_FLAG /variable/CMAKE_LINK_DEPENDS_NO_SHARED + /variable/CMAKE_LINK_DEPENDS_USE_LINKER /variable/CMAKE_LINK_GROUP_USING_FEATURE /variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED /variable/CMAKE_LINK_INTERFACE_LIBRARIES diff --git a/Help/release/dev/use-linker-depfile.rst b/Help/release/dev/use-linker-depfile.rst new file mode 100644 index 0000000..1123707 --- /dev/null +++ b/Help/release/dev/use-linker-depfile.rst @@ -0,0 +1,11 @@ +use-linker-depfile +------------------ + +* GNU (and GNU-compatible) linkers gained support for a ``--dependency-file`` + flag in GNU Binutils 2.35 and LLVM's LLD 12.0.0. The + :ref:`Makefile <Makefile Generators>` and :ref:`Ninja <Ninja Generators>` + generators will now add these flags so that files read by the linker will + cause a relink if they change (typically modified timestamps). + + This feature can be controlled by the variable + :variable:`CMAKE_LINK_DEPENDS_USE_LINKER`. diff --git a/Help/variable/CMAKE_LINK_DEPENDS_USE_LINKER.rst b/Help/variable/CMAKE_LINK_DEPENDS_USE_LINKER.rst new file mode 100644 index 0000000..e1b37a5 --- /dev/null +++ b/Help/variable/CMAKE_LINK_DEPENDS_USE_LINKER.rst @@ -0,0 +1,12 @@ +CMAKE_LINK_DEPENDS_USE_LINKER +----------------------------- + +.. versionadded:: 3.27 + +For the :ref:`Makefile <Makefile Generators>` and +:ref:`Ninja <Ninja Generators>` generators, link dependencies are now, for a +selection of linkers, generated by the linker itself. By defining this +variable with value ``FALSE``, you can deactivate this feature. + +This feature is also deactivated if the :prop_tgt:`LINK_DEPENDS_NO_SHARED` +target property is true. diff --git a/Modules/CMakeASMCompiler.cmake.in b/Modules/CMakeASMCompiler.cmake.in index e300782..8a1718b 100644 --- a/Modules/CMakeASMCompiler.cmake.in +++ b/Modules/CMakeASMCompiler.cmake.in @@ -17,5 +17,6 @@ set(CMAKE_ASM@ASM_DIALECT@_COMPILER_ENV_VAR "@_CMAKE_ASM_COMPILER_ENV_VAR@") set(CMAKE_ASM@ASM_DIALECT@_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC) set(CMAKE_ASM@ASM_DIALECT@_LINKER_PREFERENCE 0) +set(CMAKE_ASM@ASM_DIALECT@_LINKER_DEPFILE_SUPPORTED "@CMAKE_ASM_LINKER_DEPFILE_SUPPORTED@") @CMAKE_ASM_COMPILER_CUSTOM_CODE@ diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in index 8ae07a3..cf3a242 100644 --- a/Modules/CMakeCCompiler.cmake.in +++ b/Modules/CMakeCCompiler.cmake.in @@ -39,6 +39,7 @@ set(CMAKE_C_COMPILER_ID_RUN 1) set(CMAKE_C_SOURCE_FILE_EXTENSIONS c;m) set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC) set(CMAKE_C_LINKER_PREFERENCE 10) +set(CMAKE_C_LINKER_DEPFILE_SUPPORTED "@CMAKE_C_LINKER_DEPFILE_SUPPORTED@") # Save compiler ABI information. set(CMAKE_C_SIZEOF_DATA_PTR "@CMAKE_C_SIZEOF_DATA_PTR@") diff --git a/Modules/CMakeCUDACompiler.cmake.in b/Modules/CMakeCUDACompiler.cmake.in index 57d595a..3d7d552 100644 --- a/Modules/CMakeCUDACompiler.cmake.in +++ b/Modules/CMakeCUDACompiler.cmake.in @@ -30,6 +30,7 @@ set(CMAKE_CUDA_COMPILER_ID_RUN 1) set(CMAKE_CUDA_SOURCE_FILE_EXTENSIONS cu) set(CMAKE_CUDA_LINKER_PREFERENCE 15) set(CMAKE_CUDA_LINKER_PREFERENCE_PROPAGATES 1) +set(CMAKE_CUDA_LINKER_DEPFILE_SUPPORTED "@CMAKE_CUDA_LINKER_DEPFILE_SUPPORTED@") set(CMAKE_CUDA_SIZEOF_DATA_PTR "@CMAKE_CUDA_SIZEOF_DATA_PTR@") set(CMAKE_CUDA_COMPILER_ABI "@CMAKE_CUDA_COMPILER_ABI@") diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in index 834c2e6..2052e7f 100644 --- a/Modules/CMakeCXXCompiler.cmake.in +++ b/Modules/CMakeCXXCompiler.cmake.in @@ -50,6 +50,7 @@ endforeach() set(CMAKE_CXX_LINKER_PREFERENCE 30) set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1) +set(CMAKE_CXX_LINKER_DEPFILE_SUPPORTED "@CMAKE_CXX_LINKER_DEPFILE_SUPPORTED@") # Save compiler ABI information. set(CMAKE_CXX_SIZEOF_DATA_PTR "@CMAKE_CXX_SIZEOF_DATA_PTR@") diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in index fc81d0e..a7caf2b 100644 --- a/Modules/CMakeFortranCompiler.cmake.in +++ b/Modules/CMakeFortranCompiler.cmake.in @@ -29,6 +29,7 @@ set(CMAKE_Fortran_COMPILER_ID_RUN 1) set(CMAKE_Fortran_SOURCE_FILE_EXTENSIONS f;F;fpp;FPP;f77;F77;f90;F90;for;For;FOR;f95;F95;f03;F03;f08;F08@CMAKE_Fortran_VENDOR_SOURCE_FILE_EXTENSIONS@) set(CMAKE_Fortran_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC) set(CMAKE_Fortran_LINKER_PREFERENCE 20) +set(CMAKE_Fortran_LINKER_DEPFILE_SUPPORTED "@CMAKE_Fortran_LINKER_DEPFILE_SUPPORTED@") if(UNIX) set(CMAKE_Fortran_OUTPUT_EXTENSION .o) else() diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in index 8a747c6..32c1223 100644 --- a/Modules/CMakeHIPCompiler.cmake.in +++ b/Modules/CMakeHIPCompiler.cmake.in @@ -26,6 +26,7 @@ set(CMAKE_HIP_COMPILER_ID_RUN 1) set(CMAKE_HIP_SOURCE_FILE_EXTENSIONS hip) set(CMAKE_HIP_LINKER_PREFERENCE 90) set(CMAKE_HIP_LINKER_PREFERENCE_PROPAGATES 1) +set(CMAKE_HIP_LINKER_DEPFILE_SUPPORTED "@CMAKE_HIP_LINKER_DEPFILE_SUPPORTED@") set(CMAKE_HIP_SIZEOF_DATA_PTR "@CMAKE_HIP_SIZEOF_DATA_PTR@") set(CMAKE_HIP_COMPILER_ABI "@CMAKE_HIP_COMPILER_ABI@") diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in index ea11a7a..0ceb804 100644 --- a/Modules/CMakeOBJCCompiler.cmake.in +++ b/Modules/CMakeOBJCCompiler.cmake.in @@ -37,6 +37,7 @@ set(CMAKE_OBJC_COMPILER_ID_RUN 1) set(CMAKE_OBJC_SOURCE_FILE_EXTENSIONS m) set(CMAKE_OBJC_IGNORE_EXTENSIONS h;H;o;O) set(CMAKE_OBJC_LINKER_PREFERENCE 5) +set(CMAKE_OBJC_LINKER_DEPFILE_SUPPORTED "@CMAKE_OBJC_LINKER_DEPFILE_SUPPORTED@") foreach (lang C CXX OBJCXX) foreach(extension IN LISTS CMAKE_OBJC_SOURCE_FILE_EXTENSIONS) diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in index 5d0b381..f087ec3 100644 --- a/Modules/CMakeOBJCXXCompiler.cmake.in +++ b/Modules/CMakeOBJCXXCompiler.cmake.in @@ -54,6 +54,7 @@ endforeach() set(CMAKE_OBJCXX_LINKER_PREFERENCE 25) set(CMAKE_OBJCXX_LINKER_PREFERENCE_PROPAGATES 1) +set(CMAKE_OBJCXX_LINKER_DEPFILE_SUPPORTED "@CMAKE_OBJCXX_LINKER_DEPFILE_SUPPORTED@") # Save compiler ABI information. set(CMAKE_OBJCXX_SIZEOF_DATA_PTR "@CMAKE_OBJCXX_SIZEOF_DATA_PTR@") diff --git a/Modules/Compiler/Clang-HIP.cmake b/Modules/Compiler/Clang-HIP.cmake index 4dbe2e8..7e3c99c 100644 --- a/Modules/Compiler/Clang-HIP.cmake +++ b/Modules/Compiler/Clang-HIP.cmake @@ -1,4 +1,13 @@ include(Compiler/Clang) + +# +# For now, deactivate globally linker dependency file support because +# HIP compiler is based on Clang which provides support of other languages +# +foreach (lang IN ITEMS "C" "CXX" "OBJC" "OBJCXX" "Fortran" "ASM") + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) +endforeach() + __compiler_clang(HIP) __compiler_clang_cxx_standards(HIP) diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake index 5930e37..f140208 100644 --- a/Modules/Compiler/GNU.cmake +++ b/Modules/Compiler/GNU.cmake @@ -52,6 +52,44 @@ macro(__compiler_gnu lang) set(CMAKE_DEPFILE_FLAGS_${lang} "-MD -MT <DEP_TARGET> -MF <DEP_FILE>") endif() + # define flags for linker depfile generation + if (NOT DEFINED CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) + ## Ensure ninja tool is recent enough... + if(CMAKE_GENERATOR MATCHES "^Ninja") + # Ninja 1.10 or upper is required + execute_process(COMMAND "${CMAKE_MAKE_PROGRAM}" --version + OUTPUT_VARIABLE _ninja_version + ERROR_VARIABLE _ninja_version) + if (_ninja_version MATCHES "[0-9]+(\\.[0-9]+)*") + set (_ninja_version "${CMAKE_MATCH_0}") + endif() + if (_ninja_version VERSION_LESS "1.10") + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) + endif() + unset(_ninja_version) + endif() + + if (NOT DEFINED CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) + ## check if this feature is supported by the linker + execute_process(COMMAND "${CMAKE_LINKER}" --help + OUTPUT_VARIABLE _linker_capabilities + ERROR_VARIABLE _linker_capabilities) + if(_linker_capabilities MATCHES "--dependency-file") + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED TRUE) + else() + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) + endif() + unset(_linker_capabilities) + endif() + endif() + if (CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) + set(CMAKE_${lang}_LINKER_DEPFILE_FLAGS "LINKER:--dependency-file,<DEP_FILE>") + set(CMAKE_${lang}_LINKER_DEPFILE_FORMAT gcc) + set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER TRUE) + else() + unset(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER) + endif() + # Initial configuration flags. string(APPEND CMAKE_${lang}_FLAGS_INIT " ") string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g") diff --git a/Source/cmDependsCompiler.cxx b/Source/cmDependsCompiler.cxx index 0cc4946..c8061c3 100644 --- a/Source/cmDependsCompiler.cxx +++ b/Source/cmDependsCompiler.cxx @@ -128,7 +128,7 @@ bool cmDependsCompiler::CheckDependencies( } std::string line; - if (!isValidPath) { + if (!isValidPath && !source.empty()) { // insert source as first dependency depends.push_back(source); } @@ -158,14 +158,16 @@ bool cmDependsCompiler::CheckDependencies( } // ensure source file is the first dependency - if (depends.front() != source) { - cm::erase(depends, source); - if (!isValidPath) { - depends.insert(depends.begin(), source); + if (!source.empty()) { + if (depends.front() != source) { + cm::erase(depends, source); + if (!isValidPath) { + depends.insert(depends.begin(), source); + } + } else if (isValidPath) { + // remove first dependency because it must not be filtered out + depends.erase(depends.begin()); } - } else if (isValidPath) { - // remove first dependency because it must not be filtered out - depends.erase(depends.begin()); } } else { // unknown format, ignore it @@ -174,8 +176,10 @@ bool cmDependsCompiler::CheckDependencies( if (isValidPath) { cm::erase_if(depends, isValidPath); - // insert source as first dependency - depends.insert(depends.begin(), source); + if (!source.empty()) { + // insert source as first dependency + depends.insert(depends.begin(), source); + } } dependencies[target] = std::move(depends); diff --git a/Source/cmGccDepfileLexerHelper.cxx b/Source/cmGccDepfileLexerHelper.cxx index 34c8824..87377de 100644 --- a/Source/cmGccDepfileLexerHelper.cxx +++ b/Source/cmGccDepfileLexerHelper.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGccDepfileLexerHelper.h" +#include <algorithm> #include <cstdio> #include <memory> #include <string> @@ -113,6 +114,11 @@ void cmGccDepfileLexerHelper::addToCurrentPath(const char* s) void cmGccDepfileLexerHelper::sanitizeContent() { for (auto it = this->Content.begin(); it != this->Content.end();) { + // remove duplicate path entries + std::sort(it->paths.begin(), it->paths.end()); + auto last = std::unique(it->paths.begin(), it->paths.end()); + it->paths.erase(last, it->paths.end()); + // Remove empty paths and normalize windows paths for (auto pit = it->paths.begin(); pit != it->paths.end();) { if (pit->empty()) { diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 1c37e52..8ac19fc 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -8518,6 +8518,30 @@ bool cmGeneratorTarget::IsLinkable() const this->IsExecutableWithExports()); } +bool cmGeneratorTarget::HasLinkDependencyFile(std::string const& config) const +{ + if (this->GetType() != cmStateEnums::EXECUTABLE && + this->GetType() != cmStateEnums::SHARED_LIBRARY && + this->GetType() != cmStateEnums::MODULE_LIBRARY) { + return false; + } + + if (this->Target->GetProperty("LINK_DEPENDS_NO_SHARED").IsOn()) { + // Do not use the linker dependency file because it includes shared + // libraries as well + return false; + } + + const std::string depsUseLinker{ "CMAKE_LINK_DEPENDS_USE_LINKER" }; + auto linkLanguage = this->GetLinkerLanguage(config); + const std::string langDepsUseLinker{ cmStrCat("CMAKE_", linkLanguage, + "_LINK_DEPENDS_USE_LINKER") }; + + return (!this->Makefile->IsDefinitionSet(depsUseLinker) || + this->Makefile->IsOn(depsUseLinker)) && + this->Makefile->IsOn(langDepsUseLinker); +} + bool cmGeneratorTarget::IsFrameworkOnApple() const { return this->Target->IsFrameworkOnApple(); diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 87227fd..9008286 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -805,6 +805,9 @@ public: /** Return whether this target may be used to link another target. */ bool IsLinkable() const; + /** Return whether the link step generates a dependency file. */ + bool HasLinkDependencyFile(std::string const& config) const; + /** Return whether this target is a shared library Framework on Apple. */ bool IsFrameworkOnApple() const; diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index dde3648..79fe52c 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -546,6 +546,8 @@ public: return cm::nullopt; } + virtual bool SupportsLinkerDependencyFile() const { return false; } + std::string GetSharedLibFlagsForLanguage(std::string const& lang) const; /** Generate an <output>.rule file path for a given command output. */ diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index bd54168..bfbe57f 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -236,6 +236,8 @@ public: return cmDepfileFormat::GccDepfile; } + bool SupportsLinkerDependencyFile() const override { return true; } + virtual cmGeneratedFileStream* GetImplFileStream( const std::string& /*config*/) const { diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h index 214ba2a..760679a 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.h +++ b/Source/cmGlobalUnixMakefileGenerator3.h @@ -99,6 +99,12 @@ public: */ bool SupportsCustomCommandDepfile() const override { return true; } + /** + * Utilized to determine if this generator + * supports linker dependency file. + */ + bool SupportsLinkerDependencyFile() const override { return true; } + /** Get the documentation entry for this generator. */ static cmDocumentationEntry GetDocumentation(); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 64f0246..b02fa62 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1586,6 +1586,8 @@ void cmLocalGenerator::GetTargetFlags( this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config, linkLanguage); this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage); + this->AppendDependencyInfoLinkerFlags(extraLinkFlags, target, config, + linkLanguage); this->AppendModuleDefinitionFlag(extraLinkFlags, target, linkLineComputer, config); @@ -3202,6 +3204,42 @@ void cmLocalGenerator::AppendPositionIndependentLinkerFlags( } } +void cmLocalGenerator::AppendDependencyInfoLinkerFlags( + std::string& flags, cmGeneratorTarget* target, const std::string& config, + const std::string& linkLanguage) +{ + if (!this->GetGlobalGenerator()->SupportsLinkerDependencyFile() || + !target->HasLinkDependencyFile(config)) { + return; + } + + auto depFlag = *this->Makefile->GetDefinition( + cmStrCat("CMAKE_", linkLanguage, "_LINKER_DEPFILE_FLAGS")); + if (depFlag.empty()) { + return; + } + + auto depFile = this->ConvertToOutputFormat( + this->MaybeRelativeToWorkDir(this->GetLinkDependencyFile(target, config)), + cmOutputConverter::SHELL); + std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( + this->CreateRulePlaceholderExpander()); + cmRulePlaceholderExpander::RuleVariables linkDepsVariables; + linkDepsVariables.DependencyFile = depFile.c_str(); + rulePlaceholderExpander->ExpandRuleVariables(this, depFlag, + linkDepsVariables); + auto depFlags = cmExpandListWithBacktrace(depFlag); + target->ResolveLinkerWrapper(depFlags, linkLanguage); + + this->AppendFlags(flags, depFlags); +} + +std::string cmLocalGenerator::GetLinkDependencyFile( + cmGeneratorTarget* /*target*/, const std::string& /*config*/) const +{ + return "link.d"; +} + void cmLocalGenerator::AppendModuleDefinitionFlag( std::string& flags, cmGeneratorTarget const* target, cmLinkLineComputer* linkLineComputer, std::string const& config) diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index ea60a4e..3ad52c4 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -183,6 +183,12 @@ public: cmGeneratorTarget* target, const std::string& config, const std::string& lang); + void AppendDependencyInfoLinkerFlags(std::string& flags, + cmGeneratorTarget* target, + const std::string& config, + const std::string& lang); + virtual std::string GetLinkDependencyFile(cmGeneratorTarget* target, + const std::string& config) const; void AppendModuleDefinitionFlag(std::string& flags, cmGeneratorTarget const* target, cmLinkLineComputer* linkLineComputer, diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 2e837bb..82d5a60 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -200,6 +200,14 @@ std::string cmLocalNinjaGenerator::MaybeRelativeToWorkDir( this->MaybeRelativeToTopBinDir(path)); } +std::string cmLocalNinjaGenerator::GetLinkDependencyFile( + cmGeneratorTarget* target, std::string const& config) const +{ + return cmStrCat(target->GetSupportDirectory(), + this->GetGlobalNinjaGenerator()->ConfigDirectory(config), + "/link.d"); +} + // Virtual protected methods. std::string cmLocalNinjaGenerator::ConvertToIncludeReference( diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h index 347aaa0..0bc462b 100644 --- a/Source/cmLocalNinjaGenerator.h +++ b/Source/cmLocalNinjaGenerator.h @@ -94,6 +94,9 @@ public: bool HasUniqueByproducts(std::vector<std::string> const& byproducts, cmListFileBacktrace const& bt); + std::string GetLinkDependencyFile(cmGeneratorTarget* target, + std::string const& config) const override; + protected: std::string ConvertToIncludeReference( std::string const& path, cmOutputConverter::OutputFormat format) override; diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 028066e..9f3894a 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -239,6 +239,12 @@ void cmLocalUnixMakefileGenerator3::GetIndividualFileTargets( } } +std::string cmLocalUnixMakefileGenerator3::GetLinkDependencyFile( + cmGeneratorTarget* target, std::string const& /*config*/) const +{ + return cmStrCat(target->GetSupportDirectory(), "/link.d"); +} + void cmLocalUnixMakefileGenerator3::WriteLocalMakefile() { // generate the includes @@ -2008,6 +2014,18 @@ void cmLocalUnixMakefileGenerator3::WriteDependLanguageInfo( << this->MaybeRelativeToTopBinDir(src) << "\"\n"; } } + } else if (compilerLang.first == "LINK"_s) { + auto depFormat = this->Makefile->GetDefinition( + cmStrCat("CMAKE_", target->GetLinkerLanguage(this->GetConfigName()), + "_LINKER_DEPFILE_FORMAT")); + for (auto const& compilerPair : compilerPairs) { + for (auto const& src : compilerPair.second) { + cmakefileStream << R"( "" ")" + << this->MaybeRelativeToTopBinDir(compilerPair.first) + << "\" \"" << depFormat << "\" \"" + << this->MaybeRelativeToTopBinDir(src) << "\"\n"; + } + } } else { auto depFormat = this->Makefile->GetSafeDefinition( cmStrCat("CMAKE_", compilerLang.first, "_DEPFILE_FORMAT")); diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h index 78aa7f9..7d5a922 100644 --- a/Source/cmLocalUnixMakefileGenerator3.h +++ b/Source/cmLocalUnixMakefileGenerator3.h @@ -191,6 +191,9 @@ public: // Eclipse generator. void GetIndividualFileTargets(std::vector<std::string>& targets); + std::string GetLinkDependencyFile(cmGeneratorTarget* target, + std::string const& config) const override; + protected: void WriteLocalMakefile(); diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 3caabde..be8318c 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -70,6 +70,8 @@ void cmMakefileExecutableTargetGenerator::WriteRuleFiles() this->WriteExecutableRule(true); } + this->WriteTargetLinkDependRules(); + // Write clean target this->WriteTargetCleanRules(); diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 691edf4..0ebbe4d 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -62,6 +62,9 @@ void cmMakefileLibraryTargetGenerator::WriteRuleFiles() // write in rules for object files and custom commands this->WriteTargetBuildRules(); + // Write in the rules for the link dependency file + this->WriteTargetLinkDependRules(); + // write the link rules // Write the rule for this target type. switch (this->GeneratorTarget->GetType()) { diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index a0a7324..97e3fad 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -150,6 +150,8 @@ void cmMakefileTargetGenerator::GetTargetLinkFlags( this->LocalGenerator->AppendPositionIndependentLinkerFlags( flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); + this->LocalGenerator->AppendDependencyInfoLinkerFlags( + flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); } void cmMakefileTargetGenerator::CreateRuleFile() @@ -414,8 +416,10 @@ void cmMakefileTargetGenerator::WriteCommonCodeRules() this->GlobalGenerator->SupportsCompilerDependencies() && (!this->Makefile->IsDefinitionSet(depsUseCompiler) || this->Makefile->IsOn(depsUseCompiler)); + bool linkerGenerateDeps = + this->GeneratorTarget->HasLinkDependencyFile(this->GetConfigName()); - if (compilerGenerateDeps || ccGenerateDeps) { + if (compilerGenerateDeps || linkerGenerateDeps || ccGenerateDeps) { std::string compilerDependFile = cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make"); *this->BuildFileStream << "# Include any dependencies generated by the " @@ -1499,6 +1503,21 @@ bool cmMakefileTargetGenerator::WriteMakeRule( return symbolic; } +void cmMakefileTargetGenerator::WriteTargetLinkDependRules() +{ + if (!this->GeneratorTarget->HasLinkDependencyFile(this->GetConfigName())) { + return; + } + + auto depFile = this->LocalGenerator->GetLinkDependencyFile( + this->GeneratorTarget, this->GetConfigName()); + this->CleanFiles.insert(depFile); + this->LocalGenerator->AddImplicitDepends( + this->GeneratorTarget, "LINK", + this->GeneratorTarget->GetFullPath(this->GetConfigName()), depFile, + cmDependencyScannerKind::Compiler); +} + void cmMakefileTargetGenerator::WriteTargetDependRules() { // must write the targets depend info file @@ -2052,8 +2071,14 @@ void cmMakefileTargetGenerator::AppendTargetDepends( return; } - // Loop over all library dependencies. const std::string& cfg = this->GetConfigName(); + + if (this->GeneratorTarget->HasLinkDependencyFile(cfg)) { + depends.push_back( + cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")); + } + + // Loop over all library dependencies. if (cmComputeLinkInformation* cli = this->GeneratorTarget->GetLinkInformation(cfg)) { cm::append(depends, cli->GetDepends()); diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index 98c3a0e..ef7a60f 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -77,6 +77,8 @@ protected: // write the clean rules for this target void WriteTargetCleanRules(); + // write the linker depend rules for this target + void WriteTargetLinkDependRules(); // write the depend rules for this target void WriteTargetDependRules(); diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index ee4fa90..9903d63 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -417,6 +417,13 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile, std::string cmakeVarLang = cmStrCat("CMAKE_", this->TargetLinkLanguage(config)); + if (this->GeneratorTarget->HasLinkDependencyFile(config)) { + auto DepFileFormat = this->GetMakefile()->GetDefinition( + cmStrCat(cmakeVarLang, "_LINKER_DEPFILE_FORMAT")); + rule.DepType = DepFileFormat; + rule.DepFile = "$DEP_FILE"; + } + // build response file name std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); @@ -1134,6 +1141,14 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( cmNinjaBuild linkBuild(this->LanguageLinkerRule(config)); cmNinjaVars& vars = linkBuild.Variables; + if (this->GeneratorTarget->HasLinkDependencyFile(config)) { + vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( + this->ConvertToNinjaPath( + this->GetLocalGenerator()->GetLinkDependencyFile(this->GeneratorTarget, + config)), + cmOutputConverter::SHELL); + } + // Compute the comment. linkBuild.Comment = cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal); diff --git a/Tests/CMakeLib/testGccDepfileReader_data/deps1.txt b/Tests/CMakeLib/testGccDepfileReader_data/deps1.txt index fd2679f..4207b58 100644 --- a/Tests/CMakeLib/testGccDepfileReader_data/deps1.txt +++ b/Tests/CMakeLib/testGccDepfileReader_data/deps1.txt @@ -1,26 +1,26 @@ --RULES-- main.o --DEPENDENCIES-- -main.cpp +/usr/include/features.h /usr/include/stdc-predef.h /usr/include/stdio.h /usr/include/x86_64-linux-gnu/bits/libc-header-start.h -/usr/include/features.h -/usr/include/x86_64-linux-gnu/sys/cdefs.h -/usr/include/x86_64-linux-gnu/bits/wordsize.h /usr/include/x86_64-linux-gnu/bits/long-double.h -/usr/include/x86_64-linux-gnu/gnu/stubs.h -/usr/include/x86_64-linux-gnu/gnu/stubs-64.h -/usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h -/usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h +/usr/include/x86_64-linux-gnu/bits/stdio_lim.h +/usr/include/x86_64-linux-gnu/bits/sys_errlist.h /usr/include/x86_64-linux-gnu/bits/types.h -/usr/include/x86_64-linux-gnu/bits/typesizes.h +/usr/include/x86_64-linux-gnu/bits/types/FILE.h +/usr/include/x86_64-linux-gnu/bits/types/__FILE.h +/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h -/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h -/usr/include/x86_64-linux-gnu/bits/types/__FILE.h -/usr/include/x86_64-linux-gnu/bits/types/FILE.h -/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h -/usr/include/x86_64-linux-gnu/bits/stdio_lim.h -/usr/include/x86_64-linux-gnu/bits/sys_errlist.h +/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h +/usr/include/x86_64-linux-gnu/bits/typesizes.h +/usr/include/x86_64-linux-gnu/bits/wordsize.h +/usr/include/x86_64-linux-gnu/gnu/stubs-64.h +/usr/include/x86_64-linux-gnu/gnu/stubs.h +/usr/include/x86_64-linux-gnu/sys/cdefs.h +/usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h +/usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h +main.cpp diff --git a/Tests/CMakeLib/testGccDepfileReader_data/deps3.txt b/Tests/CMakeLib/testGccDepfileReader_data/deps3.txt index 448f69c..8d82c60 100644 --- a/Tests/CMakeLib/testGccDepfileReader_data/deps3.txt +++ b/Tests/CMakeLib/testGccDepfileReader_data/deps3.txt @@ -1,11 +1,11 @@ --RULES-- main.o --DEPENDENCIES-- -main.cpp -foo#bar.h -foo\#bar.h foo bar.h +foo#bar.h +foo$bar.h foo\ bar.h +foo\#bar.h foo\\ bar.h foo\\\\ -foo$bar.h +main.cpp diff --git a/Tests/RunCMake/BuildDepends/LinkDepends.cmake b/Tests/RunCMake/BuildDepends/LinkDepends.cmake new file mode 100644 index 0000000..a414e03 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/LinkDepends.cmake @@ -0,0 +1,22 @@ + +enable_language(C) + +include("${CMAKE_BINARY_DIR}/../LinkDependsExternalLibrary-build/ExternalLibrary-debug.cmake") +cmake_path(GET EXTERNAL_LIBRARY PARENT_PATH EXTERNAL_DIR) + +add_library(LinkDependsLib SHARED "${CMAKE_CURRENT_BINARY_DIR}/lib_depends.c") +target_link_directories(LinkDependsLib PRIVATE "${EXTERNAL_DIR}") +target_link_libraries(LinkDependsLib PRIVATE External) + +add_executable(LinkDependsExe "${CMAKE_CURRENT_BINARY_DIR}/exe_depends.c") +target_link_directories(LinkDependsExe PRIVATE "${EXTERNAL_DIR}") +target_link_libraries(LinkDependsExe PRIVATE External) + + +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake" + CONTENT " +set(check_pairs + \"$<TARGET_FILE:LinkDependsLib>|${EXTERNAL_LIBRARY}\" + \"$<TARGET_FILE:LinkDependsExe>|${EXTERNAL_LIBRARY}\" + ) +") diff --git a/Tests/RunCMake/BuildDepends/LinkDepends.step1.cmake b/Tests/RunCMake/BuildDepends/LinkDepends.step1.cmake new file mode 100644 index 0000000..5ce55b0 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/LinkDepends.step1.cmake @@ -0,0 +1,23 @@ + +file(WRITE "${RunCMake_TEST_BINARY_DIR}/lib_depends.c" [[ + +extern void external(void); + +void lib_depends(void) +{ + external(); +} +]]) + + +file(WRITE "${RunCMake_TEST_BINARY_DIR}/exe_depends.c" [[ + +extern void external(void); + +int main(void) +{ + external(); + + return 0; +} +]]) diff --git a/Tests/RunCMake/BuildDepends/LinkDepends.step2.cmake b/Tests/RunCMake/BuildDepends/LinkDepends.step2.cmake new file mode 100644 index 0000000..f2c0067 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/LinkDepends.step2.cmake @@ -0,0 +1,4 @@ + +include ("${RunCMake_TEST_BINARY_DIR}/../LinkDependsExternalLibrary-build/ExternalLibrary-debug.cmake") + +file(TOUCH "${EXTERNAL_LIBRARY}") diff --git a/Tests/RunCMake/BuildDepends/LinkDependsCheck.cmake b/Tests/RunCMake/BuildDepends/LinkDependsCheck.cmake new file mode 100644 index 0000000..a21096b --- /dev/null +++ b/Tests/RunCMake/BuildDepends/LinkDependsCheck.cmake @@ -0,0 +1,11 @@ + +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/LinkDependsUseLinker.cmake" + "set(CMAKE_C_LINK_DEPENDS_USE_LINKER \"${CMAKE_C_LINK_DEPENDS_USE_LINKER}\")\n") + + +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake" + CONTENT " +# no required actions +") diff --git a/Tests/RunCMake/BuildDepends/LinkDependsExternalLibrary.cmake b/Tests/RunCMake/BuildDepends/LinkDependsExternalLibrary.cmake new file mode 100644 index 0000000..fe6575c --- /dev/null +++ b/Tests/RunCMake/BuildDepends/LinkDependsExternalLibrary.cmake @@ -0,0 +1,13 @@ + +enable_language(C) + +add_library(External SHARED "${CMAKE_CURRENT_BINARY_DIR}/external.c") + +file(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/ExternalLibrary-$<LOWER_CASE:$<CONFIG>>.cmake" + CONTENT "set(EXTERNAL_LIBRARY \"$<TARGET_LINKER_FILE:External>\")\n") + + +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake" + CONTENT " +# no required actions +") diff --git a/Tests/RunCMake/BuildDepends/LinkDependsExternalLibrary.step1.cmake b/Tests/RunCMake/BuildDepends/LinkDependsExternalLibrary.step1.cmake new file mode 100644 index 0000000..df302f7 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/LinkDependsExternalLibrary.step1.cmake @@ -0,0 +1,11 @@ + +file(WRITE "${RunCMake_TEST_BINARY_DIR}/external.c" [[ + + +#if defined(_WIN32) +__declspec(dllexport) +#endif + void external(void) +{ +} +]]) diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake index 8099079..b527580 100644 --- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake +++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake @@ -194,3 +194,15 @@ if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 " OR endif() run_BuildDepends(CustomCommandUnityBuild) unset(run_BuildDepends_skip_step_2) + +#if (RunCMake_GENERATOR MATCHES "Make|Ninja" AND CMAKE_C_LINK_DEPENDS_USE_LINKER) +if (RunCMake_GENERATOR MATCHES "Make|Ninja") + set(run_BuildDepends_skip_step_2 1) + run_BuildDepends(LinkDependsCheck) + include("${RunCMake_BINARY_DIR}/LinkDependsCheck-build/LinkDependsUseLinker.cmake") + if (CMAKE_C_LINK_DEPENDS_USE_LINKER) + run_BuildDepends(LinkDependsExternalLibrary) + unset(run_BuildDepends_skip_step_2) + run_BuildDepends(LinkDepends) + endif() +endif() diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 32ccef0..ada9132 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -295,6 +295,7 @@ endif() add_RunCMake_test(BuildDepends -DMSVC_VERSION=${MSVC_VERSION} -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_C_LINK_DEPENDS_USE_COMPILER=${CMAKE_C_LINK_DEPENDS_USE_COMPILER} -DCMake_TEST_BuildDepends_GNU_AS=${CMake_TEST_BuildDepends_GNU_AS} ) if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja") diff --git a/Tests/RunCMake/TransformDepfile/deps-unix.d.txt b/Tests/RunCMake/TransformDepfile/deps-unix.d.txt index fbdecc0..2588181 100644 --- a/Tests/RunCMake/TransformDepfile/deps-unix.d.txt +++ b/Tests/RunCMake/TransformDepfile/deps-unix.d.txt @@ -1,8 +1,8 @@ subdir/out1 \ /home/build/out2: \ - subdir/in1 \ - /home/build/in2 + /home/build/in2 \ + subdir/in1 subdir/out3 \ /home/build/out4: \ - subdir/in3 \ - /home/build/in4 + /home/build/in4 \ + subdir/in3 diff --git a/Tests/RunCMake/TransformDepfile/deps-windows.d.txt b/Tests/RunCMake/TransformDepfile/deps-windows.d.txt index e09ae37..805a4c8 100644 --- a/Tests/RunCMake/TransformDepfile/deps-windows.d.txt +++ b/Tests/RunCMake/TransformDepfile/deps-windows.d.txt @@ -1,8 +1,8 @@ subdir/out1 \ C:/build/out2: \ - subdir/in1 \ - C:/build/in2 + C:/build/in2 \ + subdir/in1 subdir/out3 \ C:/build/out4: \ - subdir/in3 \ - C:/build/in4 + C:/build/in4 \ + subdir/in3 |