Browse Source

Link properties: must be transitive over private dependency on static library

Fixes: #20022
Marc Chevrier 5 years ago
parent
commit
bbba701899
25 changed files with 192 additions and 18 deletions
  1. 1 0
      Help/manual/cmake-policies.7.rst
  2. 24 0
      Help/policy/CMP0099.rst
  3. 8 0
      Help/release/dev/Link-properties-transitive.rst
  4. 22 14
      Source/cmGeneratorTarget.cxx
  5. 4 2
      Source/cmGeneratorTarget.h
  6. 7 2
      Source/cmPolicies.h
  7. 1 0
      Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
  8. 4 0
      Tests/RunCMake/target_link_directories/CMP0099-NEW-basic-check.cmake
  9. 1 0
      Tests/RunCMake/target_link_directories/CMP0099-NEW-basic-result.txt
  10. 4 0
      Tests/RunCMake/target_link_directories/CMP0099-NEW.cmake
  11. 4 0
      Tests/RunCMake/target_link_directories/CMP0099-OLD-basic-check.cmake
  12. 1 0
      Tests/RunCMake/target_link_directories/CMP0099-OLD-basic-result.txt
  13. 4 0
      Tests/RunCMake/target_link_directories/CMP0099-OLD.cmake
  14. 14 0
      Tests/RunCMake/target_link_directories/CMP0099.cmake
  15. 30 0
      Tests/RunCMake/target_link_directories/RunCMakeTest.cmake
  16. 4 0
      Tests/RunCMake/target_link_directories/exe.c
  17. 7 0
      Tests/RunCMake/target_link_directories/lib.c
  18. 4 0
      Tests/RunCMake/target_link_options/CMP0099-NEW-basic-check.cmake
  19. 1 0
      Tests/RunCMake/target_link_options/CMP0099-NEW-basic-result.txt
  20. 4 0
      Tests/RunCMake/target_link_options/CMP0099-NEW.cmake
  21. 4 0
      Tests/RunCMake/target_link_options/CMP0099-OLD-basic-check.cmake
  22. 1 0
      Tests/RunCMake/target_link_options/CMP0099-OLD-basic-result.txt
  23. 4 0
      Tests/RunCMake/target_link_options/CMP0099-OLD.cmake
  24. 16 0
      Tests/RunCMake/target_link_options/CMP0099.cmake
  25. 18 0
      Tests/RunCMake/target_link_options/RunCMakeTest.cmake

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.17
 .. toctree::
    :maxdepth: 1
 
+   CMP0099: Link properties are transitive over private dependency on static libraries. </policy/CMP0099>
    CMP0098: FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing. </policy/CMP0098>
 
 Policies Introduced by CMake 3.16

+ 24 - 0
Help/policy/CMP0099.rst

@@ -0,0 +1,24 @@
+CMP0099
+-------
+
+Target link properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
+:prop_tgt:`INTERFACE_LINK_DIRECTORIES` and :prop_tgt:`INTERFACE_LINK_DEPENDS`
+are now transitive over private dependencies of static libraries.
+
+In CMake 3.16 and below the interface link properties attached to libraries
+are not propagated for private dependencies of static libraries.
+Only the libraries themselves are propagated to link the dependent binary.
+CMake 3.17 and later prefer to propagate all interface link properties.
+This policy provides compatibility for projects that have not been updated
+to expect the new behavior.
+
+The ``OLD`` behavior for this policy is to not propagate interface link
+properties. The ``NEW`` behavior of this policy is to propagate interface link
+properties.
+
+This policy was introduced in CMake version 3.17.  Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt

+ 8 - 0
Help/release/dev/Link-properties-transitive.rst

@@ -0,0 +1,8 @@
+Link-properties-transitive
+--------------------------
+
+* Target link properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
+  :prop_tgt:`INTERFACE_LINK_DIRECTORIES` and
+  :prop_tgt:`INTERFACE_LINK_DEPENDS` are now transitive over private
+  dependency on static libraries.
+  See policy :policy:`CMP0099`.

+ 22 - 14
Source/cmGeneratorTarget.cxx

@@ -1116,7 +1116,8 @@ bool cmGeneratorTarget::GetPropertyAsBool(const std::string& prop) const
 }
 
 bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
