Browse Source

LINK_DEPENDS: add support for property INTERFACE_LINK_DEPENDS

Fixes: #17997
Marc Chevrier 7 years ago
parent
commit
cc9f88af53

+ 2 - 0
Help/manual/cmake-properties.7.rst

@@ -198,6 +198,7 @@ Properties on Targets
    /prop_tgt/IMPORTED_LIBNAME_CONFIG
    /prop_tgt/IMPORTED_LIBNAME
    /prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES_CONFIG
+   /prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES_CONFIG
    /prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES
    /prop_tgt/IMPORTED_LINK_INTERFACE_LANGUAGES_CONFIG
    /prop_tgt/IMPORTED_LINK_INTERFACE_LANGUAGES
@@ -225,6 +226,7 @@ Properties on Targets
    /prop_tgt/INTERFACE_COMPILE_FEATURES
    /prop_tgt/INTERFACE_COMPILE_OPTIONS
    /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
+   /prop_tgt/INTERFACE_LINK_DEPENDS
    /prop_tgt/INTERFACE_LINK_LIBRARIES
    /prop_tgt/INTERFACE_LINK_OPTIONS
    /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE

+ 31 - 0
Help/prop_tgt/INTERFACE_LINK_DEPENDS.rst

@@ -0,0 +1,31 @@
+INTERFACE_LINK_DEPENDS
+----------------------
+
+Additional public interface files on which a target binary depends for linking.
+
+This property is supported only by Makefile and Ninja generators.  It is
+intended to specify dependencies on "linker scripts" for custom Makefile link
+rules.
+
+When target dependencies are specified using :command:`target_link_libraries`,
+CMake will read this property from all target dependencies to determine the
+build properties of the consumer.
+
+Contents of ``INTERFACE_LINK_DEPENDS`` may use "generator expressions"
+with the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
+manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
+-manual for more on defining buildsystem properties.
+
+Link dependency files usage requirements commonly differ between the build-tree
+and the install-tree.  The ``BUILD_INTERFACE`` and ``INSTALL_INTERFACE``
+generator expressions can be used to describe separate usage requirements
+based on the usage location.  Relative paths are allowed within the
+``INSTALL_INTERFACE`` expression and are interpreted relative to the
+installation prefix.  For example:
+
+.. code-block:: cmake
+
+  set_property(TARGET mylib PROPERTY INTERFACE_LINK_DEPENDS
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/mylinkscript>
+    $<INSTALL_INTERFACE:mylinkscript>  # <prefix>/mylinkscript
+  )

+ 2 - 2
Help/prop_tgt/LINK_DEPENDS.rst

@@ -7,6 +7,6 @@ Specifies a semicolon-separated list of full-paths to files on which
 the link rule for this target depends.  The target binary will be
 linked if any of the named files is newer than it.
 
-This property is ignored by non-Makefile generators.  It is intended
-to specify dependencies on "linker scripts" for custom Makefile link
+This property is supported only by Makefile and Ninja generators.  It is
+intended to specify dependencies on "linker scripts" for custom Makefile link
 rules.

+ 4 - 0
Help/release/dev/INTERFACE_LINK_DEPENDS-property.rst

@@ -0,0 +1,4 @@
+INTERFACE_LINK_DEPENDS-property
+-------------------------------
+
+* Binary targets gained new :prop_tgt:`INTERFACE_LINK_DEPENDS` property.

+ 3 - 0
Source/cmExportBuildFileGenerator.cxx

@@ -98,6 +98,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);
+    this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte,
+                                    cmGeneratorExpression::BuildInterface,
+                                    properties, missingTargets);
     this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
                                     properties);
 

+ 31 - 0
Source/cmExportFileGenerator.cxx

@@ -420,6 +420,37 @@ void cmExportFileGenerator::PopulateIncludeDirectoriesInterface(
   }
 }
 
