Przeglądaj źródła

Add the $<LINKED:...> generator expression.

This is both a short form of using a TARGET_DEFINED expression
together with a TARGET_PROPERTY definition, and a way to strip
non-target content from interface properties when exporting.
Stephen Kelly 12 lat temu
rodzic
commit
0b92602b81

+ 8 - 0
Source/cmDocumentGeneratorExpressions.h

@@ -51,6 +51,14 @@
   "on the target tgt.\n"                                                \
   "Note that tgt is not added as a dependency of the target this "      \
   "expression is evaluated on.\n"                                       \
+  "  $<LINKED:item>            = An empty string if item is not a "     \
+  "target.  If item is a target then the "                              \
+  "INTERFACE_INCLUDE_DIRECTORIES or INTERFACE_COMPILE_DEFINITIONS "     \
+  "content is read from the target.  "                                  \
+  "This generator expression can only be used in evaluation of the "    \
+  "INCLUDE_DIRECTORIES or COMPILE_DEFINITIONS property.  Note that "    \
+  "this expression is for internal use and may be changed or removed "  \
+  "in the future.\n"                                                    \
   "  $<TARGET_POLICY:pol>          = '1' if the policy was NEW when "   \
   "the 'head' target was created, else '0'.  If the policy was not "    \
   "set, the warning message for the policy will be emitted.  This "     \

+ 60 - 6
Source/cmExportFileGenerator.cxx

@@ -25,6 +25,8 @@
 
 #include <cmsys/auto_ptr.hxx>
 
+#include "assert.h"
+
 //----------------------------------------------------------------------------
 cmExportFileGenerator::cmExportFileGenerator()
 {
@@ -160,7 +162,7 @@ void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName,
                                                            preprocessRule);
     if (!prepro.empty())
       {
-      this->ResolveTargetsInGeneratorExpressions(prepro, target,
+      this->ResolveTargetsInGeneratorExpressions(prepro, target, propName,
                                                  missingTargets);
       properties[outputName] = prepro;
       }
@@ -324,13 +326,14 @@ static bool isGeneratorExpression(const std::string &lib)
 void
 cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
                                     std::string &input,
-                                    cmTarget* target,
+                                    cmTarget* target, const char *propName,
                                     std::vector<std::string> &missingTargets,
                                     FreeTargetsReplace replace)
 {
   if (replace == NoReplaceFreeTargets)
     {
-    this->ResolveTargetsInGeneratorExpression(input, target, missingTargets);
+    this->ResolveTargetsInGeneratorExpression(input, target, propName,
+                                              missingTargets);
     return;
     }
   std::vector<std::string> parts;
@@ -349,7 +352,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
       {
       this->ResolveTargetsInGeneratorExpression(
                                     *li,
-                                    target,
+                                    target, propName,
                                     missingTargets);
       }
     input += sep + *li;
@@ -361,7 +364,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
 void
 cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
                                     std::string &input,
-                                    cmTarget* target,
+                                    cmTarget* target, const char *propName,
                                     std::vector<std::string> &missingTargets)
 {
   std::string::size_type pos = 0;
@@ -396,6 +399,57 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
     }
 
   std::string errorString;
+  pos = 0;
+  lastPos = pos;
+  while((pos = input.find("$<LINKED:", lastPos)) != input.npos)
+    {
+    std::string::size_type nameStartPos = pos + sizeof("$<LINKED:") - 1;
+    std::string::size_type endPos = input.find(">", nameStartPos);
+    if (endPos == input.npos)
+      {
+      errorString = "$<LINKED:...> expression incomplete";
+      break;
+      }
+    std::string targetName = input.substr(nameStartPos,
+                                                endPos - nameStartPos);
+    if(targetName.find("$<") != input.npos)
+      {
+      errorString = "$<LINKED:...> requires its parameter to be a "
+                    "literal.";
+      break;
+      }
+    if (this->AddTargetNamespace(targetName, target, missingTargets))
+      {
+      assert(propName); // The link libraries strings will
+                        // never contain $<LINKED>
+      std::string replacement = "$<TARGET_PROPERTY:"
+                              + targetName + "," + propName;
+      input.replace(pos, endPos - pos, replacement);
+      lastPos = pos + replacement.size() + 1;
+      }
+    else
+      {
+      if (pos != 0)
+        {
+        if (input[pos - 1] == ';')
+          {
+          --pos;
+          }
+        }
+      else if (input[endPos + 1] == ';')
+        {
+        ++endPos;
+        }
+      input.replace(pos, endPos - pos + 1, "");
+      lastPos = pos;
+      }
+    }
+  if (!errorString.empty())
+    {
+    mf->IssueMessage(cmake::FATAL_ERROR, errorString);
+    return;
+    }
+
   pos = 0;
   lastPos = pos;
   while((pos = input.find("$<TARGET_NAME:", lastPos)) != input.npos)
