Browse Source

Merge topic 'tll-includes-defines'

e48d842 Cache context-independent includes on evaluation.
089fe1c Optimize genex evaluation for includes and defines.
179f495 find_package: Reword <package>_NO_INTERFACES documentation
e7b579b Test workaround of bad interface include directories from depends.
77cecb7 Add includes and compile definitions with target_link_libraries.
0b92602 Add the $<LINKED:...> generator expression.
0fa7f69 Add API to check if we're reading a includes or defines property.
2c3654c Add a way to exclude INTERFACE properties from exported targets.
d4297d5 Export targets to a targets file, not a Config file.
df4d2b2 Make it an error for INSTALL_PREFIX to be evaluated.
7ceeba9 Advance more when preprocessing exported strings.
30268b4 Handle reading empty properties defined by the link interface.
Brad King 12 years ago
parent
commit
ec85306025
41 changed files with 633 additions and 94 deletions
  1. 8 0
      Source/cmDocumentGeneratorExpressions.h
  2. 65 10
      Source/cmExportFileGenerator.cxx
  3. 2 2
      Source/cmExportFileGenerator.h
  4. 20 0
      Source/cmFindPackageCommand.cxx
  5. 7 1
      Source/cmGeneratorExpression.cxx
  6. 5 0
      Source/cmGeneratorExpression.h
  7. 42 0
      Source/cmGeneratorExpressionDAGChecker.cxx
  8. 5 1
      Source/cmGeneratorExpressionDAGChecker.h
  9. 122 5
      Source/cmGeneratorExpressionEvaluator.cxx
  10. 1 0
      Source/cmGeneratorExpressionEvaluator.h
  11. 30 11
      Source/cmTarget.cxx
  12. 3 3
      Source/cmTarget.h
  13. 53 0
      Source/cmTargetLinkLibrariesCommand.cxx
  14. 9 0
      Source/cmTargetLinkLibrariesCommand.h
  15. 6 0
      Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt
  16. 4 0
      Tests/CMakeCommands/target_compile_definitions/consumer.cpp
  17. 10 1
      Tests/CMakeCommands/target_include_directories/CMakeLists.txt
  18. 5 0
      Tests/CMakeCommands/target_include_directories/consumer.cpp
  19. 21 4
      Tests/CMakeCommands/target_link_libraries/CMakeLists.txt
  20. 7 0
      Tests/CMakeCommands/target_link_libraries/depG.cpp
  21. 7 0
      Tests/CMakeCommands/target_link_libraries/depG.h
  22. 16 0
      Tests/CMakeCommands/target_link_libraries/targetC.cpp
  23. 13 1
      Tests/CompatibleInterface/CMakeLists.txt
  24. 22 19
      Tests/ExportImport/Export/CMakeLists.txt
  25. 3 15
      Tests/ExportImport/Import/A/CMakeLists.txt
  26. 5 0
      Tests/ExportImport/Import/CMakeLists.txt
  27. 23 0
      Tests/ExportImport/Import/package_new_new/CMakeLists.txt
  28. 24 0
      Tests/ExportImport/Import/package_new_old/CMakeLists.txt
  29. 24 0
      Tests/ExportImport/Import/package_old_old/CMakeLists.txt
  30. 0 1
      Tests/GeneratorExpression/CMakeLists.txt
  31. 0 1
      Tests/GeneratorExpression/check-part2.cmake
  32. 41 0
      Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt
  33. 0 19
      Tests/Qt4Targets/CMakeLists.txt
  34. 1 0
      Tests/RunCMake/GeneratorExpression/BadInstallPrefix-result.txt
  35. 9 0
      Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt
  36. 3 0
      Tests/RunCMake/GeneratorExpression/BadInstallPrefix.cmake
  37. 1 0
      Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
  38. 1 0
      Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-result.txt
  39. 7 0
      Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked-stderr.txt
  40. 7 0
      Tests/RunCMake/TargetPropertyGeneratorExpressions/BadLinked.cmake
  41. 1 0
      Tests/RunCMake/TargetPropertyGeneratorExpressions/RunCMakeTest.cmake

+ 8 - 0
Source/cmDocumentGeneratorExpressions.h

@@ -51,6 +51,14 @@
   "on the target tgt.\n"                                                \
   "on the target tgt.\n"                                                \
   "Note that tgt is not added as a dependency of the target this "      \
   "Note that tgt is not added as a dependency of the target this "      \
   "expression is evaluated on.\n"                                       \
   "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 "   \
   "  $<TARGET_POLICY:pol>          = '1' if the policy was NEW when "   \
   "the 'head' target was created, else '0'.  If the policy was not "    \
   "the 'head' target was created, else '0'.  If the policy was not "    \
   "set, the warning message for the policy will be emitted.  This "     \
   "set, the warning message for the policy will be emitted.  This "     \

+ 65 - 10
Source/cmExportFileGenerator.cxx

@@ -25,6 +25,8 @@
 
 
 #include <cmsys/auto_ptr.hxx>
 #include <cmsys/auto_ptr.hxx>
 
 
+#include "assert.h"
+
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 cmExportFileGenerator::cmExportFileGenerator()
 cmExportFileGenerator::cmExportFileGenerator()
 {
 {
@@ -160,7 +162,7 @@ void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName,
                                                            preprocessRule);
                                                            preprocessRule);
     if (!prepro.empty())
     if (!prepro.empty())
       {
       {
-      this->ResolveTargetsInGeneratorExpressions(prepro, target,
+      this->ResolveTargetsInGeneratorExpressions(prepro, target, propName,
                                                  missingTargets);
                                                  missingTargets);
       properties[outputName] = prepro;
       properties[outputName] = prepro;
       }
       }