+void cmExportFileGenerator::PopulateLinkDependsInterface(
+  cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule,
+  ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
+{
+  cmGeneratorTarget* gt = tei->Target;
+  assert(preprocessRule == cmGeneratorExpression::InstallInterface);
+
+  const char* propName = "INTERFACE_LINK_DEPENDS";
+  const char* input = gt->GetProperty(propName);
+
+  if (!input) {
+    return;
+  }
+
+  if (!*input) {
+    properties[propName].clear();
+    return;
+  }
+
+  std::string prepro =
+    cmGeneratorExpression::Preprocess(input, preprocessRule, true);
+  if (!prepro.empty()) {
+    this->ResolveTargetsInGeneratorExpressions(prepro, gt, missingTargets);
+
+    if (!checkInterfaceDirs(prepro, gt, propName)) {
+      return;
+    }
+    properties[propName] = prepro;
+  }
+}
+
 void cmExportFileGenerator::PopulateInterfaceProperty(
   const std::string& propName, cmGeneratorTarget* target,
   cmGeneratorExpression::PreprocessContext preprocessRule,

+ 4 - 0
Source/cmExportFileGenerator.h

@@ -147,6 +147,10 @@ protected:
     cmTargetExport* target,
     cmGeneratorExpression::PreprocessContext preprocessRule,
     ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
+  void PopulateLinkDependsInterface(
+    cmTargetExport* target,
+    cmGeneratorExpression::PreprocessContext preprocessRule,
+    ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
 
   void SetImportLinkInterface(
     const std::string& config, std::string const& suffix,

+ 2 - 0
Source/cmExportInstallFileGenerator.cxx

@@ -106,6 +106,8 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt,
                                     cmGeneratorExpression::InstallInterface,
                                     properties, missingTargets);
+    this->PopulateLinkDependsInterface(
+      te, cmGeneratorExpression::InstallInterface, properties, missingTargets);
 
     std::string errorMessage;
     if (!this->PopulateExportProperties(gt, properties, errorMessage)) {

+ 2 - 1
Source/cmGeneratorExpressionDAGChecker.h

@@ -26,7 +26,8 @@ struct cmGeneratorExpressionContext;
   SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS)                        \
   SELECT(F, EvaluatingSources, SOURCES)                                       \
   SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES)                      \
-  SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS)
+  SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS)                              \
+  SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)
 
 #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F)                                    \
   CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)

+ 36 - 0
Source/cmGeneratorTarget.cxx

@@ -3008,6 +3008,42 @@ void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
   }
 }
 