@@ -491,7 +545,7 @@ cmExportFileGenerator
                                                          preprocessRule);
   if (!prepro.empty())
     {
-    this->ResolveTargetsInGeneratorExpressions(prepro, target,
+    this->ResolveTargetsInGeneratorExpressions(prepro, target, 0,
                                                missingTargets,
                                                ReplaceFreeTargets);
     properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;

+ 2 - 2
Source/cmExportFileGenerator.h

@@ -119,7 +119,7 @@ protected:
   };
 
   void ResolveTargetsInGeneratorExpressions(std::string &input,
-                          cmTarget* target,
+                          cmTarget* target, const char *propName,
                           std::vector<std::string> &missingTargets,
                           FreeTargetsReplace replace = NoReplaceFreeTargets);
 
@@ -150,7 +150,7 @@ private:
                           std::vector<std::string> &missingTargets);
 
   void ResolveTargetsInGeneratorExpression(std::string &input,
-                                    cmTarget* target,
+                                    cmTarget* target, const char *propName,
                                     std::vector<std::string> &missingTargets);
 
   virtual void ReplaceInstallPrefix(std::string &input);

+ 85 - 0
Source/cmGeneratorExpressionEvaluator.cxx

@@ -637,6 +637,89 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode
 
 } installPrefixNode;
 
+//----------------------------------------------------------------------------
+static const struct LinkedNode : public cmGeneratorExpressionNode
+{
+  LinkedNode() {}
+
+  virtual bool GeneratesContent() const { return true; }
+  virtual int NumExpectedParameters() const { return 1; }
+  virtual bool RequiresLiteralInput() const { return true; }
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content,
+                       cmGeneratorExpressionDAGChecker *dagChecker) const
+  {
+    if (dagChecker->EvaluatingIncludeDirectories())
+      {
+      return this->GetInterfaceProperty(parameters.front(),
+                                        "INCLUDE_DIRECTORIES",
+                                        context, content, dagChecker);
+      }
+    if (dagChecker->EvaluatingCompileDefinitions())
+      {
+      return this->GetInterfaceProperty(parameters.front(),
+                                        "COMPILE_DEFINITIONS",
+                                        context, content, dagChecker);
+      }
+
+    reportError(context, content->GetOriginalExpression(),
+                "$<LINKED:...> may only be used in INCLUDE_DIRECTORIES and "
+                "COMPILE_DEFINITIONS properties.");
+
+    return std::string();
+  }
+
+private:
+  std::string GetInterfaceProperty(const std::string &item,
+                      const std::string &prop,
+                      cmGeneratorExpressionContext *context,
+                      const GeneratorExpressionContent *content,
+                      cmGeneratorExpressionDAGChecker *dagCheckerParent) const
+  {
+    cmTarget *target = context->CurrentTarget
+                              ->GetMakefile()->FindTargetToUse(item.c_str());
+    if (!target)
+      {
+      return std::string();
+      }
+    std::string propertyName = "INTERFACE_" + prop;
+    const char *propContent = target->GetProperty(propertyName.c_str());
+    if (!propContent)
+      {
+      return std::string();
+      }
+
+    cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace,
+                                               target->GetName(),
+                                               propertyName,
+                                               content,
+                                               dagCheckerParent);
+
+    switch (dagChecker.check())
+      {
+    case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
+      dagChecker.reportError(context, content->GetOriginalExpression());
+      return std::string();
+    case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
+      // No error. We just skip cyclic references.
+      return std::string();
+    case cmGeneratorExpressionDAGChecker::DAG:
+      break;
+      }
+
+    cmGeneratorExpression ge(context->Backtrace);
+    return ge.Parse(propContent)->Evaluate(context->Makefile,
+                        context->Config,
+                        context->Quiet,
+                        context->HeadTarget,
+                        target,
+                        &dagChecker);
+  }
+
+} linkedNode;
+
 //----------------------------------------------------------------------------
 template<bool linker, bool soname>
 struct TargetFilesystemArtifactResultCreator
