Browse Source

Link step: use linker dependency linker file

Based on work done by @ben.boeckel (!8051)

Fixes: #22217
Marc Chevrier 2 years ago
parent
commit
375e6fdbbe
43 changed files with 385 additions and 39 deletions
  1. 1 0
      Help/manual/cmake-variables.7.rst
  2. 11 0
      Help/release/dev/use-linker-depfile.rst
  3. 12 0
      Help/variable/CMAKE_LINK_DEPENDS_USE_LINKER.rst
  4. 1 0
      Modules/CMakeASMCompiler.cmake.in
  5. 1 0
      Modules/CMakeCCompiler.cmake.in
  6. 1 0
      Modules/CMakeCUDACompiler.cmake.in
  7. 1 0
      Modules/CMakeCXXCompiler.cmake.in
  8. 1 0
      Modules/CMakeFortranCompiler.cmake.in
  9. 1 0
      Modules/CMakeHIPCompiler.cmake.in
  10. 1 0
      Modules/CMakeOBJCCompiler.cmake.in
  11. 1 0
      Modules/CMakeOBJCXXCompiler.cmake.in
  12. 9 0
      Modules/Compiler/Clang-HIP.cmake
  13. 38 0
      Modules/Compiler/GNU.cmake
  14. 14 10
      Source/cmDependsCompiler.cxx
  15. 6 0
      Source/cmGccDepfileLexerHelper.cxx
  16. 24 0
      Source/cmGeneratorTarget.cxx
  17. 3 0
      Source/cmGeneratorTarget.h
  18. 2 0
      Source/cmGlobalGenerator.h
  19. 2 0
      Source/cmGlobalNinjaGenerator.h
  20. 6 0
      Source/cmGlobalUnixMakefileGenerator3.h
  21. 38 0
      Source/cmLocalGenerator.cxx
  22. 6 0
      Source/cmLocalGenerator.h
  23. 8 0
      Source/cmLocalNinjaGenerator.cxx
  24. 3 0
      Source/cmLocalNinjaGenerator.h
  25. 18 0
      Source/cmLocalUnixMakefileGenerator3.cxx
  26. 3 0
      Source/cmLocalUnixMakefileGenerator3.h
  27. 2 0
      Source/cmMakefileExecutableTargetGenerator.cxx
  28. 3 0
      Source/cmMakefileLibraryTargetGenerator.cxx
  29. 27 2
      Source/cmMakefileTargetGenerator.cxx
  30. 2 0
      Source/cmMakefileTargetGenerator.h
  31. 15 0
      Source/cmNinjaNormalTargetGenerator.cxx
  32. 15 15
      Tests/CMakeLib/testGccDepfileReader_data/deps1.txt
  33. 4 4
      Tests/CMakeLib/testGccDepfileReader_data/deps3.txt
  34. 22 0
      Tests/RunCMake/BuildDepends/LinkDepends.cmake
  35. 23 0
      Tests/RunCMake/BuildDepends/LinkDepends.step1.cmake
  36. 4 0
      Tests/RunCMake/BuildDepends/LinkDepends.step2.cmake
  37. 11 0
      Tests/RunCMake/BuildDepends/LinkDependsCheck.cmake
  38. 13 0
      Tests/RunCMake/BuildDepends/LinkDependsExternalLibrary.cmake
  39. 11 0
      Tests/RunCMake/BuildDepends/LinkDependsExternalLibrary.step1.cmake
  40. 12 0
      Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
  41. 1 0
      Tests/RunCMake/CMakeLists.txt
  42. 4 4
      Tests/RunCMake/TransformDepfile/deps-unix.d.txt
  43. 4 4
      Tests/RunCMake/TransformDepfile/deps-windows.d.txt

+ 1 - 0
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

+ 11 - 0
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`.

+ 12 - 0
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.

+ 1 - 0
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@

+ 1 - 0
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@")

+ 1 - 0
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@")

+ 1 - 0
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@")

+ 1 - 0
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()

+ 1 - 0
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@")

+ 1 - 0
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)

+ 1 - 0
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@")

+ 9 - 0
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)
 

+ 38 - 0
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")

+ 14 - 10
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);

+ 6 - 0
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()) {

+ 24 - 0
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();

+ 3 - 0
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;

+ 2 - 0
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.  */

+ 2 - 0
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
   {

+ 6 - 0
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();
 

+ 38 - 0
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)

+ 6 - 0
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,

+ 8 - 0
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(

+ 3 - 0
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;

+ 18 - 0
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"));

+ 3 - 0
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();
 

+ 2 - 0
Source/cmMakefileExecutableTargetGenerator.cxx

@@ -70,6 +70,8 @@ void cmMakefileExecutableTargetGenerator::WriteRuleFiles()
     this->WriteExecutableRule(true);
   }
 
+  this->WriteTargetLinkDependRules();
+
   // Write clean target
   this->WriteTargetCleanRules();
 

+ 3 - 0
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()) {

+ 27 - 2
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());

+ 2 - 0
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();
 

+ 15 - 0
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);

+ 15 - 15
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

+ 4 - 4
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

+ 22 - 0
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}\"
+  )
+")

+ 23 - 0
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;
+}
+]])

+ 4 - 0
Tests/RunCMake/BuildDepends/LinkDepends.step2.cmake

@@ -0,0 +1,4 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/../LinkDependsExternalLibrary-build/ExternalLibrary-debug.cmake")
+
+file(TOUCH "${EXTERNAL_LIBRARY}")

+ 11 - 0
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
+")

+ 13 - 0
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
+")

+ 11 - 0
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)
+{
+}
+]])

+ 12 - 0
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()

+ 1 - 0
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")

+ 4 - 4
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

+ 4 - 4
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