-  std::string const& prop, cmGeneratorExpressionContext* context) const
+  std::string const& prop, cmGeneratorExpressionContext* context,
+  bool usage_requirements_only) const
 {
   std::string const key = prop + '@' + context->Config;
   auto i = this->MaybeInterfacePropertyExists.find(key);
@@ -1135,7 +1136,7 @@ bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
         context->HeadTarget ? context->HeadTarget : this;
       if (cmLinkInterfaceLibraries const* iface =
             this->GetLinkInterfaceLibraries(context->Config, headTarget,
-                                            true)) {
+                                            usage_requirements_only)) {
         if (iface->HadHeadSensitiveCondition) {
           // With a different head target we may get to a library with
           // this interface property.
@@ -1145,7 +1146,8 @@ bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
           // head target, so we can follow them.
           for (cmLinkItem const& lib : iface->Libraries) {
             if (lib.Target &&
-                lib.Target->MaybeHaveInterfaceProperty(prop, context)) {
+                lib.Target->MaybeHaveInterfaceProperty(
+                  prop, context, usage_requirements_only)) {
               maybeInterfaceProp = true;
               break;
             }
@@ -1159,12 +1161,14 @@ bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
 
 std::string cmGeneratorTarget::EvaluateInterfaceProperty(
   std::string const& prop, cmGeneratorExpressionContext* context,
-  cmGeneratorExpressionDAGChecker* dagCheckerParent) const
+  cmGeneratorExpressionDAGChecker* dagCheckerParent,
+  bool usage_requirements_only) const
 {
   std::string result;
 
   // If the property does not appear transitively at all, we are done.
-  if (!this->MaybeHaveInterfaceProperty(prop, context)) {
+  if (!this->MaybeHaveInterfaceProperty(prop, context,
+                                        usage_requirements_only)) {
     return result;
   }
 
@@ -1196,8 +1200,8 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty(
       p, context->LG, context, headTarget, &dagChecker, this);
   }
 
-  if (cmLinkInterfaceLibraries const* iface =
-        this->GetLinkInterfaceLibraries(context->Config, headTarget, true)) {
+  if (cmLinkInterfaceLibraries const* iface = this->GetLinkInterfaceLibraries(
+        context->Config, headTarget, usage_requirements_only)) {
     for (cmLinkItem const& lib : iface->Libraries) {
       // Broken code can have a target in its own link interface.
       // Don't follow such link interface entries so as not to create a
@@ -1240,7 +1244,8 @@ void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
                          std::string const& config, std::string const& prop,
                          std::string const& lang,
                          cmGeneratorExpressionDAGChecker* dagChecker,
-                         std::vector<EvaluatedTargetPropertyEntry>& entries)
+                         std::vector<EvaluatedTargetPropertyEntry>& entries,
+                         bool usage_requirements_only = true)
 {
   if (cmLinkImplementationLibraries const* impl =
         headTarget->GetLinkImplementationLibraries(config)) {
@@ -1253,9 +1258,9 @@ void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
         cmGeneratorExpressionContext context(
           headTarget->GetLocalGenerator(), config, false, headTarget,
           headTarget, true, lib.Backtrace, lang);
-        cmExpandList(
-          lib.Target->EvaluateInterfaceProperty(prop, &context, dagChecker),
-          ee.Values);
+        cmExpandList(lib.Target->EvaluateInterfaceProperty(
+                       prop, &context, dagChecker, usage_requirements_only),
+                     ee.Values);
         ee.ContextDependent = context.HadContextSensitiveCondition;
         entries.emplace_back(std::move(ee));
       }
@@ -3663,7 +3668,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
                                   this->LinkOptionsEntries);
 
   AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language,
-                      &dagChecker, entries);
+                      &dagChecker, entries,
+                      this->GetPolicyStatusCMP0099() != cmPolicies::NEW);
 
   processOptions(this, entries, result, uniqueOptions, debugOptions,
                  "link options", OptionsParse::Shell);
@@ -3918,7 +3924,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories(
                                   this->LinkDirectoriesEntries);
 
   AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", language,
-                      &dagChecker, entries);
+                      &dagChecker, entries,
+                      this->GetPolicyStatusCMP0099() != cmPolicies::NEW);
 
   processLinkDirectories(this, entries, result, uniqueDirectories,
                          debugDirectories);
@@ -3956,7 +3963,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
     }
   }
   AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language,