@@ -264,15 +266,16 @@ void cmExportFileGenerator::GenerateInterfaceProperties(cmTarget *target,
 {
 {
   if (!properties.empty())
   if (!properties.empty())
     {
     {
+    os << "if(NOT ${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES)\n";
     std::string targetName = this->Namespace;
     std::string targetName = this->Namespace;
     targetName += target->GetName();
     targetName += target->GetName();
-    os << "set_target_properties(" << targetName << " PROPERTIES\n";
+    os << "  set_target_properties(" << targetName << " PROPERTIES\n";
     for(ImportPropertyMap::const_iterator pi = properties.begin();
     for(ImportPropertyMap::const_iterator pi = properties.begin();
         pi != properties.end(); ++pi)
         pi != properties.end(); ++pi)
       {
       {
-      os << "  " << pi->first << " \"" << pi->second << "\"\n";
+      os << "    " << pi->first << " \"" << pi->second << "\"\n";
       }
       }
-    os << ")\n\n";
+    os << "  )\nendif()\n\n";
     }
     }
 }
 }
 
 
@@ -323,13 +326,14 @@ static bool isGeneratorExpression(const std::string &lib)
 void
 void
 cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
 cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
                                     std::string &input,
                                     std::string &input,
-                                    cmTarget* target,
+                                    cmTarget* target, const char *propName,
                                     std::vector<std::string> &missingTargets,
                                     std::vector<std::string> &missingTargets,
                                     FreeTargetsReplace replace)
                                     FreeTargetsReplace replace)
 {
 {
   if (replace == NoReplaceFreeTargets)
   if (replace == NoReplaceFreeTargets)
     {
     {
-    this->ResolveTargetsInGeneratorExpression(input, target, missingTargets);
+    this->ResolveTargetsInGeneratorExpression(input, target, propName,
+                                              missingTargets);
     return;
     return;
     }
     }
   std::vector<std::string> parts;
   std::vector<std::string> parts;
@@ -348,7 +352,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
       {
       {
       this->ResolveTargetsInGeneratorExpression(
       this->ResolveTargetsInGeneratorExpression(
                                     *li,
                                     *li,
-                                    target,
+                                    target, propName,
                                     missingTargets);
                                     missingTargets);
       }
       }
     input += sep + *li;
     input += sep + *li;
@@ -360,7 +364,7 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
 void
 void
 cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
 cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
                                     std::string &input,
                                     std::string &input,
-                                    cmTarget* target,
+                                    cmTarget* target, const char *propName,
                                     std::vector<std::string> &missingTargets)
                                     std::vector<std::string> &missingTargets)
 {
 {
   std::string::size_type pos = 0;
   std::string::size_type pos = 0;
@@ -391,10 +395,61 @@ cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
       {
       {
       input.replace(nameStartPos, commaPos - nameStartPos, targetName);
       input.replace(nameStartPos, commaPos - nameStartPos, targetName);
       }
       }
-    lastPos = pos + targetName.size();
+    lastPos = nameStartPos + targetName.size() + 1;
     }
     }
 
 
   std::string errorString;
   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;
   pos = 0;
   lastPos = pos;
   lastPos = pos;
   while((pos = input.find("$<TARGET_NAME:", lastPos)) != input.npos)
   while((pos = input.find("$<TARGET_NAME:", lastPos)) != input.npos)
@@ -490,7 +545,7 @@ cmExportFileGenerator
                                                          preprocessRule);
                                                          preprocessRule);
   if (!prepro.empty())
   if (!prepro.empty())
     {
     {
-    this->ResolveTargetsInGeneratorExpressions(prepro, target,
+    this->ResolveTargetsInGeneratorExpressions(prepro, target, 0,
                                                missingTargets,
                                                missingTargets,
                                                ReplaceFreeTargets);
                                                ReplaceFreeTargets);
     properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;
     properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;

+ 2 - 2
Source/cmExportFileGenerator.h

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

+ 20 - 0
Source/cmFindPackageCommand.cxx

@@ -376,6 +376,26 @@ void cmFindPackageCommand::GenerateDocumentation()
     "The package configuration file may set <package>_FOUND to false "
     "The package configuration file may set <package>_FOUND to false "
     "to tell find_package that component requirements are not satisfied."
     "to tell find_package that component requirements are not satisfied."
     "\n"
     "\n"
+    "A package configuration file may include() a <package>Targets.cmake "
+    "file, created by install(EXPORT) in the upstream source, to import "
+    "targets into the downstream consumer.  "
+    "When a new version of the upstream adds INTERFACE properties not "
+    "present in a previous version it can change behavior for existing "
+    "downstreams.  "
+    "In order to remain source compatible the upstream package configuration "
+    "file may set <package>_NO_INTERFACES to disable INTERFACE properties.  "
+    "For example, code of the form:\n"
+    "  if(<package>_FIND_VERSION VERSION_LESS <new-version>\n"
+    "      AND NOT <package>_INTERFACES)\n"
+    "    set(<package>_NO_INTERFACES 1)\n"
+    "  endif()\n"
+    "  include(\"${CMAKE_CURRENT_LIST_DIR}/<package>Targets.cmake\")\n"
+    "tells <package>Targets.cmake not to provide the INTERFACE properties "
+    "unless the downstream requests at least <new-version> or sets "
+    "<package>_INTERFACES to explicitly request them.  "
+    "This allows consumers to decide when to enable the new interfaces when "
+    "upgrading."
+    "\n"
     "See the cmake_policy() command documentation for discussion of the "
     "See the cmake_policy() command documentation for discussion of the "
     "NO_POLICY_SCOPE option."
     "NO_POLICY_SCOPE option."
     ;
     ;

+ 7 - 1
Source/cmGeneratorExpression.cxx

@@ -88,6 +88,7 @@ const char *cmCompiledGeneratorExpression::Evaluate(
   context.Config = config;
   context.Config = config;
   context.Quiet = quiet;
   context.Quiet = quiet;
   context.HadError = false;
   context.HadError = false;
+  context.HadContextSensitiveCondition = false;
   context.HeadTarget = headTarget;
   context.HeadTarget = headTarget;
   context.CurrentTarget = currentTarget ? currentTarget : headTarget;
   context.CurrentTarget = currentTarget ? currentTarget : headTarget;
   context.Backtrace = this->Backtrace;
   context.Backtrace = this->Backtrace;
@@ -109,6 +110,10 @@ const char *cmCompiledGeneratorExpression::Evaluate(
       break;
       break;
       }
       }
     }
     }
+  if (!context.HadError)
+    {
+    this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
+    }
 
 
   this->Targets = context.Targets;
   this->Targets = context.Targets;
   // TODO: Return a std::string from here instead?
   // TODO: Return a std::string from here instead?
@@ -118,7 +123,8 @@ const char *cmCompiledGeneratorExpression::Evaluate(
 cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
 cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
               cmListFileBacktrace const& backtrace,
               cmListFileBacktrace const& backtrace,
               const char *input)
               const char *input)
-  : Backtrace(backtrace), Input(input ? input : "")
+  : Backtrace(backtrace), Input(input ? input : ""),
+    HadContextSensitiveCondition(false)
 {
 {
   cmGeneratorExpressionLexer l;
   cmGeneratorExpressionLexer l;
   std::vector<cmGeneratorExpressionToken> tokens =
   std::vector<cmGeneratorExpressionToken> tokens =

+ 5 - 0
Source/cmGeneratorExpression.h

@@ -100,6 +100,10 @@ public:
   {
   {
     return this->Backtrace;
     return this->Backtrace;
   }
   }
+  bool GetHadContextSensitiveCondition() const
+  {
+    return this->HadContextSensitiveCondition;
+  }
 
 
 private:
 private:
   cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace,
   cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace,
@@ -118,6 +122,7 @@ private:
   mutable std::set<cmTarget*> Targets;
   mutable std::set<cmTarget*> Targets;
   mutable std::map<cmStdString, cmStdString> SeenTargetProperties;
   mutable std::map<cmStdString, cmStdString> SeenTargetProperties;
   mutable std::string Output;
   mutable std::string Output;
+  mutable bool HadContextSensitiveCondition;
 };
 };
 
 
 #endif
 #endif

+ 42 - 0
Source/cmGeneratorExpressionDAGChecker.cxx

