Ver código fonte

export: Allow export with LINK_ONLY library dependencies

Martin Duffy 6 meses atrás
pai
commit
ebe487ea81

+ 62 - 23
Source/cmExportPackageInfoGenerator.cxx

@@ -2,6 +2,7 @@
    file LICENSE.rst or https://cmake.org/licensing for details.  */
 #include "cmExportPackageInfoGenerator.h"
 
+#include <cstddef>
 #include <memory>
 #include <set>
 #include <utility>
@@ -210,13 +211,15 @@ bool cmExportPackageInfoGenerator::GenerateInterfaceProperties(
 }
 
 namespace {
-bool forbidGeneratorExpressions(std::string const& propertyName,
-                                std::string const& propertyValue,
-                                cmGeneratorTarget const* target)
+bool ForbidGeneratorExpressions(
+  cmGeneratorTarget const* target, std::string const& propertyName,
+  std::string const& propertyValue, std::string& evaluatedValue,
+  std::map<std::string, std::vector<std::string>>& allowList)
 {
-  std::string const& evaluatedValue = cmGeneratorExpression::Preprocess(
-    propertyValue, cmGeneratorExpression::StripAllGeneratorExpressions);
-  if (evaluatedValue != propertyValue) {
+  size_t const allowedExpressions = allowList.size();
+  evaluatedValue = cmGeneratorExpression::Collect(propertyValue, allowList);
+  if (evaluatedValue != propertyValue &&
+      allowList.size() > allowedExpressions) {
     target->Makefile->IssueMessage(
       MessageType::FATAL_ERROR,
       cmStrCat("Property \"", propertyName, "\" of target \"",
@@ -224,8 +227,32 @@ bool forbidGeneratorExpressions(std::string const& propertyName,
                "\" contains a generator expression. This is not allowed."));
     return false;
   }
+  // Forbid Nested Generator Expressions
+  for (auto const& genexp : allowList) {
+    for (auto const& value : genexp.second) {
+      if (value.find("$<") != std::string::npos) {
+        target->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat(
+            "$<", genexp.first, ":...> expression in \"", propertyName,
+            "\" of target \"", target->GetName(),
+            "\" contains a generator expression. This is not allowed."));
+        return false;
+      }
+    }
+  }
   return true;
 }
+
+bool ForbidGeneratorExpressions(cmGeneratorTarget const* target,
+                                std::string const& propertyName,
+                                std::string const& propertyValue)
+{
+  std::map<std::string, std::vector<std::string>> allowList;
+  std::string evaluatedValue;
+  return ForbidGeneratorExpressions(target, propertyName, propertyValue,
+                                    evaluatedValue, allowList);
+}
 }
 
 bool cmExportPackageInfoGenerator::NoteLinkedTarget(
@@ -317,31 +344,43 @@ void cmExportPackageInfoGenerator::GenerateInterfaceLinkProperties(
     return;
   }
 
-  // TODO: Support $<LINK_ONLY>.
-  if (!forbidGeneratorExpressions(iter->first, iter->second, target)) {
+  // Extract any $<LINK_ONLY:...> from the link libraries, and assert that no
+  // other generator expressions are present.
+  std::map<std::string, std::vector<std::string>> allowList = { { "LINK_ONLY",
+                                                                  {} } };
+  std::string interfaceLinkLibraries;
+  if (!ForbidGeneratorExpressions(target, iter->first, iter->second,
+                                  interfaceLinkLibraries, allowList)) {
     result = false;
     return;
   }
 
-  std::vector<std::string> buildRequires;
-  // std::vector<std::string> linkRequires; TODO
   std::vector<std::string> linkLibraries;
+  std::vector<std::string> linkRequires;
+  std::vector<std::string> buildRequires;
 
-  for (auto const& name : cmList{ iter->second }) {
-    auto const& ti = this->LinkTargets.find(name);
-    if (ti != this->LinkTargets.end()) {
-      if (ti->second.empty()) {
-        result = false;
+  auto addLibraries = [this, &linkLibraries,
+                       &result](std::vector<std::string> const& names,
+                                std::vector<std::string>& output) -> void {
+    for (auto const& name : names) {
+      auto const& ti = this->LinkTargets.find(name);
+      if (ti != this->LinkTargets.end()) {
+        if (ti->second.empty()) {
+          result = false;
+        } else {
+          output.emplace_back(ti->second);
+        }
       } else {
-        buildRequires.emplace_back(ti->second);
+        linkLibraries.emplace_back(name);
       }
-    } else {
-      linkLibraries.emplace_back(name);
     }
-  }
+  };
+
+  addLibraries(allowList["LINK_ONLY"], linkRequires);
+  addLibraries(cmList{ interfaceLinkLibraries }, buildRequires);
 
   buildArray(component, "requires", buildRequires);
-  // buildArray(component, "link_requires", linkRequires); TODO
+  buildArray(component, "link_requires", linkRequires);
   buildArray(component, "link_libraries", linkLibraries);
 }
 
@@ -354,7 +393,7 @@ void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures(
     return;
   }
 
-  if (!forbidGeneratorExpressions(iter->first, iter->second, target)) {
+  if (!ForbidGeneratorExpressions(target, iter->first, iter->second)) {
     result = false;
     return;
   }
@@ -383,7 +422,7 @@ void cmExportPackageInfoGenerator::GenerateInterfaceCompileDefines(
   }
 
   // TODO: Support language-specific defines.
-  if (!forbidGeneratorExpressions(iter->first, iter->second, target)) {
+  if (!ForbidGeneratorExpressions(target, iter->first, iter->second)) {
     result = false;
     return;
   }
@@ -414,7 +453,7 @@ void cmExportPackageInfoGenerator::GenerateInterfaceListProperty(
     return;
   }
 
-  if (!forbidGeneratorExpressions(prop, iter->second, target)) {
+  if (!ForbidGeneratorExpressions(target, prop, iter->second)) {
     result = false;
     return;
   }

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/LinkInterfaceGeneratorExpression-result.txt

@@ -0,0 +1 @@
+1

+ 6 - 0
Tests/RunCMake/ExportPackageInfo/LinkInterfaceGeneratorExpression-stderr.txt

@@ -0,0 +1,6 @@
+CMake Error in CMakeLists.txt:
+  Property "INTERFACE_LINK_LIBRARIES" of target "bar" contains a generator
+  expression.  This is not allowed.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 11 - 0
Tests/RunCMake/ExportPackageInfo/LinkInterfaceGeneratorExpression.cmake

@@ -0,0 +1,11 @@
+project(LinkInterfaceGeneratorExpression CXX)
+
+add_library(foo foo.cxx)
+add_library(bar foo.cxx)
+target_link_libraries(bar $<1:foo>)
+
+install(TARGETS foo EXPORT foo)
+export(EXPORT foo PACKAGE_INFO foo)
+
+install(TARGETS bar EXPORT bar)
+export(EXPORT bar PACKAGE_INFO bar)

+ 12 - 0
Tests/RunCMake/ExportPackageInfo/LinkOnly-check.cmake

@@ -0,0 +1,12 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/LinkOnly-build")
+
+file(READ "${out_dir}/bar.cps" content)
+string(JSON component GET "${content}" "components" "bar")
+expect_array("${component}" 2 "link_requires")
+expect_value("${component}" "foo:linkOnlyOne" "link_requires" 0)
+expect_value("${component}" "foo:linkOnlyTwo" "link_requires" 1)
+expect_array("${component}" 1 "requires")
+expect_value("${component}" "foo:foo" "requires" 0)
+expect_missing("${component}" "foo:foo" "link_libraries")

+ 13 - 0
Tests/RunCMake/ExportPackageInfo/LinkOnly.cmake

@@ -0,0 +1,13 @@
+project(LinkOnly CXX)
+
+add_library(linkOnlyOne foo.cxx)
+add_library(linkOnlyTwo foo.cxx)
+add_library(foo foo.cxx)
+add_library(bar foo.cxx)
+target_link_libraries(bar $<LINK_ONLY:linkOnlyOne> $<LINK_ONLY:linkOnlyTwo> foo)
+
+install(TARGETS foo linkOnlyOne linkOnlyTwo EXPORT foo)
+export(EXPORT foo PACKAGE_INFO foo)
+
+install(TARGETS bar EXPORT bar)
+export(EXPORT bar PACKAGE_INFO bar)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/LinkOnlyRecursive-result.txt

@@ -0,0 +1 @@
+1

+ 6 - 0
Tests/RunCMake/ExportPackageInfo/LinkOnlyRecursive-stderr.txt

@@ -0,0 +1,6 @@
+CMake Error in CMakeLists.txt:
+  \$<LINK_ONLY:...> expression in "INTERFACE_LINK_LIBRARIES" of target "bar"
+  contains a generator expression.  This is not allowed.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 11 - 0
Tests/RunCMake/ExportPackageInfo/LinkOnlyRecursive.cmake

@@ -0,0 +1,11 @@
+project(LinkOnly CXX)
+
+add_library(foo foo.cxx)
+add_library(bar foo.cxx)
+target_link_libraries(bar $<LINK_ONLY:$<LINK_ONLY:foo>>)
+
+install(TARGETS foo EXPORT foo)
+export(EXPORT foo PACKAGE_INFO foo)
+
+install(TARGETS bar EXPORT bar)
+export(EXPORT bar PACKAGE_INFO bar)

+ 3 - 0
Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake

@@ -24,6 +24,8 @@ run_cmake(ReferencesWronglyImportedTarget)
 run_cmake(ReferencesWronglyNamespacedTarget)
 run_cmake(DependsMultipleDifferentNamespace)
 run_cmake(DependsMultipleDifferentSets)
+run_cmake(LinkInterfaceGeneratorExpression)
+run_cmake(LinkOnlyRecursive)
 
 # Test functionality
 run_cmake(Appendix)
@@ -35,3 +37,4 @@ run_cmake(LowerCaseFile)
 run_cmake(Requirements)
 run_cmake(TargetTypes)
 run_cmake(DependsMultiple)
+run_cmake(LinkOnly)