-                      &dagChecker, entries);
+                      &dagChecker, entries,
+                      this->GetPolicyStatusCMP0099() != cmPolicies::NEW);
 
   processOptions(this, entries, result, uniqueOptions, false, "link depends",
                  OptionsParse::None);

+ 4 - 2
Source/cmGeneratorTarget.h

@@ -707,7 +707,8 @@ public:
 
   std::string EvaluateInterfaceProperty(
     std::string const& prop, cmGeneratorExpressionContext* context,
-    cmGeneratorExpressionDAGChecker* dagCheckerParent) const;
+    cmGeneratorExpressionDAGChecker* dagCheckerParent,
+    bool usage_requirements_only = true) const;
 
   bool HaveInstallTreeRPATH(const std::string& config) const;
 
@@ -886,7 +887,8 @@ private:
 
   mutable std::unordered_map<std::string, bool> MaybeInterfacePropertyExists;
   bool MaybeHaveInterfaceProperty(std::string const& prop,
-                                  cmGeneratorExpressionContext* context) const;
+                                  cmGeneratorExpressionContext* context,
+                                  bool usage_requirements_only) const;
 
   using TargetPropertyEntryVector =
     std::vector<std::unique_ptr<TargetPropertyEntry>>;

+ 7 - 2
Source/cmPolicies.h