@@ -24,7 +24,33 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
   : Parent(parent), Target(target), Property(property),
   : Parent(parent), Target(target), Property(property),
     Content(content), Backtrace(backtrace)
     Content(content), Backtrace(backtrace)
 {
 {
+  const cmGeneratorExpressionDAGChecker *top = this;
+  const cmGeneratorExpressionDAGChecker *p = this->Parent;
+  while (p)
+    {
+    top = p;
+    p = p->Parent;
+    }
   this->CheckResult = this->checkGraph();
   this->CheckResult = this->checkGraph();
+
+  if (CheckResult == DAG && (top->Property == "INCLUDE_DIRECTORIES"
+      || top->Property == "COMPILE_DEFINITIONS") )
+    {
+    std::map<cmStdString, std::set<cmStdString> >::const_iterator it
+                                                    = top->Seen.find(target);
+    if (it != top->Seen.end())
+      {
+      const std::set<cmStdString> &propSet = it->second;
+      const std::set<cmStdString>::const_iterator i = propSet.find(property);
+      if (i != propSet.end())
+        {
+        this->CheckResult = ALREADY_SEEN;
+        return;
+        }
+      }
+    const_cast<cmGeneratorExpressionDAGChecker *>(top)
+                                            ->Seen[target].insert(property);
+    }
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
@@ -125,3 +151,19 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries()
        || strncmp(prop, "LINK_INTERFACE_LIBRARIES_", 26) == 0
        || strncmp(prop, "LINK_INTERFACE_LIBRARIES_", 26) == 0
        || strncmp(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_", 35) == 0);
        || strncmp(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_", 35) == 0);
 }
 }
+
+//----------------------------------------------------------------------------
+bool cmGeneratorExpressionDAGChecker::EvaluatingIncludeDirectories()
+{
+  const char *prop = this->Property.c_str();
+  return (strcmp(prop, "INCLUDE_DIRECTORIES") == 0
+       || strcmp(prop, "INTERFACE_INCLUDE_DIRECTORIES") == 0 );
+}
+
+//----------------------------------------------------------------------------
+bool cmGeneratorExpressionDAGChecker::EvaluatingCompileDefinitions()
+{
+  const char *prop = this->Property.c_str();
+  return (strcmp(prop, "COMPILE_DEFINITIONS") == 0
+       || strcmp(prop, "INTERFACE_COMPILE_DEFINITIONS") == 0 );
+}

+ 5 - 1
Source/cmGeneratorExpressionDAGChecker.h

@@ -28,7 +28,8 @@ struct cmGeneratorExpressionDAGChecker
   enum Result {
   enum Result {
     DAG,
     DAG,
     SELF_REFERENCE,
     SELF_REFERENCE,
-    CYCLIC_REFERENCE
+    CYCLIC_REFERENCE,
+    ALREADY_SEEN
   };
   };
 
 
   Result check() const;
   Result check() const;
@@ -37,6 +38,8 @@ struct cmGeneratorExpressionDAGChecker
                    const std::string &expr);
                    const std::string &expr);
 
 
   bool EvaluatingLinkLibraries();
   bool EvaluatingLinkLibraries();
+  bool EvaluatingIncludeDirectories();
+  bool EvaluatingCompileDefinitions();
 
 
 private:
 private:
   Result checkGraph() const;
   Result checkGraph() const;
@@ -45,6 +48,7 @@ private:
   const cmGeneratorExpressionDAGChecker * const Parent;
   const cmGeneratorExpressionDAGChecker * const Parent;
   const std::string Target;
   const std::string Target;
   const std::string Property;
   const std::string Property;
+  std::map<cmStdString, std::set<cmStdString> > Seen;
   const GeneratorExpressionContent * const Content;
   const GeneratorExpressionContent * const Content;
   const cmListFileBacktrace Backtrace;
   const cmListFileBacktrace Backtrace;
   Result CheckResult;
   Result CheckResult;

+ 122 - 5
Source/cmGeneratorExpressionEvaluator.cxx

@@ -238,6 +238,7 @@ static const struct ConfigurationNode : public cmGeneratorExpressionNode
                        const GeneratorExpressionContent *,
                        const GeneratorExpressionContent *,
                        cmGeneratorExpressionDAGChecker *) const
                        cmGeneratorExpressionDAGChecker *) const
   {
   {
+    context->HadContextSensitiveCondition = true;
     return context->Config ? context->Config : "";
     return context->Config ? context->Config : "";
   }
   }
 } configurationNode;
 } configurationNode;
@@ -262,6 +263,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
                   "Expression syntax not recognized.");
                   "Expression syntax not recognized.");
       return std::string();
       return std::string();
       }
       }
+    context->HadContextSensitiveCondition = true;
     if (!context->Config)
     if (!context->Config)
       {
       {
       return parameters.front().empty() ? "1" : "0";
       return parameters.front().empty() ? "1" : "0";
@@ -435,6 +437,9 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
     case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
     case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
       // No error. We just skip cyclic references.
       // No error. We just skip cyclic references.
       return std::string();
       return std::string();
+    case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
+      // No error. We're not going to find anything new here.
+      return std::string();
     case cmGeneratorExpressionDAGChecker::DAG:
     case cmGeneratorExpressionDAGChecker::DAG:
       break;
       break;
       }
       }
@@ -452,12 +457,14 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
         }
         }
       if (propertyName == "POSITION_INDEPENDENT_CODE")
       if (propertyName == "POSITION_INDEPENDENT_CODE")
         {
         {
+        context->HadContextSensitiveCondition = true;
         return target->GetLinkInterfaceDependentBoolProperty(
         return target->GetLinkInterfaceDependentBoolProperty(
                     "POSITION_INDEPENDENT_CODE", context->Config) ? "1" : "0";
                     "POSITION_INDEPENDENT_CODE", context->Config) ? "1" : "0";
         }
         }
       if (target->IsLinkInterfaceDependentBoolProperty(propertyName,
       if (target->IsLinkInterfaceDependentBoolProperty(propertyName,
                                                        context->Config))
                                                        context->Config))
         {
         {
+        context->HadContextSensitiveCondition = true;
         return target->GetLinkInterfaceDependentBoolProperty(
         return target->GetLinkInterfaceDependentBoolProperty(
                                                 propertyName,
                                                 propertyName,
                                                 context->Config) ? "1" : "0";
                                                 context->Config) ? "1" : "0";
@@ -465,9 +472,12 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
       if (target->IsLinkInterfaceDependentStringProperty(propertyName,
       if (target->IsLinkInterfaceDependentStringProperty(propertyName,
                                                          context->Config))
                                                          context->Config))
         {
         {
-        return target->GetLinkInterfaceDependentStringProperty(
+        context->HadContextSensitiveCondition = true;
+        const char *propContent =
+                              target->GetLinkInterfaceDependentStringProperty(
                                                 propertyName,
                                                 propertyName,
                                                 context->Config);
                                                 context->Config);
+        return propContent ? propContent : "";
         }
         }
 
 
       return std::string();
       return std::string();
@@ -481,12 +491,19 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
       if (targetPropertyTransitiveWhitelist[i] == propertyName)
       if (targetPropertyTransitiveWhitelist[i] == propertyName)
         {
         {
         cmGeneratorExpression ge(context->Backtrace);
         cmGeneratorExpression ge(context->Backtrace);
-        return ge.Parse(prop)->Evaluate(context->Makefile,
+        cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop);
+        std::string result = cge->Evaluate(context->Makefile,
                             context->Config,
                             context->Config,
                             context->Quiet,
                             context->Quiet,
                             context->HeadTarget,
                             context->HeadTarget,
                             target,
                             target,
                             &dagChecker);
                             &dagChecker);
+
+        if (cge->GetHadContextSensitiveCondition())
+          {
+          context->HadContextSensitiveCondition = true;
+          }
+        return result;
         }
         }
       }
       }
     return prop;
     return prop;