@@ -874,6 +957,8 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier)
     return &targetDefinedNode;
   else if (identifier == "INSTALL_PREFIX")
     return &installPrefixNode;
+  else if (identifier == "LINKED")
+    return &linkedNode;
   return 0;
 
 }

+ 6 - 0
Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt

@@ -16,9 +16,15 @@ add_executable(consumer
   "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp"
 )
 
+add_library(linked UNKNOWN IMPORTED)
+set_property(TARGET linked PROPERTY
+  INTERFACE_COMPILE_DEFINITIONS "MY_LINKED_DEFINE")
+
+
 target_compile_definitions(consumer
   PRIVATE $<TARGET_PROPERTY:target_compile_definitions,INTERFACE_COMPILE_DEFINITIONS>
   $<$<TARGET_DEFINED:notdefined>:SHOULD_NOT_BE_DEFINED>
   $<$<TARGET_DEFINED:target_compile_definitions>:SHOULD_BE_DEFINED>
+  $<LINKED:linked>
   -DDASH_D_DEFINE
 )

+ 4 - 0
Tests/CMakeCommands/target_compile_definitions/consumer.cpp

@@ -23,4 +23,8 @@
 #error Expected DASH_D_DEFINE
 #endif
 
+#ifndef MY_LINKED_DEFINE
+#error Expected MY_LINKED_DEFINE
+#endif
+
 int main() { return 0; }

+ 10 - 1
Tests/CMakeCommands/target_include_directories/CMakeLists.txt

@@ -17,6 +17,9 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/poison/common.h" "#error Should not be i
 file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/cure")
 file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cure/common.h" "#define CURE_DEFINE\n")
 
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/linkedinclude")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/linkedinclude/linkedinclude.h" "#define LINKEDINCLUDE_DEFINE\n")
+
 add_executable(target_include_directories
   "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
 )
@@ -42,7 +45,13 @@ add_executable(consumer
   "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp"
 )
 
+add_library(linked UNKNOWN IMPORTED)
+set_property(TARGET linked PROPERTY
+  INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/linkedinclude")
+
 target_include_directories(consumer
-  PRIVATE $<TARGET_PROPERTY:target_include_directories,INTERFACE_INCLUDE_DIRECTORIES>
+  PRIVATE
+    $<TARGET_PROPERTY:target_include_directories,INTERFACE_INCLUDE_DIRECTORIES>
+    $<LINKED:linked>
   relative_dir
 )

+ 5 - 0
Tests/CMakeCommands/target_include_directories/consumer.cpp

@@ -3,6 +3,7 @@
 #include "publicinclude.h"
 #include "interfaceinclude.h"
 #include "relative_dir.h"
+#include "linkedinclude.h"
 
 #ifdef PRIVATEINCLUDE_DEFINE
 #error Unexpected PRIVATEINCLUDE_DEFINE
@@ -24,4 +25,8 @@
 #error Expected RELATIVE_DIR_DEFINE
 #endif
 
+#ifndef LINKEDINCLUDE_DEFINE
+#error Expected LINKEDINCLUDE_DEFINE
+#endif
+
 int main() { return 0; }

+ 1 - 0
Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-result.txt

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

+ 7 - 0
Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-stderr.txt

@@ -0,0 +1,7 @@
+CMake Error:
+  Error evaluating generator expression:
+
+    \$<LINKED:something>
+
+  \$<LINKED:...> may only be used in INCLUDE_DIRECTORIES and
+  COMPILE_DEFINITIONS properties.$

+ 7 - 0
Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked.cmake

@@ -0,0 +1,7 @@
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
+           "int main(int, char **) { return 0; }\n")
+
+add_executable(TargetPropertyGeneratorExpressions
+           "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
+target_link_libraries(TargetPropertyGeneratorExpressions "$<LINKED:something>")

+ 1 - 0
Tests/RunCMake/TargetPropertyGeneratorExpressions/RunCMakeTest.cmake

@@ -15,3 +15,4 @@ run_cmake(BadInvalidName5)
 run_cmake(BadInvalidName6)
 run_cmake(BadInvalidName7)
 run_cmake(BadInvalidName8)
+run_cmake(BadLinked)