@@ -293,7 +293,11 @@ class cmMakefile;
          3, 16, 0, cmPolicies::WARN)                                          \
   SELECT(POLICY, CMP0098,                                                     \
          "FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing.", 3, \
-         17, 0, cmPolicies::WARN)
+         17, 0, cmPolicies::WARN)                                             \
+  SELECT(POLICY, CMP0099,                                                     \
+         "Link properties are transitive over private dependency on static "  \
+         "libraries.",                                                        \
+         3, 17, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -322,7 +326,8 @@ class cmMakefile;
   F(CMP0076)                                                                  \
   F(CMP0081)                                                                  \
   F(CMP0083)                                                                  \
-  F(CMP0095)
+  F(CMP0095)                                                                  \
+  F(CMP0099)
 
 /** \class cmPolicies
  * \brief Handles changes in CMake behavior and policies

+ 1 - 0
Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt

@@ -28,6 +28,7 @@
    \* CMP0081
    \* CMP0083
    \* CMP0095
+   \* CMP0099
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/target_link_directories/CMP0099-NEW-basic-check.cmake

@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "DIR_INTERFACE")
+  string (APPEND RunCMake_TEST_FAILED "\nNot found expected 'DIR_INTERFACE'.")
+endif()

+ 1 - 0
Tests/RunCMake/target_link_directories/CMP0099-NEW-basic-result.txt

@@ -0,0 +1 @@
+.*

+ 4 - 0
Tests/RunCMake/target_link_directories/CMP0099-NEW.cmake

@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0099 NEW)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/CMP0099.cmake)

+ 4 - 0
Tests/RunCMake/target_link_directories/CMP0099-OLD-basic-check.cmake

@@ -0,0 +1,4 @@
+
+if (actual_stdout MATCHES "DIR_INTERFACE")
+  string (APPEND RunCMake_TEST_FAILED "\nFound unexpected 'DIR_INTERFACE'.")
+endif()

+ 1 - 0
Tests/RunCMake/target_link_directories/CMP0099-OLD-basic-result.txt

@@ -0,0 +1 @@
+.*

+ 4 - 0
Tests/RunCMake/target_link_directories/CMP0099-OLD.cmake

@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0099 OLD)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/CMP0099.cmake)

+ 14 - 0
Tests/RunCMake/target_link_directories/CMP0099.cmake

@@ -0,0 +1,14 @@
+
+enable_language(C)
+
+set(CMAKE_VERBOSE_MAKEFILE TRUE)
+set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE)
+
+add_library(LinkDirs_interface INTERFACE)
+target_link_directories (LinkDirs_interface INTERFACE "/DIR_INTERFACE"})
+
+add_library(LinkDirs_static STATIC lib.c)
+target_link_libraries (LinkDirs_static PRIVATE LinkDirs_interface)
+
+add_executable(LinkDirs_exe exe.c)
+target_link_libraries (LinkDirs_exe PRIVATE LinkDirs_static)

+ 30 - 0
Tests/RunCMake/target_link_directories/RunCMakeTest.cmake

@@ -1,3 +1,33 @@
 include(RunCMake)
 
+macro(run_cmake_target test subtest target)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${test}-${subtest} ${CMAKE_COMMAND} --build . --target ${target} ${ARGN})
+
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+endmacro()
+
 run_cmake(empty_keyword_args)
+
+if(RunCMake_GENERATOR MATCHES "(Ninja|Makefiles)" AND
+    NOT RunCMake_GENERATOR MATCHES "(NMake|Borland)")
+  set(RunCMake_TEST_OUTPUT_MERGE TRUE)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+  endif()
+  if (RunCMake_GENERATOR MATCHES "Ninja")
+    set(VERBOSE -- -v)
+  endif()
+
+  run_cmake(CMP0099-NEW)
+  run_cmake_target(CMP0099-NEW basic LinkDirs_exe ${VERBOSE})
+
+
+  run_cmake(CMP0099-OLD)
+  run_cmake_target(CMP0099-OLD basic LinkDirs_exe ${VERBOSE})
+
+  unset(RunCMake_TEST_OPTIONS)
+  unset(RunCMake_TEST_OUTPUT_MERGE)
+endif()

+ 4 - 0
Tests/RunCMake/target_link_directories/exe.c

@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}

+ 7 - 0
Tests/RunCMake/target_link_directories/lib.c

@@ -0,0 +1,7 @@
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+  int flags_lib(void)
+{
+  return 0;
+}

+ 4 - 0
Tests/RunCMake/target_link_options/CMP0099-NEW-basic-check.cmake

@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "BADFLAG_INTERFACE")
+  string (APPEND RunCMake_TEST_FAILED "\nNot found expected 'BADFLAG_INTERFACE'.")
+endif()

+ 1 - 0
Tests/RunCMake/target_link_options/CMP0099-NEW-basic-result.txt

@@ -0,0 +1 @@
+.*

+ 4 - 0
Tests/RunCMake/target_link_options/CMP0099-NEW.cmake

@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0099 NEW)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/CMP0099.cmake)

+ 4 - 0
Tests/RunCMake/target_link_options/CMP0099-OLD-basic-check.cmake

@@ -0,0 +1,4 @@
+
+if (actual_stdout MATCHES "BADFLAG_INTERFACE")
+  string (APPEND RunCMake_TEST_FAILED "\nFound unexpected 'BADFLAG_INTERFACE'.")
+endif()

+ 1 - 0
Tests/RunCMake/target_link_options/CMP0099-OLD-basic-result.txt

@@ -0,0 +1 @@
+.*

+ 4 - 0
Tests/RunCMake/target_link_options/CMP0099-OLD.cmake

@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0099 OLD)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/CMP0099.cmake)

+ 16 - 0
Tests/RunCMake/target_link_options/CMP0099.cmake

@@ -0,0 +1,16 @@
+
+enable_language(C)
+
+set(obj "${CMAKE_C_OUTPUT_EXTENSION}")
+if(BORLAND)
+  set(pre -)
+endif()
+
+add_library(LinkOptions_interface INTERFACE)
+target_link_options (LinkOptions_interface INTERFACE ${pre}BADFLAG_INTERFACE${obj})
+
+add_library(LinkOptions_static STATIC LinkOptionsLib.c)
+target_link_libraries (LinkOptions_static PRIVATE LinkOptions_interface)
+
+add_executable(LinkOptions_exe LinkOptionsExe.c)
+target_link_libraries (LinkOptions_exe PRIVATE LinkOptions_static)

+ 18 - 0
Tests/RunCMake/target_link_options/RunCMakeTest.cmake

@@ -41,3 +41,21 @@ if(RunCMake_GENERATOR MATCHES "(Ninja|Makefile)")
 endif()
 
 run_cmake(empty_keyword_args)
+
+if (NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
+  # Intel compiler does not reject bad flags or objects!
+  set(RunCMake_TEST_OUTPUT_MERGE TRUE)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+  endif()
+
+  run_cmake(CMP0099-NEW)
+  run_cmake_target(CMP0099-NEW basic LinkOptions_exe)
+
+
+  run_cmake(CMP0099-OLD)
+  run_cmake_target(CMP0099-OLD basic LinkOptions_exe)
+
+  unset(RunCMake_TEST_OPTIONS)
+  unset(RunCMake_TEST_OUTPUT_MERGE)
+endif()