@@ -580,6 +597,9 @@ static const struct TargetPolicyNode : public cmGeneratorExpressionNode
           "be used with add_custom_command.");
           "be used with add_custom_command.");
       return std::string();
       return std::string();
       }
       }
+
+    context->HadContextSensitiveCondition = true;
+
     for (size_t i = 0;
     for (size_t i = 0;
          i < (sizeof(targetPolicyWhitelist) /
          i < (sizeof(targetPolicyWhitelist) /
               sizeof(*targetPolicyWhitelist));
               sizeof(*targetPolicyWhitelist));
@@ -619,19 +639,114 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode
 {
 {
   InstallPrefixNode() {}
   InstallPrefixNode() {}
 
 
-  virtual bool GeneratesContent() const { return false; }
+  virtual bool GeneratesContent() const { return true; }
   virtual int NumExpectedParameters() const { return 0; }
   virtual int NumExpectedParameters() const { return 0; }
 
 
   std::string Evaluate(const std::vector<std::string> &,
   std::string Evaluate(const std::vector<std::string> &,
-                       cmGeneratorExpressionContext *,
-                       const GeneratorExpressionContent *,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content,
                        cmGeneratorExpressionDAGChecker *) const
                        cmGeneratorExpressionDAGChecker *) const
   {
   {
+    reportError(context, content->GetOriginalExpression(),
+                "INSTALL_PREFIX is a marker for install(EXPORT) only.  It "
+                "should never be evaluated.");
     return std::string();
     return std::string();
   }
   }
 
 
 } installPrefixNode;
 } 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::ALREADY_SEEN:
+      // No error. We're not going to find anything new here.
+      return std::string();
+    case cmGeneratorExpressionDAGChecker::DAG:
+      break;
+      }
+
+    cmGeneratorExpression ge(context->Backtrace);
+    cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(propContent);
+    std::string result = cge->Evaluate(context->Makefile,
+                        context->Config,
+                        context->Quiet,
+                        context->HeadTarget,
+                        target,
+                        &dagChecker);
+    if (cge->GetHadContextSensitiveCondition())
+      {
+      context->HadContextSensitiveCondition = true;
+      }
+    return result;
+  }
+
+} linkedNode;
+
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 template<bool linker, bool soname>
 template<bool linker, bool soname>
 struct TargetFilesystemArtifactResultCreator
 struct TargetFilesystemArtifactResultCreator
@@ -869,6 +984,8 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier)
     return &targetDefinedNode;
     return &targetDefinedNode;
   else if (identifier == "INSTALL_PREFIX")
   else if (identifier == "INSTALL_PREFIX")
     return &installPrefixNode;
     return &installPrefixNode;
+  else if (identifier == "LINKED")
+    return &linkedNode;
   return 0;
   return 0;
 
 
 }
 }

+ 1 - 0
Source/cmGeneratorExpressionEvaluator.h

@@ -32,6 +32,7 @@ struct cmGeneratorExpressionContext
                            // directly or indirectly in the property.
                            // directly or indirectly in the property.
   bool Quiet;
   bool Quiet;
   bool HadError;
   bool HadError;
+  bool HadContextSensitiveCondition;
 };
 };
 
 
 struct cmGeneratorExpressionDAGChecker;
 struct cmGeneratorExpressionDAGChecker;

+ 30 - 11
Source/cmTarget.cxx

@@ -134,6 +134,7 @@ public:
       : ge(cge)
       : ge(cge)
     {}
     {}
     const cmsys::auto_ptr<cmCompiledGeneratorExpression> ge;
     const cmsys::auto_ptr<cmCompiledGeneratorExpression> ge;
+    std::vector<std::string> CachedIncludes;
   };
   };
   std::vector<IncludeDirectoriesEntry*> IncludeDirectoriesEntries;
   std::vector<IncludeDirectoriesEntry*> IncludeDirectoriesEntries;
 };
 };
@@ -2778,22 +2779,36 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config)
       end = this->Internal->IncludeDirectoriesEntries.end();
       end = this->Internal->IncludeDirectoriesEntries.end();
       it != end; ++it)
       it != end; ++it)
     {
     {
-    std::vector<std::string> entryIncludes;
-    cmSystemTools::ExpandListArgument((*it)->ge->Evaluate(this->Makefile,
-                                              config,
-                                              false,
-                                              this,
-                                              &dagChecker),
-                                    entryIncludes);
+
+    bool testIsOff = true;
+    bool cacheIncludes = false;
+    std::vector<std::string> entryIncludes = (*it)->CachedIncludes;
+    if(!entryIncludes.empty())
+      {
+      testIsOff = false;
+      }
+    else
+      {
+      cmSystemTools::ExpandListArgument((*it)->ge->Evaluate(this->Makefile,
+                                                config,
+                                                false,
+                                                this,
+                                                &dagChecker),
+                                      entryIncludes);
+      if (!(*it)->ge->GetHadContextSensitiveCondition())
+        {
+        cacheIncludes = true;
+        }
+      }
     std::string usedIncludes;
     std::string usedIncludes;
-    for(std::vector<std::string>::const_iterator
+    for(std::vector<std::string>::iterator
           li = entryIncludes.begin(); li != entryIncludes.end(); ++li)
           li = entryIncludes.begin(); li != entryIncludes.end(); ++li)
       {
       {
-      std::string inc = *li;
-      if (!cmSystemTools::IsOff(inc.c_str()))
+      if (testIsOff && !cmSystemTools::IsOff(li->c_str()))
         {
         {
-        cmSystemTools::ConvertToUnixSlashes(inc);
+        cmSystemTools::ConvertToUnixSlashes(*li);
         }
         }
+      std::string inc = *li;
 
 
       if(uniqueIncludes.insert(inc).second)
       if(uniqueIncludes.insert(inc).second)
         {
         {
@@ -2804,6 +2819,10 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config)
           }
           }
         }
         }
       }
       }