+namespace {
+void processLinkDepends(
+  cmGeneratorTarget const* tgt,
+  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
+  std::vector<std::string>& options,
+  std::unordered_set<std::string>& uniqueOptions,
+  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
+  std::string const& language)
+{
+  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
+                         config, false, "link depends", language,
+                         OptionsParse::None);
+}
+}
+
+void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result,
+                                       const std::string& config,
+                                       const std::string& language) const
+{
+  if (const char* linkDepends = this->GetProperty("LINK_DEPENDS")) {
+    cmSystemTools::ExpandListArgument(linkDepends, result);
+  }
+
+  std::unordered_set<std::string> uniqueOptions;
+  std::vector<cmGeneratorTarget::TargetPropertyEntry*> linkDependsEntries;
+  cmGeneratorExpressionDAGChecker dagChecker(this->GetName(), "LINK_DEPENDS",
+                                             nullptr, nullptr);
+
+  AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS",
+                      linkDependsEntries);
+  processLinkDepends(this, linkDependsEntries, result, uniqueOptions,
+                     &dagChecker, config, language);
+
+  cmDeleteAll(linkDependsEntries);
+}
+
 void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
 {
   if (this->IsImported()) {

+ 4 - 0
Source/cmGeneratorTarget.h

@@ -422,6 +422,10 @@ public:
                       const std::string& config,
                       const std::string& language) const;
 
+  void GetLinkDepends(std::vector<std::string>& result,
+                      const std::string& config,
+                      const std::string& language) const;
+
   bool IsSystemIncludeDirectory(const std::string& dir,
                                 const std::string& config,
                                 const std::string& language) const;

+ 11 - 11
Source/cmMakefileExecutableTargetGenerator.cxx

@@ -97,15 +97,15 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule(
 
   std::vector<std::string> commands;
 
-  // Build list of dependencies.
-  std::vector<std::string> depends;
-  this->AppendLinkDepends(depends);
-
   // Get the language to use for linking this library.
   std::string linkLanguage = "CUDA";
   std::string const objExt =
     this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
 
+  // Build list of dependencies.
+  std::vector<std::string> depends;
+  this->AppendLinkDepends(depends, linkLanguage);
+
   // Get the name of the device object to generate.
   std::string const targetOutputReal =
     this->GeneratorTarget->ObjectDirectory + "cmake_device_link" + objExt;
@@ -294,13 +294,6 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
 {
   std::vector<std::string> commands;
 
-  // Build list of dependencies.
-  std::vector<std::string> depends;
-  this->AppendLinkDepends(depends);
-  if (!this->DeviceLinkObject.empty()) {
-    depends.push_back(this->DeviceLinkObject);
-  }
-
   // Get the name of the executable to generate.
   std::string targetName;
   std::string targetNameReal;
@@ -378,6 +371,13 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
     return;
   }
 
+  // Build list of dependencies.
+  std::vector<std::string> depends;
+  this->AppendLinkDepends(depends, linkLanguage);
+  if (!this->DeviceLinkObject.empty()) {
+    depends.push_back(this->DeviceLinkObject);
+  }
+
   this->NumberOfProgressActions++;
   if (!this->NoRuleMessages) {
     cmLocalUnixMakefileGenerator3::EchoProgress progress;

+ 11 - 11
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -260,15 +260,15 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules(
   // code duplication.
   std::vector<std::string> commands;
 
-  // Build list of dependencies.
-  std::vector<std::string> depends;
-  this->AppendLinkDepends(depends);
-
   // Get the language to use for linking this library.
   std::string linkLanguage = "CUDA";
   std::string const objExt =
     this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
 
+  // Build list of dependencies.
+  std::vector<std::string> depends;
+  this->AppendLinkDepends(depends, linkLanguage);
+
   // Create set of linking flags.
   std::string linkFlags;
   this->GetTargetLinkFlags(linkFlags, linkLanguage);
@@ -444,13 +444,6 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
   // code duplication.
   std::vector<std::string> commands;
 
-  // Build list of dependencies.
-  std::vector<std::string> depends;
-  this->AppendLinkDepends(depends);
-  if (!this->DeviceLinkObject.empty()) {
-    depends.push_back(this->DeviceLinkObject);
-  }
-
   // Get the language to use for linking this library.
   std::string linkLanguage =
     this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
@@ -462,6 +455,13 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
     return;
   }
 
+  // Build list of dependencies.
+  std::vector<std::string> depends;
+  this->AppendLinkDepends(depends, linkLanguage);
+  if (!this->DeviceLinkObject.empty()) {
+    depends.push_back(this->DeviceLinkObject);
+  }
+
   // Create set of linking flags.
   std::string linkFlags;
   this->LocalGenerator->AppendFlags(linkFlags, extraFlags);

+ 3 - 5
Source/cmMakefileTargetGenerator.cxx

@@ -1387,7 +1387,7 @@ void cmMakefileTargetGenerator::AppendObjectDepends(
 }
 
 void cmMakefileTargetGenerator::AppendLinkDepends(
-  std::vector<std::string>& depends)
+  std::vector<std::string>& depends, const std::string& linkLanguage)
 {
   this->AppendObjectDepends(depends);
 
@@ -1411,10 +1411,8 @@ void cmMakefileTargetGenerator::AppendLinkDepends(
   }
 
   // Add user-specified dependencies.
-  if (const char* linkDepends =
-        this->GeneratorTarget->GetProperty("LINK_DEPENDS")) {
-    cmSystemTools::ExpandListArgument(linkDepends, depends);
-  }
+  this->GeneratorTarget->GetLinkDepends(depends, this->ConfigName,
+                                        linkLanguage);
 }
 
 std::string cmMakefileTargetGenerator::GetLinkRule(

+ 2 - 1
Source/cmMakefileTargetGenerator.h

@@ -128,7 +128,8 @@ protected:
   void AppendObjectDepends(std::vector<std::string>& depends);
 
   // Append link rule dependencies (objects, etc.).
-  void AppendLinkDepends(std::vector<std::string>& depends);
+  void AppendLinkDepends(std::vector<std::string>& depends,
+                         const std::string& linkLanguage);
 
   // Lookup the link rule for this target.
   std::string GetLinkRule(const std::string& linkRuleVar);

+ 2 - 2
Source/cmNinjaNormalTargetGenerator.cxx

@@ -625,7 +625,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement()
   outputs.push_back(targetOutputReal);
   // Compute specific libraries to link with.
   cmNinjaDeps explicitDeps = this->GetObjects();
-  cmNinjaDeps implicitDeps = this->ComputeLinkDeps();
+  cmNinjaDeps implicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage);
 
   std::string frameworkPath;
   std::string linkPath;
@@ -794,7 +794,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
 
   // Compute specific libraries to link with.
   cmNinjaDeps explicitDeps = this->GetObjects();
-  cmNinjaDeps implicitDeps = this->ComputeLinkDeps();
+  cmNinjaDeps implicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage);
 
   if (!this->DeviceLinkObject.empty()) {
     explicitDeps.push_back(this->DeviceLinkObject);

+ 7 - 8
Source/cmNinjaTargetGenerator.cxx

@@ -235,7 +235,8 @@ std::string cmNinjaTargetGenerator::ComputeIncludes(
   return includesString;
 }
 
-cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
+cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps(
+  const std::string& linkLanguage) const
 {
   // Static libraries never depend on other targets for linking.
   if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
@@ -270,13 +271,11 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
   }
 
   // Add user-specified dependencies.
-  if (const char* linkDepends =
-        this->GeneratorTarget->GetProperty("LINK_DEPENDS")) {
-    std::vector<std::string> linkDeps;
-    cmSystemTools::ExpandListArgument(linkDepends, linkDeps);
-    std::transform(linkDeps.begin(), linkDeps.end(),
-                   std::back_inserter(result), MapToNinjaPath());
-  }
+  std::vector<std::string> linkDeps;
+  this->GeneratorTarget->GetLinkDepends(linkDeps, this->ConfigName,
+                                        linkLanguage);
+  std::transform(linkDeps.begin(), linkDeps.end(), std::back_inserter(result),
+                 MapToNinjaPath());
 
   return result;
 }

+ 1 - 1
Source/cmNinjaTargetGenerator.h

@@ -95,7 +95,7 @@ protected:
   }
 
   /// @return the list of link dependency for the given target @a target.
