Просмотр исходного кода

Merge topic 'extract-computed-target-properties'

cef59bb8 cmTarget: Implement GetProperty in terms of cmState::Snapshot
0d57b07a cmTarget: Group code for checking written properties together
c3fb0d95 cmTarget: Move sanity checks and computed property access to callers
fa9dbc56 cmGeneratorTarget: Implement cmTargetPropertyComputer interface
848ae2a6 cmTargetPropertyComputer: Template some methods on the Target
a0a720e6 cm{,Generator}Target: Add global generator accessors
637e3f3e cmTargetPropertyComputer: Unify whitelist handling from cmTarget
05251e6d cmTargetPropertyComputer: Move whitelist check from cmTarget
fbf1721c cmTargetPropertyComputer: Extract into new files
390a7d86 cmTargetPropertyComputer: Implement GetProperty without cmMakefile
e32a6bdd cmListFileBacktrace: Add a method to retrieve the Bottom of a snapshot
7863fba1 cmTarget: Extract GetLocation method
8096682e cmTarget: Extract GetSources method
7d57c1a2 cmTarget: Extract location computation methods
a55cac4b cmTarget: Split property computation into separate class
705fcf52 cmTarget: Move IMPORTED check to callers
...
Brad King 9 лет назад
Родитель
Сommit
188c762f8a

+ 2 - 0
Source/CMakeLists.txt