+    if (cacheIncludes)
+      {
+      (*it)->CachedIncludes = entryIncludes;
+      }
     if (!usedIncludes.empty())
     if (!usedIncludes.empty())
       {
       {
       this->Makefile->GetCMakeInstance()->IssueMessage(cmake::LOG,
       this->Makefile->GetCMakeInstance()->IssueMessage(cmake::LOG,

+ 3 - 3
Source/cmTarget.h

@@ -514,6 +514,9 @@ public:
 
 
   const char *GetLinkInterfaceDependentStringProperty(const std::string &p,
   const char *GetLinkInterfaceDependentStringProperty(const std::string &p,
                                                       const char *config);
                                                       const char *config);
+
+  std::string GetDebugGeneratorExpressions(const std::string &value,
+                                  cmTarget::LinkLibraryType llt);
 private:
 private:
   /**
   /**
    * A list of direct dependencies. Use in conjunction with DependencyMap.
    * A list of direct dependencies. Use in conjunction with DependencyMap.
@@ -659,9 +662,6 @@ private:
 
 
   void ProcessSourceExpression(std::string const& expr);
   void ProcessSourceExpression(std::string const& expr);
 
 
-  std::string GetDebugGeneratorExpressions(const std::string &value,
-                                  cmTarget::LinkLibraryType llt);
-
   // The cmMakefile instance that owns this target.  This should
   // The cmMakefile instance that owns this target.  This should
   // always be set.
   // always be set.
   cmMakefile* Makefile;
   cmMakefile* Makefile;

+ 53 - 0
Source/cmTargetLinkLibrariesCommand.cxx

@@ -249,11 +249,52 @@ cmTargetLinkLibrariesCommand
   this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
   this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
 }
 }
 
 
+//----------------------------------------------------------------------------
+static std::string compileProperty(cmTarget *tgt, const std::string &lib,
+                                   bool isGenex,
+                                   const std::string &property,
+                                   cmTarget::LinkLibraryType llt)
+{
+  std::string value = !isGenex ? "$<LINKED:" + lib + ">"
+                               : "$<$<TARGET_DEFINED:" + lib + ">:" +
+                                   "$<TARGET_PROPERTY:" + lib +
+                                   ",INTERFACE_" + property + ">"
+                                 ">";
+
+  return tgt->GetDebugGeneratorExpressions(value, llt);
+}
+
+//----------------------------------------------------------------------------
+static bool isGeneratorExpression(const std::string &lib)
+{
+  const std::string::size_type openpos = lib.find("$<");
+  return (openpos != std::string::npos)
+      && (lib.find(">", openpos) != std::string::npos);
+}
+
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 void
 void
 cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib,
 cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib,
                                             cmTarget::LinkLibraryType llt)
                                             cmTarget::LinkLibraryType llt)
 {
 {
+  const bool isGenex = isGeneratorExpression(lib);
+
+  cmsys::RegularExpression targetNameValidator;
+  targetNameValidator.compile("^[A-Za-z0-9_.:-]+$");
+  const bool potentialTargetName = targetNameValidator.find(lib);
+
+  if (potentialTargetName || isGenex)
+    {
+    this->Target->AppendProperty("INCLUDE_DIRECTORIES",
+                                 compileProperty(this->Target, lib,
+                                                 isGenex,
+                                      "INCLUDE_DIRECTORIES", llt).c_str());
+    this->Target->AppendProperty("COMPILE_DEFINITIONS",
+                                 compileProperty(this->Target, lib,
+                                                 isGenex,
+                                      "COMPILE_DEFINITIONS", llt).c_str());
+    }
+
   // Handle normal case first.
   // Handle normal case first.
   if(this->CurrentProcessingState != ProcessingLinkInterface)
   if(this->CurrentProcessingState != ProcessingLinkInterface)
     {
     {
@@ -266,6 +307,18 @@ cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib,
       }
       }
     }
     }
 
 
+  if (potentialTargetName || isGenex)
+    {
+    this->Target->AppendProperty("INTERFACE_COMPILE_DEFINITIONS",
+                                compileProperty(this->Target, lib,
+                                                isGenex,
+                                        "COMPILE_DEFINITIONS", llt).c_str());
+    this->Target->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES",
+                                compileProperty(this->Target, lib,
+                                                isGenex,
+                                        "INCLUDE_DIRECTORIES", llt).c_str());
+    }
+
   // Get the list of configurations considered to be DEBUG.
   // Get the list of configurations considered to be DEBUG.
   std::vector<std::string> const& debugConfigs =
   std::vector<std::string> const& debugConfigs =
     this->Makefile->GetCMakeInstance()->GetDebugConfigs();
     this->Makefile->GetCMakeInstance()->GetDebugConfigs();

+ 9 - 0
Source/cmTargetLinkLibrariesCommand.h

@@ -97,6 +97,15 @@ public:
       "Calls to other signatures of this command may set the property "
       "Calls to other signatures of this command may set the property "
       "making any libraries linked exclusively by this signature private."
       "making any libraries linked exclusively by this signature private."
       "\n"
       "\n"
+      "Target usage requirements are also consumed by this command. If the "
+      "<target> is linked to another target which has "
+      "a populated INTERFACE_INCLUDE_DIRECTORIES, the content of it is "
+      "appended to the INCLUDE_DIRECTORIES of <target>.  Similarly, the "
+      "INTERFACE_COMPILE_DEFINITONS of a dependee are added to the "
+      "COMPILE_DEFINITONS of <target>, and the "
+      "INTERFACE_POSITION_INDEPENDENT_CODE property is used to determine the "
+      "POSITION_INDEPENDENT_CODE property of <target>."
+      "\n"
       "  target_link_libraries(<target> LINK_INTERFACE_LIBRARIES\n"
       "  target_link_libraries(<target> LINK_INTERFACE_LIBRARIES\n"
       "                        [[debug|optimized|general] <lib>] ...)\n"
       "                        [[debug|optimized|general] <lib>] ...)\n"
       "The LINK_INTERFACE_LIBRARIES mode appends the libraries "
       "The LINK_INTERFACE_LIBRARIES mode appends the libraries "

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

@@ -16,9 +16,15 @@ add_executable(consumer
   "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp"
   "${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
 target_compile_definitions(consumer
   PRIVATE $<TARGET_PROPERTY:target_compile_definitions,INTERFACE_COMPILE_DEFINITIONS>
   PRIVATE $<TARGET_PROPERTY:target_compile_definitions,INTERFACE_COMPILE_DEFINITIONS>
   $<$<TARGET_DEFINED:notdefined>:SHOULD_NOT_BE_DEFINED>
   $<$<TARGET_DEFINED:notdefined>:SHOULD_NOT_BE_DEFINED>
   $<$<TARGET_DEFINED:target_compile_definitions>:SHOULD_BE_DEFINED>
   $<$<TARGET_DEFINED:target_compile_definitions>:SHOULD_BE_DEFINED>
+  $<LINKED:linked>
   -DDASH_D_DEFINE
   -DDASH_D_DEFINE
 )
 )

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

@@ -23,4 +23,8 @@
 #error Expected DASH_D_DEFINE
 #error Expected DASH_D_DEFINE
 #endif
 #endif
 
 
+#ifndef MY_LINKED_DEFINE
+#error Expected MY_LINKED_DEFINE
+#endif
+
 int main() { return 0; }
 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(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/cure")
 file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cure/common.h" "#define CURE_DEFINE\n")
 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
 add_executable(target_include_directories
   "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
   "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
 )
 )
@@ -42,7 +45,13 @@ add_executable(consumer
   "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp"
   "${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
 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
   relative_dir
 )
 )

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

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

+ 21 - 4
Tests/CMakeCommands/target_link_libraries/CMakeLists.txt

@@ -62,10 +62,6 @@ assert_property(targetA LINK_INTERFACE_LIBRARIES "")
 
 
 add_subdirectory(subdir)
 add_subdirectory(subdir)
 target_link_libraries(targetA subdirlib)
 target_link_libraries(targetA subdirlib)
-set_property(TARGET targetA APPEND PROPERTY
-  INCLUDE_DIRECTORIES
-    $<TARGET_PROPERTY:subdirlib,INTERFACE_INCLUDE_DIRECTORIES>
-)
 
 
 target_link_libraries(targetA depB depC)
 target_link_libraries(targetA depB depC)
 
 
@@ -87,3 +83,24 @@ set_property(TARGET depD APPEND PROPERTY
 
 
 add_executable(targetB targetB.cpp)
 add_executable(targetB targetB.cpp)
 target_link_libraries(targetB depD)
 target_link_libraries(targetB depD)
+
+macro(create_header _name)
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${_name}")
+  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_name}/${_name}.h" "//${_name}.h\n")
+endmacro()
+
+create_header(foo)
+create_header(bar)
+
+add_library(depG SHARED depG.cpp)
+generate_export_header(depG)
+target_include_directories(depG INTERFACE
+    "${CMAKE_CURRENT_BINARY_DIR}/foo"
+    "${CMAKE_CURRENT_BINARY_DIR}/bar"
+)
+target_compile_definitions(depG INTERFACE
+    TEST_DEF
+)
+
+add_executable(targetC targetC.cpp)
+target_link_libraries(targetC depG)

+ 7 - 0
Tests/CMakeCommands/target_link_libraries/depG.cpp

@@ -0,0 +1,7 @@
+
+#include "depG.h"
+
+int DepG::foo()
+{
+  return 0;
+}

+ 7 - 0
Tests/CMakeCommands/target_link_libraries/depG.h

@@ -0,0 +1,7 @@
+
+#include "depg_export.h"
+
+struct DEPG_EXPORT DepG
+{
+  int foo();
+};

+ 16 - 0
Tests/CMakeCommands/target_link_libraries/targetC.cpp

@@ -0,0 +1,16 @@
+
+#include "depG.h"
+
+#include "foo.h"
+#include "bar.h"
+
+#ifndef TEST_DEF
+#error Expected TEST_DEF definition
+#endif
+
+int main(int argc, char **argv)
+{
+  DepG g;
+
+  return g.foo();
+}

+ 13 - 1
Tests/CompatibleInterface/CMakeLists.txt

@@ -48,10 +48,22 @@ target_compile_definitions(CompatibleInterface
 add_library(iface2 SHARED iface2.cpp)
 add_library(iface2 SHARED iface2.cpp)
 generate_export_header(iface2)
 generate_export_header(iface2)
 
 
+set_property(TARGET iface2 APPEND PROPERTY
+  COMPATIBLE_INTERFACE_STRING
+    Iface2_PROP
+)
+
 # For the LINK_LIBRARIES and related properties, we should not evaluate
 # For the LINK_LIBRARIES and related properties, we should not evaluate
 # properties defined only in the interface - they should be implicitly zero
 # properties defined only in the interface - they should be implicitly zero
 set_property(TARGET iface2
 set_property(TARGET iface2
   APPEND PROPERTY
   APPEND PROPERTY
     LINK_INTERFACE_LIBRARIES $<$<BOOL:$<TARGET_PROPERTY:BOOL_PROP4>>:nonexistant>
     LINK_INTERFACE_LIBRARIES $<$<BOOL:$<TARGET_PROPERTY:BOOL_PROP4>>:nonexistant>
 )
 )
-target_link_libraries(CompatibleInterface iface2)
+target_link_libraries(CompatibleInterface iface2
+      $<$<BOOL:$<TARGET_PROPERTY:Iface2_PROP>>:nonexistant>
+)
+# Test that this does not segfault:
+target_compile_definitions(CompatibleInterface
+  PRIVATE
+    $<$<BOOL:$<TARGET_PROPERTY:Iface2_PROP>>:SOME_DEFINE>
+)

+ 22 - 19
Tests/ExportImport/Export/CMakeLists.txt

@@ -90,23 +90,7 @@ set_property(TARGET testLibCycleA PROPERTY LINK_INTERFACE_MULTIPLICITY 3)
 # Test exporting dependent libraries into different exports
 # Test exporting dependent libraries into different exports
 add_library(testLibRequired testLibRequired.c)
 add_library(testLibRequired testLibRequired.c)
 add_library(testLibDepends testLibDepends.c)
 add_library(testLibDepends testLibDepends.c)
-set_property(TARGET testLibDepends APPEND PROPERTY
-  INCLUDE_DIRECTORIES
-    $<TARGET_PROPERTY:testLibRequired,INTERFACE_INCLUDE_DIRECTORIES>
-)
-set_property(TARGET testLibDepends APPEND PROPERTY
-  COMPILE_DEFINITIONS
-    $<TARGET_PROPERTY:testLibRequired,INTERFACE_COMPILE_DEFINITIONS>
-)
-set_property(TARGET testLibDepends APPEND PROPERTY
-  INTERFACE_INCLUDE_DIRECTORIES
-    $<TARGET_PROPERTY:testLibRequired,INTERFACE_INCLUDE_DIRECTORIES>
-)
-set_property(TARGET testLibDepends APPEND PROPERTY
-  INTERFACE_COMPILE_DEFINITIONS
-    $<TARGET_PROPERTY:testLibRequired,INTERFACE_COMPILE_DEFINITIONS>
-)
-target_link_libraries(testLibDepends testLibRequired)
+target_link_libraries(testLibDepends LINK_PUBLIC testLibRequired)
 
 
 macro(add_include_lib _libName)
 macro(add_include_lib _libName)
   file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "// no content\n")
   file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "// no content\n")
@@ -234,11 +218,30 @@ install(TARGETS testLibRequired
                 testLibIncludeRequired6
                 testLibIncludeRequired6
                 testSharedLibRequired
                 testSharedLibRequired
         EXPORT RequiredExp DESTINATION lib )
         EXPORT RequiredExp DESTINATION lib )
-install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredConfig.cmake DESTINATION lib/cmake/testLibRequired)
+install(EXPORT RequiredExp NAMESPACE Req:: FILE testLibRequiredTargets.cmake DESTINATION lib/cmake/testLibRequired)
 
 
 install(TARGETS testLibDepends testSharedLibDepends EXPORT DependsExp DESTINATION lib )
 install(TARGETS testLibDepends testSharedLibDepends EXPORT DependsExp DESTINATION lib )
-install(EXPORT DependsExp FILE testLibDependsConfig.cmake DESTINATION lib/cmake/testLibDepends)
+install(EXPORT DependsExp FILE testLibDependsTargets.cmake DESTINATION lib/cmake/testLibDepends)
 
 
+file(WRITE
+  "${CMAKE_CURRENT_BINARY_DIR}/testLibRequiredConfig.cmake"
+  "
+if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION VERSION_LESS 2.3 AND NOT \${CMAKE_FIND_PACKAGE_NAME}_INTERFACES)
+  set(\${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES 1)
+endif()
+include(\"\${CMAKE_CURRENT_LIST_DIR}/testLibRequiredTargets.cmake\")
+set(\${CMAKE_FIND_PACKAGE_NAME}_INCLUDE_DIRS \"${CMAKE_CURRENT_BINARY_DIR}\" \"${CMAKE_CURRENT_SOURCE_DIR}\" )
+"
+)
+
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file( testLibRequiredConfigVersion.cmake VERSION 2.5 COMPATIBILITY AnyNewerVersion)
+
+install(FILES
+  "${CMAKE_CURRENT_BINARY_DIR}/testLibRequiredConfig.cmake"
+  "${CMAKE_CURRENT_BINARY_DIR}/testLibRequiredConfigVersion.cmake"
+  DESTINATION lib/cmake/testLibRequired
+)
 
 
 # Install and export from install tree.
 # Install and export from install tree.
 install(
 install(

+ 3 - 15
Tests/ExportImport/Import/A/CMakeLists.txt

@@ -5,8 +5,8 @@ include(${Import_BINARY_DIR}/../Export/ExportBuildTree.cmake)
 include(${CMAKE_INSTALL_PREFIX}/lib/exp/exp.cmake)
 include(${CMAKE_INSTALL_PREFIX}/lib/exp/exp.cmake)
 
 
 # Import two exports, where the Depends one depends on an exported target from the Required one:
 # Import two exports, where the Depends one depends on an exported target from the Required one:
-include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibRequired/testLibRequiredConfig.cmake)
-include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibDepends/testLibDependsConfig.cmake)
+include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibRequired/testLibRequiredTargets.cmake)
+include(${CMAKE_INSTALL_PREFIX}/lib/cmake/testLibDepends/testLibDependsTargets.cmake)
 
 
 # Try referencing an executable imported from the install tree.
 # Try referencing an executable imported from the install tree.
 add_custom_command(
 add_custom_command(
@@ -159,18 +159,11 @@ endif()
 
 
 add_executable(deps_iface deps_iface.c)
 add_executable(deps_iface deps_iface.c)
 target_link_libraries(deps_iface testLibDepends)
 target_link_libraries(deps_iface testLibDepends)
-target_include_directories(deps_iface PRIVATE $<TARGET_PROPERTY:testLibDepends,INTERFACE_INCLUDE_DIRECTORIES>)
-target_compile_definitions(deps_iface PRIVATE $<TARGET_PROPERTY:testLibDepends,INTERFACE_COMPILE_DEFINITIONS>)
 
 
 add_executable(deps_shared_iface deps_shared_iface.cpp)
 add_executable(deps_shared_iface deps_shared_iface.cpp)
 target_link_libraries(deps_shared_iface testSharedLibDepends)
 target_link_libraries(deps_shared_iface testSharedLibDepends)
-target_include_directories(deps_shared_iface
-  PRIVATE
-    $<TARGET_PROPERTY:testSharedLibDepends,INTERFACE_INCLUDE_DIRECTORIES>
-)
 target_compile_definitions(deps_shared_iface
 target_compile_definitions(deps_shared_iface
   PRIVATE
   PRIVATE
-    $<TARGET_PROPERTY:testSharedLibDepends,INTERFACE_COMPILE_DEFINITIONS>
     $<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:PIC_PROPERTY_IS_ON>
     $<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:PIC_PROPERTY_IS_ON>
     $<$<BOOL:$<TARGET_PROPERTY:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON>
     $<$<BOOL:$<TARGET_PROPERTY:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON>
     $<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH>
     $<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH>
@@ -200,13 +193,8 @@ endif()
 
 
 add_executable(deps_shared_iface2 deps_shared_iface.cpp)
 add_executable(deps_shared_iface2 deps_shared_iface.cpp)
 target_link_libraries(deps_shared_iface2 bld_testSharedLibDepends bld_subdirlib)
 target_link_libraries(deps_shared_iface2 bld_testSharedLibDepends bld_subdirlib)
-target_include_directories(deps_shared_iface2
-  PRIVATE
-    $<TARGET_PROPERTY:bld_testSharedLibDepends,INTERFACE_INCLUDE_DIRECTORIES>
-    $<TARGET_PROPERTY:bld_subdirlib,INTERFACE_INCLUDE_DIRECTORIES>
-)
 target_compile_definitions(deps_shared_iface2
 target_compile_definitions(deps_shared_iface2
-  PRIVATE $<TARGET_PROPERTY:bld_testSharedLibDepends,INTERFACE_COMPILE_DEFINITIONS> TEST_SUBDIR_LIB
+  PRIVATE TEST_SUBDIR_LIB
   $<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:PIC_PROPERTY_IS_ON>
   $<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:PIC_PROPERTY_IS_ON>
   $<$<BOOL:$<TARGET_PROPERTY:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON>
   $<$<BOOL:$<TARGET_PROPERTY:CUSTOM_PROP>>:CUSTOM_PROPERTY_IS_ON>
   $<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH>
   $<$<STREQUAL:$<TARGET_PROPERTY:CUSTOM_STRING>,testcontent>:CUSTOM_STRING_IS_MATCH>

+ 5 - 0
Tests/ExportImport/Import/CMakeLists.txt

@@ -17,3 +17,8 @@ add_executable(imp_testTransExe1 imp_testTransExe1.c)
 target_link_libraries(imp_testTransExe1 imp_lib1)
 target_link_libraries(imp_testTransExe1 imp_lib1)
 add_executable(imp_testTransExe1b imp_testTransExe1.c)
 add_executable(imp_testTransExe1b imp_testTransExe1.c)
 target_link_libraries(imp_testTransExe1b imp_lib1b)
 target_link_libraries(imp_testTransExe1b imp_lib1b)
+
+# Test package INTERFACE controls
+add_subdirectory(package_old_old)
+add_subdirectory(package_new_old)
+add_subdirectory(package_new_new)

+ 23 - 0
Tests/ExportImport/Import/package_new_new/CMakeLists.txt

@@ -0,0 +1,23 @@
+
+cmake_minimum_required(VERSION 2.8)
+
+find_package(testLibRequired 2.5 REQUIRED)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
+  "
+#include \"testSharedLibRequired.h\"
+int main(int argc, char **argv)
+{
+  TestSharedLibRequired req;
+  return req.foo();
+}
+"
+)
+
+get_target_property(prop Req::testSharedLibRequired INTERFACE_INCLUDE_DIRECTORIES)
+if (NOT prop)
+  message(SEND_ERROR "Interface of Req::testSharedLibRequired should not be empty")
+endif()
+
+add_executable(new_new_test "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
+target_link_libraries(new_new_test Req::testSharedLibRequired)

+ 24 - 0
Tests/ExportImport/Import/package_new_old/CMakeLists.txt

@@ -0,0 +1,24 @@
+
+cmake_minimum_required(VERSION 2.8)
+
+find_package(testLibRequired 2.5 REQUIRED)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
+  "
+#include \"testSharedLibRequired.h\"
+int main(int argc, char **argv)
+{
+  TestSharedLibRequired req;
+  return req.foo();
+}
+"
+)
+
+get_target_property(prop Req::testSharedLibRequired INTERFACE_INCLUDE_DIRECTORIES)
+if ("${prop}" STREQUAL "")
+  message(SEND_ERROR "Interface of Req::testSharedLibRequired should not be empty")
+endif()
+
+add_executable(new_old_test "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
+target_link_libraries(new_old_test Req::testSharedLibRequired)
+include_directories(${testLibRequired_INCLUDE_DIRS})

+ 24 - 0
Tests/ExportImport/Import/package_old_old/CMakeLists.txt

@@ -0,0 +1,24 @@
+
+cmake_minimum_required(VERSION 2.8)
+
+find_package(testLibRequired 2.1 REQUIRED)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
+  "
+#include \"testSharedLibRequired.h\"
+int main(int argc, char **argv)
+{
+  TestSharedLibRequired req;
+  return req.foo();
+}
+"
+)
+
+get_target_property(prop Req::testSharedLibRequired INTERFACE_INCLUDE_DIRECTORIES)
+if (prop)
+  message(SEND_ERROR "Interface of Req::testSharedLibRequired should be empty, but is ${prop}")
+endif()
+
+add_executable(old_old_test "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
+target_link_libraries(old_old_test Req::testSharedLibRequired)
+include_directories(${testLibRequired_INCLUDE_DIRS})

+ 0 - 1
Tests/GeneratorExpression/CMakeLists.txt

@@ -89,7 +89,6 @@ add_custom_target(check-part2 ALL
     -Dtest_install_interface=$<INSTALL_INTERFACE:install>
     -Dtest_install_interface=$<INSTALL_INTERFACE:install>
     -Dtest_target_name_1=$<TARGET_NAME:tgt,ok>
     -Dtest_target_name_1=$<TARGET_NAME:tgt,ok>
     -Dtest_target_name_2=$<TARGET_NAME:tgt:ok>
     -Dtest_target_name_2=$<TARGET_NAME:tgt:ok>
-    -Dtest_install_prefix=$<INSTALL_PREFIX>
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part2.cmake
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part2.cmake
   COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 2)"
   COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 2)"
   VERBATIM
   VERBATIM

+ 0 - 1
Tests/GeneratorExpression/check-part2.cmake

@@ -26,4 +26,3 @@ check(test_build_interface "build")
 check(test_install_interface "")
 check(test_install_interface "")
 check(test_target_name_1 "tgt,ok")
 check(test_target_name_1 "tgt,ok")
 check(test_target_name_2 "tgt:ok")
 check(test_target_name_2 "tgt:ok")
-check(test_install_prefix "")

+ 41 - 0
Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt

@@ -82,3 +82,44 @@ add_custom_target(test_custom_target
         $<TARGET_PROPERTY:TargetIncludeDirectories,COMPILE_DEFINITIONS>
         $<TARGET_PROPERTY:TargetIncludeDirectories,COMPILE_DEFINITIONS>
         WORKING_DIRECTORY
         WORKING_DIRECTORY
         "${CMAKE_CURRENT_SOURCE_DIR}")
         "${CMAKE_CURRENT_SOURCE_DIR}")
+
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bad")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/bad/common.h" "#error Should not be included\n")
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/good")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/good/common.h" "#include \"othergood.h\"\n")
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/othergood")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/othergood/othergood.h" "// No error\n")
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libothergood.cpp" "// No content \n")
+add_library(libothergood "${CMAKE_CURRENT_BINARY_DIR}/libothergood.cpp")
+set_property(TARGET libothergood APPEND PROPERTY
+  INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/othergood"
+)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libgood.cpp" "// No content \n")
+add_library(libgood "${CMAKE_CURRENT_BINARY_DIR}/libgood.cpp")
+set_property(TARGET libgood APPEND PROPERTY
+  INTERFACE_INCLUDE_DIRECTORIES
+    "${CMAKE_CURRENT_BINARY_DIR}/good;$<TARGET_PROPERTY:libothergood,INTERFACE_INCLUDE_DIRECTORIES>"
+)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libbad.cpp" "// No content \n")
+add_library(libbad "${CMAKE_CURRENT_BINARY_DIR}/libbad.cpp")
+set_property(TARGET libbad APPEND PROPERTY
+  INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/bad"
+)
+
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib5.cpp" "#include \"common.h\"\n")
+add_library(lib5 "${CMAKE_CURRENT_BINARY_DIR}/lib5.cpp")
+
+# Assuming the link order must be:
+target_link_libraries(lib5 libbad libgood)
+
+# Oops!.
+# As include directory order and link order are the same when using target_link_libraries, we have to
+# get the libgood includes in before the libbad includes.
+# We do that with this command:
+target_include_directories(lib5
+  BEFORE PRIVATE $<LINKED:libgood>
+)

+ 0 - 19
Tests/Qt4Targets/CMakeLists.txt

@@ -12,29 +12,10 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
 add_executable(Qt4Targets WIN32 main.cpp)
 add_executable(Qt4Targets WIN32 main.cpp)
 target_link_libraries(Qt4Targets Qt4::QtGui)
 target_link_libraries(Qt4Targets Qt4::QtGui)
 
 
-set_property(TARGET Qt4Targets APPEND PROPERTY
-  INCLUDE_DIRECTORIES
-    $<TARGET_PROPERTY:Qt4::QtGui,INTERFACE_INCLUDE_DIRECTORIES>
-)
-set_property(TARGET Qt4Targets APPEND PROPERTY
-  COMPILE_DEFINITIONS
-    $<TARGET_PROPERTY:Qt4::QtGui,INTERFACE_COMPILE_DEFINITIONS>
-)
-
 if (WIN32)
 if (WIN32)
   if (TARGET Qt4::QAxServer)
   if (TARGET Qt4::QAxServer)
     add_executable(activeqtexe WIN32 activeqtexe.cpp)
     add_executable(activeqtexe WIN32 activeqtexe.cpp)
     set_property(TARGET activeqtexe PROPERTY QT4_NO_LINK_QTMAIN ON)
     set_property(TARGET activeqtexe PROPERTY QT4_NO_LINK_QTMAIN ON)
     target_link_libraries(activeqtexe Qt4::QAxServer Qt4::QtGui)
     target_link_libraries(activeqtexe Qt4::QAxServer Qt4::QtGui)
-    set_property(TARGET activeqtexe APPEND PROPERTY
-      INCLUDE_DIRECTORIES
-      $<TARGET_PROPERTY:Qt4::QAxServer,INTERFACE_INCLUDE_DIRECTORIES>
-      $<TARGET_PROPERTY:Qt4::QtGui,INTERFACE_INCLUDE_DIRECTORIES>
-    )
-    set_property(TARGET activeqtexe APPEND PROPERTY
-      COMPILE_DEFINITIONS
-      $<TARGET_PROPERTY:Qt4::QAxServer,INTERFACE_COMPILE_DEFINITIONS>
-      $<TARGET_PROPERTY:Qt4::QtGui,INTERFACE_COMPILE_DEFINITIONS>
-    )
   endif()
   endif()
 endif()
 endif()

+ 1 - 0
Tests/RunCMake/GeneratorExpression/BadInstallPrefix-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at BadInstallPrefix.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<INSTALL_PREFIX>
+
+  INSTALL_PREFIX is a marker for install\(EXPORT\) only.  It should never be
+  evaluated.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 3 - 0
Tests/RunCMake/GeneratorExpression/BadInstallPrefix.cmake

@@ -0,0 +1,3 @@
+add_custom_target(check ALL COMMAND check
+  $<INSTALL_PREFIX>/include
+  VERBATIM)

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

@@ -7,3 +7,4 @@ run_cmake(BadNOT)
 run_cmake(BadStrEqual)
 run_cmake(BadStrEqual)
 run_cmake(BadZero)
 run_cmake(BadZero)
 run_cmake(BadTargetName)
 run_cmake(BadTargetName)
+run_cmake(BadInstallPrefix)

+ 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(BadInvalidName6)
 run_cmake(BadInvalidName7)
 run_cmake(BadInvalidName7)
 run_cmake(BadInvalidName8)
 run_cmake(BadInvalidName8)
+run_cmake(BadLinked)