-  cmNinjaDeps ComputeLinkDeps() const;
+  cmNinjaDeps ComputeLinkDeps(const std::string& linkLanguage) const;
 
   /// @return the source file path for the given @a source.
   std::string GetSourceFilePath(cmSourceFile const* source) const;

+ 11 - 0
Tests/BuildDepends/CMakeLists.txt

@@ -343,6 +343,17 @@ if(TEST_LINK_DEPENDS)
   ${linkdep}
 is not newer than dependency
   ${TEST_LINK_DEPENDS}
+")
+  endif()
+
+  set(linkdep2 ${BuildDepends_BINARY_DIR}/Project/linkdep2${CMAKE_EXECUTABLE_SUFFIX})
+  if(${linkdep2} IS_NEWER_THAN ${TEST_LINK_DEPENDS})
+    message("INTERFACE_LINK_DEPENDS worked")
+  else()
+    message(SEND_ERROR "INTERFACE_LINK_DEPENDS failed.  Executable
+  ${linkdep2}
+is not newer than dependency
+  ${TEST_LINK_DEPENDS}
 ")
   endif()
 endif()

+ 5 - 0
Tests/BuildDepends/Project/CMakeLists.txt

@@ -107,6 +107,11 @@ set_property(
 if(TEST_LINK_DEPENDS)
   add_executable(linkdep linkdep.cxx)
   set_property(TARGET linkdep PROPERTY LINK_DEPENDS ${TEST_LINK_DEPENDS})
+
+  add_library(foo_interface INTERFACE)
+  set_property(TARGET foo_interface PROPERTY INTERFACE_LINK_DEPENDS $<1:${TEST_LINK_DEPENDS}>)
+  add_executable(linkdep2 linkdep.cxx)
+  target_link_libraries(linkdep2 PRIVATE foo_interface)
 endif()
 
 add_library(link_depends_no_shared_lib SHARED link_depends_no_shared_lib.c

+ 14 - 0
Tests/ExportImport/Export/CMakeLists.txt

@@ -607,3 +607,17 @@ target_link_options(testLinkOptions INTERFACE INTERFACE_FLAG)
 install(TARGETS testLinkOptions
         EXPORT RequiredExp DESTINATION lib)
 export(TARGETS testLinkOptions NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake)
+
+
+#------------------------------------------------------------------------------
+# test export of INTERFACE_LINK_DEPENDS
+if(CMAKE_GENERATOR MATCHES "Make|Ninja")
+  add_library(testLinkDepends INTERFACE)
+  set_property(TARGET testLinkDepends PROPERTY INTERFACE_LINK_DEPENDS
+    $<BUILD_INTERFACE:BUILD_LINK_DEPENDS>
+    $<INSTALL_INTERFACE:INSTALL_LINK_DEPENDS>)
+
+  install(TARGETS testLinkDepends
+    EXPORT RequiredExp DESTINATION lib)
+  export(TARGETS testLinkDepends NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake)
+endif()

+ 7 - 0
Tests/ExportImport/Import/A/CMakeLists.txt

@@ -477,3 +477,10 @@ endif()
 # check that imported libraries have the expected INTERFACE_LINK_OPTIONS property
 checkForProperty(bld_testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG")
 checkForProperty(Req::testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG")
+
+#---------------------------------------------------------------------------------
+# check that imported libraries have the expected INTERFACE_LINK_DEPENDS property
+if(CMAKE_GENERATOR MATCHES "Make|Ninja")
+  checkForProperty(bld_testLinkDepends "INTERFACE_LINK_DEPENDS" "BUILD_LINK_DEPENDS")
+  checkForProperty(Req::testLinkDepends "INTERFACE_LINK_DEPENDS" "${CMAKE_INSTALL_PREFIX}/INSTALL_LINK_DEPENDS")
+endif()