@@ -364,6 +364,8 @@ set(SRCS
   cmSystemTools.h
   cmTarget.cxx
   cmTarget.h
+  cmTargetPropertyComputer.cxx
+  cmTargetPropertyComputer.h
   cmTargetExport.h
   cmTest.cxx
   cmTest.h

+ 56 - 1
Source/cmGeneratorTarget.cxx

@@ -18,6 +18,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
+#include "cmTargetPropertyComputer.h"
 #include "cm_auto_ptr.hxx"
 #include "cmake.h"
 
@@ -42,6 +43,28 @@
 #define UNORDERED_SET std::set
 #endif
 
+template <>
+const char* cmTargetPropertyComputer::GetSources<cmGeneratorTarget>(
+  cmGeneratorTarget const* tgt, cmMessenger* /* messenger */,
+  cmListFileBacktrace const& /* context */)
+{
+  return tgt->GetSourcesProperty();
+}
+
+template <>
+const char* cmTargetPropertyComputer::ComputeLocationForBuild<
+  cmGeneratorTarget>(cmGeneratorTarget const* tgt)
+{
+  return tgt->GetLocation("");
+}
+
+template <>
+const char* cmTargetPropertyComputer::ComputeLocation<cmGeneratorTarget>(
+  cmGeneratorTarget const* tgt, const std::string& config)
+{
+  return tgt->GetLocation(config);
+}
+
 class cmGeneratorTarget::TargetPropertyEntry
 {
   static cmLinkImplItem NoLinkImplItem;
@@ -320,6 +343,26 @@ cmGeneratorTarget::~cmGeneratorTarget()
   cmDeleteAll(this->LinkInformation);
 }
 
+const char* cmGeneratorTarget::GetSourcesProperty() const
+{
+  std::vector<std::string> values;
+  for (std::vector<cmGeneratorTarget::TargetPropertyEntry *>::const_iterator
+         it = this->SourceEntries.begin(),
+         end = this->SourceEntries.end();
+       it != end; ++it) {
+    values.push_back((*it)->ge->GetInput());
+  }
+  static std::string value;
+  value.clear();
+  value = cmJoin(values, "");
+  return value.c_str();
+}
+
+cmGlobalGenerator* cmGeneratorTarget::GetGlobalGenerator() const
+{
+  return this->GetLocalGenerator()->GetGlobalGenerator();
+}
+
 cmLocalGenerator* cmGeneratorTarget::GetLocalGenerator() const
 {
   return this->LocalGenerator;
@@ -354,6 +397,18 @@ std::string cmGeneratorTarget::GetExportName() const
 
 const char* cmGeneratorTarget::GetProperty(const std::string& prop) const
 {
+  if (!cmTargetPropertyComputer::PassesWhitelist(
+        this->GetType(), prop, this->Makefile->GetMessenger(),
+        this->GetBacktrace())) {
+    return 0;
+  }
+  if (const char* result = cmTargetPropertyComputer::GetProperty(
+        this, prop, this->Makefile->GetMessenger(), this->GetBacktrace())) {
+    return result;
+  }
+  if (cmSystemTools::GetFatalErrorOccured()) {
+    return CM_NULLPTR;
+  }
   return this->Target->GetProperty(prop);
 }
 
@@ -3966,7 +4021,7 @@ void cmGeneratorTarget::ComputeVersionedName(std::string& vName,
 
 std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const
 {
-  cmPropertyMap propsObject = this->Target->GetProperties();
+  cmPropertyMap const& propsObject = this->Target->GetProperties();
   std::vector<std::string> props;
   props.reserve(propsObject.size());
   for (cmPropertyMap::const_iterator it = propsObject.begin();

+ 4 - 0
Source/cmGeneratorTarget.h

@@ -32,6 +32,8 @@ public:
 
   cmLocalGenerator* GetLocalGenerator() const;
 
+  cmGlobalGenerator* GetGlobalGenerator() const;
+
   bool IsImported() const;
   bool IsImportedGloballyVisible() const;
   const char* GetLocation(const std::string& config) const;
@@ -533,6 +535,8 @@ public:
 
   std::string GetFortranModuleDirectory(std::string const& working_dir) const;
 
+  const char* GetSourcesProperty() const;
+
 private:
   void AddSourceCommon(const std::string& src);
 

+ 13 - 2
Source/cmGetPropertyCommand.cxx

@@ -6,6 +6,7 @@
 #include "cmPropertyDefinition.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
+#include "cmTargetPropertyComputer.h"
 #include "cmTest.h"
 #include "cmake.h"
 
@@ -246,8 +247,18 @@ bool cmGetPropertyCommand::HandleTargetMode()
       }
       return this->StoreResult(CM_NULLPTR);
     }
-    return this->StoreResult(
-      target->GetProperty(this->PropertyName, this->Makefile));
+    const char* prop_cstr = 0;
+    cmListFileBacktrace bt = this->Makefile->GetBacktrace();
+    cmMessenger* messenger = this->Makefile->GetMessenger();
+    if (cmTargetPropertyComputer::PassesWhitelist(
+          target->GetType(), this->PropertyName, messenger, bt)) {
+      prop_cstr =
+        target->GetComputedProperty(this->PropertyName, messenger, bt);
+      if (!prop_cstr) {
+        prop_cstr = target->GetProperty(this->PropertyName);
+      }
+    }
+    return this->StoreResult(prop_cstr);
   }
   std::ostringstream e;
   e << "could not find TARGET " << this->Name

+ 12 - 1
Source/cmGetTargetPropertyCommand.cxx

@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGetTargetPropertyCommand.h"
 
+#include "cmTargetPropertyComputer.h"
+
 // cmSetTargetPropertyCommand
 bool cmGetTargetPropertyCommand::InitialPass(
   std::vector<std::string> const& args, cmExecutionStatus&)
@@ -22,7 +24,16 @@ bool cmGetTargetPropertyCommand::InitialPass(
         prop_exists = true;
       }
     } else if (!args[2].empty()) {
-      const char* prop_cstr = tgt->GetProperty(args[2], this->Makefile);
+      const char* prop_cstr = 0;
+      cmListFileBacktrace bt = this->Makefile->GetBacktrace();
+      cmMessenger* messenger = this->Makefile->GetMessenger();
+      if (cmTargetPropertyComputer::PassesWhitelist(tgt->GetType(), args[2],
+                                                    messenger, bt)) {
+        prop_cstr = tgt->GetComputedProperty(args[2], messenger, bt);
+        if (!prop_cstr) {
+          prop_cstr = tgt->GetProperty(args[2]);
+        }
+      }
       if (prop_cstr) {
         prop = prop_cstr;
         prop_exists = true;

+ 2 - 0
Source/cmListFileCache.h

@@ -120,6 +120,8 @@ public:
   cmListFileBacktrace& operator=(cmListFileBacktrace const& r);
   ~cmListFileBacktrace();
 
+  cmState::Snapshot GetBottom() const { return this->Bottom; }
+
   // Get a backtrace with the given file scope added to the top.
   // May not be called until after construction with a valid snapshot.
   cmListFileBacktrace Push(std::string const& file) const;

+ 180 - 278
Source/cmTarget.cxx

@@ -8,11 +8,13 @@
 #include "cmGlobalGenerator.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
+#include "cmMessenger.h"
 #include "cmOutputConverter.h"
 #include "cmProperty.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
 #include "cmSystemTools.h"
+#include "cmTargetPropertyComputer.h"
 #include "cmake.h"
 
 #include <algorithm>
@@ -33,6 +35,134 @@
 #define UNORDERED_SET std::set
 #endif
 
+template <>
+const char* cmTargetPropertyComputer::ComputeLocationForBuild<cmTarget>(
+  cmTarget const* tgt)
+{
+  static std::string loc;
+  if (tgt->IsImported()) {
+    loc = tgt->ImportedGetFullPath("", false);
+    return loc.c_str();
+  }
+
+  cmGlobalGenerator* gg = tgt->GetGlobalGenerator();
+  if (!gg->GetConfigureDoneCMP0026()) {
+    gg->CreateGenerationObjects();
+  }
+  cmGeneratorTarget* gt = gg->FindGeneratorTarget(tgt->GetName());
+  loc = gt->GetLocationForBuild();
+  return loc.c_str();
+}
+
+template <>
+const char* cmTargetPropertyComputer::ComputeLocation<cmTarget>(
+  cmTarget const* tgt, const std::string& config)
+{
+  static std::string loc;
+  if (tgt->IsImported()) {
+    loc = tgt->ImportedGetFullPath(config, false);
+    return loc.c_str();
+  }
+
+  cmGlobalGenerator* gg = tgt->GetGlobalGenerator();
+  if (!gg->GetConfigureDoneCMP0026()) {
+    gg->CreateGenerationObjects();
+  }
+  cmGeneratorTarget* gt = gg->FindGeneratorTarget(tgt->GetName());
+  loc = gt->GetFullPath(config, false);
+  return loc.c_str();
+}
+
+template <>
+const char* cmTargetPropertyComputer::GetSources<cmTarget>(
+  cmTarget const* tgt, cmMessenger* messenger,
+  cmListFileBacktrace const& context)
+{
+  cmStringRange entries = tgt->GetSourceEntries();
+  if (entries.empty()) {
+    return CM_NULLPTR;
+  }
+
+  std::ostringstream ss;
+  const char* sep = "";
+  for (std::vector<std::string>::const_iterator i = entries.begin();
+       i != entries.end(); ++i) {
+    std::string const& entry = *i;
+
+    std::vector<std::string> files;
+    cmSystemTools::ExpandListArgument(entry, files);
+    for (std::vector<std::string>::const_iterator li = files.begin();
+         li != files.end(); ++li) {
+      if (cmHasLiteralPrefix(*li, "$<TARGET_OBJECTS:") &&
+          (*li)[li->size() - 1] == '>') {
+        std::string objLibName = li->substr(17, li->size() - 18);
+
+        if (cmGeneratorExpression::Find(objLibName) != std::string::npos) {
+          ss << sep;
+          sep = ";";
+          ss << *li;
+          continue;
+        }
+
+        bool addContent = false;
+        bool noMessage = true;
+        std::ostringstream e;
+        cmake::MessageType messageType = cmake::AUTHOR_WARNING;
+        switch (context.GetBottom().GetPolicy(cmPolicies::CMP0051)) {
+          case cmPolicies::WARN:
+            e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0051) << "\n";
+            noMessage = false;
+          case cmPolicies::OLD:
+            break;
+          case cmPolicies::REQUIRED_ALWAYS:
+          case cmPolicies::REQUIRED_IF_USED:
+          case cmPolicies::NEW:
+            addContent = true;
+        }
+        if (!noMessage) {
+          e << "Target \"" << tgt->GetName()
+            << "\" contains "
+               "$<TARGET_OBJECTS> generator expression in its sources "
+               "list.  "
+               "This content was not previously part of the SOURCES "
+               "property "
+               "when that property was read at configure time.  Code "
+               "reading "
+               "that property needs to be adapted to ignore the generator "
+               "expression using the string(GENEX_STRIP) command.";
+          messenger->IssueMessage(messageType, e.str(), context);
+        }
+        if (addContent) {
+          ss << sep;
+          sep = ";";
+          ss << *li;
+        }
+      } else if (cmGeneratorExpression::Find(*li) == std::string::npos) {
+        ss << sep;
+        sep = ";";
+        ss << *li;
+      } else {
+        cmSourceFile* sf = tgt->GetMakefile()->GetOrCreateSource(*li);
+        // Construct what is known about this source file location.
+        cmSourceFileLocation const& location = sf->GetLocation();
+        std::string sname = location.GetDirectory();
+        if (!sname.empty()) {
+          sname += "/";
+        }
+        sname += location.GetName();
+
+        ss << sep;
+        sep = ";";
+        // Append this list entry.
+        ss << sname;
+      }
+    }
+  }
+  static std::string srcs;
+  srcs = ss.str();
+  return srcs.c_str();
+}
+
 class cmTargetInternals
 {
 public:
@@ -270,6 +400,11 @@ cmTarget::cmTarget(std::string const& name, cmState::TargetType type,
   }
 }
 
+cmGlobalGenerator* cmTarget::GetGlobalGenerator() const
+{
+  return this->GetMakefile()->GetGlobalGenerator();
+}
+
 void cmTarget::AddUtility(const std::string& u, cmMakefile* makefile)
 {
   if (this->Utilities.insert(u).second && makefile) {
@@ -705,43 +840,11 @@ cmBacktraceRange cmTarget::GetLinkImplementationBacktraces() const
   return cmMakeRange(this->Internal->LinkImplementationPropertyBacktraces);
 }
 
-static bool whiteListedInterfaceProperty(const std::string& prop)
-{
-  if (cmHasLiteralPrefix(prop, "INTERFACE_")) {
-    return true;
-  }
-  static UNORDERED_SET<std::string> builtIns;
-  if (builtIns.empty()) {
-    builtIns.insert("COMPATIBLE_INTERFACE_BOOL");
-    builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MAX");
-    builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MIN");
-    builtIns.insert("COMPATIBLE_INTERFACE_STRING");
-    builtIns.insert("EXPORT_NAME");
-    builtIns.insert("IMPORTED");
-    builtIns.insert("NAME");
-    builtIns.insert("TYPE");
-  }
-
-  if (builtIns.count(prop)) {
-    return true;
-  }
-
-  if (cmHasLiteralPrefix(prop, "MAP_IMPORTED_CONFIG_")) {
-    return true;
-  }
-
-  return false;
-}
-
 void cmTarget::SetProperty(const std::string& prop, const char* value)
 {
-  if (this->GetType() == cmState::INTERFACE_LIBRARY &&
-      !whiteListedInterfaceProperty(prop)) {
-    std::ostringstream e;
-    e << "INTERFACE_LIBRARY targets may only have whitelisted properties.  "
-         "The property \""
-      << prop << "\" is not allowed.";
-    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+  if (!cmTargetPropertyComputer::PassesWhitelist(
+        this->GetType(), prop, this->Makefile->GetMessenger(),
+        this->Makefile->GetBacktrace())) {
     return;
   }
   if (prop == "NAME") {
@@ -749,7 +852,20 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
     e << "NAME property is read-only\n";
     this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
     return;
+  } else if (prop == "EXPORT_NAME" && this->IsImported()) {
+    std::ostringstream e;
+    e << "EXPORT_NAME property can't be set on imported targets (\""
+      << this->Name << "\")\n";
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+    return;
+  } else if (prop == "SOURCES" && this->IsImported()) {
+    std::ostringstream e;
+    e << "SOURCES property can't be set on imported targets (\"" << this->Name
+      << "\")\n";
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+    return;
   }
+
   if (prop == "INCLUDE_DIRECTORIES") {
     this->Internal->IncludeDirectoriesEntries.clear();
     this->Internal->IncludeDirectoriesBacktraces.clear();
@@ -782,11 +898,6 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
       cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
       this->Internal->CompileDefinitionsBacktraces.push_back(lfbt);
     }
-  } else if (prop == "EXPORT_NAME" && this->IsImported()) {
-    std::ostringstream e;
-    e << "EXPORT_NAME property can't be set on imported targets (\""
-      << this->Name << "\")\n";
-    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
   } else if (prop == "LINK_LIBRARIES") {
     this->Internal->LinkImplementationPropertyEntries.clear();
     this->Internal->LinkImplementationPropertyBacktraces.clear();
@@ -796,14 +907,6 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
       this->Internal->LinkImplementationPropertyBacktraces.push_back(lfbt);
     }
   } else if (prop == "SOURCES") {
-    if (this->IsImported()) {
-      std::ostringstream e;
-      e << "SOURCES property can't be set on imported targets (\""
-        << this->Name << "\")\n";
-      this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
-      return;
-    }
-
     this->Internal->SourceEntries.clear();
     this->Internal->SourceBacktraces.clear();
     if (value) {
@@ -819,13 +922,9 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
 void cmTarget::AppendProperty(const std::string& prop, const char* value,
                               bool asString)
 {
-  if (this->GetType() == cmState::INTERFACE_LIBRARY &&
-      !whiteListedInterfaceProperty(prop)) {
-    std::ostringstream e;
-    e << "INTERFACE_LIBRARY targets may only have whitelisted properties.  "
-         "The property \""
-      << prop << "\" is not allowed.";
-    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+  if (!cmTargetPropertyComputer::PassesWhitelist(
+        this->GetType(), prop, this->Makefile->GetMessenger(),
+        this->Makefile->GetBacktrace())) {
     return;
   }
   if (prop == "NAME") {
@@ -833,6 +932,18 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
     e << "NAME property is read-only\n";
     this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
     return;
+  } else if (prop == "EXPORT_NAME" && this->IsImported()) {
+    std::ostringstream e;
+    e << "EXPORT_NAME property can't be set on imported targets (\""
+      << this->Name << "\")\n";
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+    return;
+  } else if (prop == "SOURCES" && this->IsImported()) {
+    std::ostringstream e;
+    e << "SOURCES property can't be set on imported targets (\"" << this->Name
+      << "\")\n";
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+    return;
   }
   if (prop == "INCLUDE_DIRECTORIES") {
     if (value && *value) {
@@ -858,11 +969,6 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
       cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
       this->Internal->CompileDefinitionsBacktraces.push_back(lfbt);
     }
-  } else if (prop == "EXPORT_NAME" && this->IsImported()) {
-    std::ostringstream e;
-    e << "EXPORT_NAME property can't be set on imported targets (\""
-      << this->Name << "\")\n";
-    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
   } else if (prop == "LINK_LIBRARIES") {
     if (value && *value) {
       cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
@@ -870,13 +976,6 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
       this->Internal->LinkImplementationPropertyBacktraces.push_back(lfbt);
     }
   } else if (prop == "SOURCES") {
-    if (this->IsImported()) {
-      std::ostringstream e;
-      e << "SOURCES property can't be set on imported targets (\""
-        << this->Name << "\")\n";
-      this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
-      return;
-    }
     cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
     this->Internal->SourceEntries.push_back(value);
     this->Internal->SourceBacktraces.push_back(lfbt);
@@ -1030,136 +1129,15 @@ void cmTarget::CheckProperty(const std::string& prop,
   }
 }
 
-bool cmTarget::HandleLocationPropertyPolicy(cmMakefile* context) const
+const char* cmTarget::GetComputedProperty(
+  const std::string& prop, cmMessenger* messenger,
+  cmListFileBacktrace const& context) const
 {
-  if (this->IsImported()) {
-    return true;
-  }
-  std::ostringstream e;
-  const char* modal = CM_NULLPTR;
-  cmake::MessageType messageType = cmake::AUTHOR_WARNING;
-  switch (context->GetPolicyStatus(cmPolicies::CMP0026)) {
-    case cmPolicies::WARN:
-      e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0026) << "\n";
-      modal = "should";
-    case cmPolicies::OLD:
-      break;
-    case cmPolicies::REQUIRED_ALWAYS:
-    case cmPolicies::REQUIRED_IF_USED:
-    case cmPolicies::NEW:
-      modal = "may";
-      messageType = cmake::FATAL_ERROR;
-  }
-
-  if (modal) {
-    e << "The LOCATION property " << modal << " not be read from target \""
-      << this->GetName()
-      << "\".  Use the target name directly with "
-         "add_custom_command, or use the generator expression $<TARGET_FILE>, "
-         "as appropriate.\n";
-    context->IssueMessage(messageType, e.str());
-  }
-
-  return messageType != cmake::FATAL_ERROR;
+  return cmTargetPropertyComputer::GetProperty(this, prop, messenger, context);
 }
 
 const char* cmTarget::GetProperty(const std::string& prop) const
 {
-  return this->GetProperty(prop, this->Makefile);
-}
-
-const char* cmTarget::GetProperty(const std::string& prop,
-                                  cmMakefile* context) const
-{
-  if (this->GetType() == cmState::INTERFACE_LIBRARY &&
-      !whiteListedInterfaceProperty(prop)) {
-    std::ostringstream e;
-    e << "INTERFACE_LIBRARY targets may only have whitelisted properties.  "
-         "The property \""
-      << prop << "\" is not allowed.";
-    context->IssueMessage(cmake::FATAL_ERROR, e.str());
-    return CM_NULLPTR;
-  }
-
-  // Watch for special "computed" properties that are dependent on
-  // other properties or variables.  Always recompute them.
-  if (this->GetType() == cmState::EXECUTABLE ||
-      this->GetType() == cmState::STATIC_LIBRARY ||
-      this->GetType() == cmState::SHARED_LIBRARY ||
-      this->GetType() == cmState::MODULE_LIBRARY ||
-      this->GetType() == cmState::UNKNOWN_LIBRARY) {
-    static const std::string propLOCATION = "LOCATION";
-    if (prop == propLOCATION) {
-      if (!this->HandleLocationPropertyPolicy(context)) {
-        return CM_NULLPTR;
-      }
-
-      // Set the LOCATION property of the target.
-      //
-      // For an imported target this is the location of an arbitrary
-      // available configuration.
-      //
-      if (this->IsImported()) {
-        this->Properties.SetProperty(
-          propLOCATION, this->ImportedGetFullPath("", false).c_str());
-      } else {
-        // For a non-imported target this is deprecated because it
-        // cannot take into account the per-configuration name of the
-        // target because the configuration type may not be known at
-        // CMake time.
-        cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
-        if (!gg->GetConfigureDoneCMP0026()) {
-          gg->CreateGenerationObjects();
-        }
-        cmGeneratorTarget* gt = gg->FindGeneratorTarget(this->GetName());
-        this->Properties.SetProperty(propLOCATION, gt->GetLocationForBuild());
-      }
-
-    }
-
-    // Support "LOCATION_<CONFIG>".
-    else if (cmHasLiteralPrefix(prop, "LOCATION_")) {
-      if (!this->HandleLocationPropertyPolicy(context)) {
-        return CM_NULLPTR;
-      }
-      const char* configName = prop.c_str() + 9;
-
-      if (this->IsImported()) {
-        this->Properties.SetProperty(
-          prop, this->ImportedGetFullPath(configName, false).c_str());
-      } else {
-        cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
-        if (!gg->GetConfigureDoneCMP0026()) {
-          gg->CreateGenerationObjects();
-        }
-        cmGeneratorTarget* gt = gg->FindGeneratorTarget(this->GetName());
-        this->Properties.SetProperty(
-          prop, gt->GetFullPath(configName, false).c_str());
-      }
-    }
-    // Support "<CONFIG>_LOCATION".
-    else if (cmHasLiteralSuffix(prop, "_LOCATION") &&
-             !cmHasLiteralPrefix(prop, "XCODE_ATTRIBUTE_")) {
-      std::string configName(prop.c_str(), prop.size() - 9);
-      if (configName != "IMPORTED") {
-        if (!this->HandleLocationPropertyPolicy(context)) {
-          return CM_NULLPTR;
-        }
-        if (this->IsImported()) {
-          this->Properties.SetProperty(
-            prop, this->ImportedGetFullPath(configName, false).c_str());
-        } else {
-          cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
-          if (!gg->GetConfigureDoneCMP0026()) {
-            gg->CreateGenerationObjects();
-          }
-          cmGeneratorTarget* gt = gg->FindGeneratorTarget(this->GetName());
-          this->Properties.SetProperty(
-            prop, gt->GetFullPath(configName, false).c_str());
-        }
-      }
-    }
-  }
   static UNORDERED_SET<std::string> specialProps;
 #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP
   MAKE_STATIC_PROP(LINK_LIBRARIES);
@@ -1244,93 +1222,16 @@ const char* cmTarget::GetProperty(const std::string& prop,
       return this->GetName().c_str();
     }
     if (prop == propBINARY_DIR) {
-      return this->GetMakefile()->GetCurrentBinaryDirectory();
+      return this->GetMakefile()
+        ->GetStateSnapshot()
+        .GetDirectory()
+        .GetCurrentBinary();
     }
     if (prop == propSOURCE_DIR) {
-      return this->GetMakefile()->GetCurrentSourceDirectory();
-    }
-    if (prop == propSOURCES) {
-      if (this->Internal->SourceEntries.empty()) {
-        return CM_NULLPTR;
-      }
-
-      std::ostringstream ss;
-      const char* sep = "";
-      for (std::vector<std::string>::const_iterator i =
-             this->Internal->SourceEntries.begin();
-           i != this->Internal->SourceEntries.end(); ++i) {
-        std::string const& entry = *i;
-
-        std::vector<std::string> files;
-        cmSystemTools::ExpandListArgument(entry, files);
-        for (std::vector<std::string>::const_iterator li = files.begin();
-             li != files.end(); ++li) {
-          if (cmHasLiteralPrefix(*li, "$<TARGET_OBJECTS:") &&
-              (*li)[li->size() - 1] == '>') {
-            std::string objLibName = li->substr(17, li->size() - 18);
-
-            if (cmGeneratorExpression::Find(objLibName) != std::string::npos) {
-              ss << sep;
-              sep = ";";
-              ss << *li;
-              continue;
-            }
-
-            bool addContent = false;
-            bool noMessage = true;
-            std::ostringstream e;
-            cmake::MessageType messageType = cmake::AUTHOR_WARNING;
-            switch (context->GetPolicyStatus(cmPolicies::CMP0051)) {
-              case cmPolicies::WARN:
-                e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0051) << "\n";
-                noMessage = false;
-              case cmPolicies::OLD:
-                break;
-              case cmPolicies::REQUIRED_ALWAYS:
-              case cmPolicies::REQUIRED_IF_USED:
-              case cmPolicies::NEW:
-                addContent = true;
-            }
-            if (!noMessage) {
-              e << "Target \"" << this->Name
-                << "\" contains "
-                   "$<TARGET_OBJECTS> generator expression in its sources "
-                   "list.  "
-                   "This content was not previously part of the SOURCES "
-                   "property "
-                   "when that property was read at configure time.  Code "
-                   "reading "
-                   "that property needs to be adapted to ignore the generator "
-                   "expression using the string(GENEX_STRIP) command.";
-              context->IssueMessage(messageType, e.str());
-            }
-            if (addContent) {
-              ss << sep;
-              sep = ";";
-              ss << *li;
-            }
-          } else if (cmGeneratorExpression::Find(*li) == std::string::npos) {
-            ss << sep;
-            sep = ";";
-            ss << *li;
-          } else {
-            cmSourceFile* sf = this->Makefile->GetOrCreateSource(*li);
-            // Construct what is known about this source file location.
-            cmSourceFileLocation const& location = sf->GetLocation();
-            std::string sname = location.GetDirectory();
-            if (!sname.empty()) {
-              sname += "/";
-            }
-            sname += location.GetName();
-
-            ss << sep;
-            sep = ";";
-            // Append this list entry.
-            ss << sname;
-          }
-        }
-      }
-      this->Properties.SetProperty("SOURCES", ss.str().c_str());
+      return this->GetMakefile()
+        ->GetStateSnapshot()
+        .GetDirectory()
+        .GetCurrentSource();
     }
   }
 
@@ -1339,7 +1240,8 @@ const char* cmTarget::GetProperty(const std::string& prop,
     const bool chain = this->GetMakefile()->GetState()->IsPropertyChained(
       prop, cmProperty::TARGET);
     if (chain) {
-      return this->Makefile->GetProperty(prop, chain);
+      return this->Makefile->GetStateSnapshot().GetDirectory().GetProperty(
+        prop, chain);
     }
   }
   return retVal;

+ 11 - 8
Source/cmTarget.h

@@ -30,6 +30,7 @@
 
 class cmMakefile;
 class cmSourceFile;
+class cmGlobalGenerator;
 class cmTargetInternals;
 
 class cmTargetInternalPointer
@@ -76,6 +77,8 @@ public:
    */
   cmState::TargetType GetType() const { return this->TargetTypeValue; }
 
+  cmGlobalGenerator* GetGlobalGenerator() const;
+
   ///! Set/Get the name of the target
   const std::string& GetName() const { return this->Name; }
 
@@ -197,9 +200,11 @@ public:
   void AppendProperty(const std::string& prop, const char* value,
                       bool asString = false);
   const char* GetProperty(const std::string& prop) const;
-  const char* GetProperty(const std::string& prop, cmMakefile* context) const;
   bool GetPropertyAsBool(const std::string& prop) const;
   void CheckProperty(const std::string& prop, cmMakefile* context) const;
+  const char* GetComputedProperty(const std::string& prop,
+                                  cmMessenger* messenger,
+                                  cmListFileBacktrace const& context) const;
 
   bool IsImported() const { return this->IsImportedTarget; }
   bool IsImportedGloballyVisible() const
@@ -208,7 +213,7 @@ public:
   }
 
   // Get the properties
-  cmPropertyMap& GetProperties() const { return this->Properties; }
+  cmPropertyMap const& GetProperties() const { return this->Properties; }
 
   bool GetMappedConfig(std::string const& desired_config, const char** loc,
                        const char** imp, std::string& suffix) const;
@@ -267,9 +272,10 @@ public:
     bool operator()(cmTarget const* t1, cmTarget const* t2) const;
   };
 
-private:
-  bool HandleLocationPropertyPolicy(cmMakefile* context) const;
+  std::string ImportedGetFullPath(const std::string& config,
+                                  bool implib) const;
 
+private:
   const char* GetSuffixVariableInternal(bool implib) const;
   const char* GetPrefixVariableInternal(bool implib) const;
 
@@ -278,11 +284,8 @@ private:
   void SetPropertyDefault(const std::string& property,
                           const char* default_value);
 
-  std::string ImportedGetFullPath(const std::string& config,
-                                  bool implib) const;
-
 private:
-  mutable cmPropertyMap Properties;
+  cmPropertyMap Properties;
   std::set<std::string> SystemIncludeDirectories;
   std::set<std::string> LinkDirectoriesEmmitted;
   std::set<std::string> Utilities;

+ 99 - 0
Source/cmTargetPropertyComputer.cxx

@@ -0,0 +1,99 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmTargetPropertyComputer.h"
+
+#include "cmGeneratorTarget.h"
+#include "cmGlobalGenerator.h"
+#include "cmMakefile.h"
+#include "cmMessenger.h"
+#include "cmSourceFile.h"
+#include "cmSourceFileLocation.h"
+#include "cmTarget.h"
+
+#if defined(CMake_HAVE_CXX_UNORDERED_SET)
+#include <unordered_set>
+#define UNORDERED_SET std::unordered_set
+#elif defined(CMAKE_BUILD_WITH_CMAKE)
+#include <cmsys/hash_set.hxx>
+#define UNORDERED_SET cmsys::hash_set
+#else
+#define UNORDERED_SET std::set
+#endif
+
+bool cmTargetPropertyComputer::HandleLocationPropertyPolicy(
+  std::string const& tgtName, cmMessenger* messenger,
+  cmListFileBacktrace const& context)
+{
+  std::ostringstream e;
+  const char* modal = CM_NULLPTR;
+  cmake::MessageType messageType = cmake::AUTHOR_WARNING;
+  switch (context.GetBottom().GetPolicy(cmPolicies::CMP0026)) {
+    case cmPolicies::WARN:
+      e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0026) << "\n";
+      modal = "should";
+    case cmPolicies::OLD:
+      break;
+    case cmPolicies::REQUIRED_ALWAYS:
+    case cmPolicies::REQUIRED_IF_USED:
+    case cmPolicies::NEW:
+      modal = "may";
+      messageType = cmake::FATAL_ERROR;
+  }
+
+  if (modal) {
+    e << "The LOCATION property " << modal << " not be read from target \""
+      << tgtName
+      << "\".  Use the target name directly with "
+         "add_custom_command, or use the generator expression $<TARGET_FILE>, "
+         "as appropriate.\n";
+    messenger->IssueMessage(messageType, e.str(), context);
+  }
+
+  return messageType != cmake::FATAL_ERROR;
+}
+
+bool cmTargetPropertyComputer::WhiteListedInterfaceProperty(
+  const std::string& prop)
+{
+  if (cmHasLiteralPrefix(prop, "INTERFACE_")) {
+    return true;
+  }
+  static UNORDERED_SET<std::string> builtIns;
+  if (builtIns.empty()) {
+    builtIns.insert("COMPATIBLE_INTERFACE_BOOL");
+    builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MAX");
+    builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MIN");
+    builtIns.insert("COMPATIBLE_INTERFACE_STRING");
+    builtIns.insert("EXPORT_NAME");
+    builtIns.insert("IMPORTED");
+    builtIns.insert("NAME");
+    builtIns.insert("TYPE");
+  }
+
+  if (builtIns.count(prop)) {
+    return true;
+  }
+
+  if (cmHasLiteralPrefix(prop, "MAP_IMPORTED_CONFIG_")) {
+    return true;
+  }
+
+  return false;
+}
+
+bool cmTargetPropertyComputer::PassesWhitelist(
+  cmState::TargetType tgtType, std::string const& prop, cmMessenger* messenger,
+  cmListFileBacktrace const& context)
+{
+  if (tgtType == cmState::INTERFACE_LIBRARY &&
+      !WhiteListedInterfaceProperty(prop)) {
+    std::ostringstream e;
+    e << "INTERFACE_LIBRARY targets may only have whitelisted properties.  "
+         "The property \""
+      << prop << "\" is not allowed.";
+    messenger->IssueMessage(cmake::FATAL_ERROR, e.str(), context);
+    return false;
+  }
+  return true;
+}

+ 110 - 0
Source/cmTargetPropertyComputer.h

@@ -0,0 +1,110 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmTargetPropertyComputer_h
+#define cmTargetPropertyComputer_h
+
+#include <cmConfigure.h> // IWYU pragma: keep
+
+#include "cmListFileCache.h"
+#include "cmSystemTools.h"
+
+#include <map>
+#include <string>
+
+class cmTarget;
+class cmMessenger;
+
+class cmTargetPropertyComputer
+{
+public:
+  template <typename Target>
+  static const char* GetProperty(Target const* tgt, const std::string& prop,
+                                 cmMessenger* messenger,
+                                 cmListFileBacktrace const& context)
+  {
+    if (const char* loc = GetLocation(tgt, prop, messenger, context)) {
+      return loc;
+    }
+    if (cmSystemTools::GetFatalErrorOccured()) {
+      return CM_NULLPTR;
+    }
+    if (prop == "SOURCES") {
+      return GetSources(tgt, messenger, context);
+    }
+    return CM_NULLPTR;
+  }
+
+  static bool WhiteListedInterfaceProperty(const std::string& prop);
+
+  static bool PassesWhitelist(cmState::TargetType tgtType,
+                              std::string const& prop, cmMessenger* messenger,
+                              cmListFileBacktrace const& context);
+
+private:
+  static bool HandleLocationPropertyPolicy(std::string const& tgtName,
+                                           cmMessenger* messenger,
+                                           cmListFileBacktrace const& context);
+
+  template <typename Target>
+  static const char* ComputeLocationForBuild(Target const* tgt);
+  template <typename Target>
+  static const char* ComputeLocation(Target const* tgt,
+                                     std::string const& config);
+
+  template <typename Target>
+  static const char* GetLocation(Target const* tgt, std::string const& prop,
+                                 cmMessenger* messenger,
+                                 cmListFileBacktrace const& context)
+
+  {
+    // Watch for special "computed" properties that are dependent on
+    // other properties or variables.  Always recompute them.
+    if (tgt->GetType() == cmState::EXECUTABLE ||
+        tgt->GetType() == cmState::STATIC_LIBRARY ||
+        tgt->GetType() == cmState::SHARED_LIBRARY ||
+        tgt->GetType() == cmState::MODULE_LIBRARY ||
+        tgt->GetType() == cmState::UNKNOWN_LIBRARY) {
+      static const std::string propLOCATION = "LOCATION";
+      if (prop == propLOCATION) {
+        if (!tgt->IsImported() &&
+            !HandleLocationPropertyPolicy(tgt->GetName(), messenger,
+                                          context)) {
+          return CM_NULLPTR;
+        }
+        return ComputeLocationForBuild(tgt);
+      }
+
+      // Support "LOCATION_<CONFIG>".
+      else if (cmHasLiteralPrefix(prop, "LOCATION_")) {
+        if (!tgt->IsImported() &&
+            !HandleLocationPropertyPolicy(tgt->GetName(), messenger,
+                                          context)) {
+          return CM_NULLPTR;
+        }
+        const char* configName = prop.c_str() + 9;
+        return ComputeLocation(tgt, configName);
+      }
+
+      // Support "<CONFIG>_LOCATION".
+      else if (cmHasLiteralSuffix(prop, "_LOCATION") &&
+               !cmHasLiteralPrefix(prop, "XCODE_ATTRIBUTE_")) {
+        std::string configName(prop.c_str(), prop.size() - 9);
+        if (configName != "IMPORTED") {
+          if (!tgt->IsImported() &&
+              !HandleLocationPropertyPolicy(tgt->GetName(), messenger,
+                                            context)) {
+            return CM_NULLPTR;
+          }
+          return ComputeLocation(tgt, configName);
+        }
+      }
+    }
+    return CM_NULLPTR;
+  }
+
+  template <typename Target>
+  static const char* GetSources(Target const* tgt, cmMessenger* messenger,
+                                cmListFileBacktrace const& context);
+};
+
+#endif

+ 3 - 1
Tests/RunCMake/GeneratorExpression/TARGET_PROPERTY-LOCATION-stderr.txt

@@ -1,4 +1,4 @@
-CMake Warning \(dev\) in CMakeLists.txt:
+CMake Warning \(dev\) at TARGET_PROPERTY-LOCATION.cmake:2 \(add_library\):
   Policy CMP0026 is not set: Disallow use of the LOCATION target property.
   Run "cmake --help-policy CMP0026" for policy details.  Use the cmake_policy
   command to set the policy and suppress this warning.
@@ -7,4 +7,6 @@ CMake Warning \(dev\) in CMakeLists.txt:
   name directly with add_custom_command, or use the generator expression
   \$<TARGET_FILE>, as appropriate.
 
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
 This warning is for project developers.  Use -Wno-dev to suppress it.

+ 1 - 0
bootstrap

@@ -311,6 +311,7 @@ CMAKE_CXX_SOURCES="\
   cmBootstrapCommands2 \
   cmCommandsForBootstrap \
   cmTarget \
+  cmTargetPropertyComputer \
   cmTest \
   cmCustomCommand \
   cmCustomCommandGenerator \