浏览代码

cmGeneratorTarget: Factor compatible interface properties into own source

Brad King 1 年之前
父节点
当前提交
b4924c562a
共有 4 个文件被更改,包括 739 次插入708 次删除
  1. 1 0
      Source/CMakeLists.txt
  2. 0 708
      Source/cmGeneratorTarget.cxx
  3. 737 0
      Source/cmGeneratorTarget_CompatibleInterface.cxx
  4. 1 0
      bootstrap

+ 1 - 0
Source/CMakeLists.txt

@@ -285,6 +285,7 @@ add_library(
   cmGeneratorExpression.h
   cmGeneratorTarget.cxx
   cmGeneratorTarget.h
+  cmGeneratorTarget_CompatibleInterface.cxx
   cmGeneratorTarget_IncludeDirectories.cxx
   cmGeneratorTarget_LinkDirectories.cxx
   cmGeneratorTarget_Options.cxx

+ 0 - 708
Source/cmGeneratorTarget.cxx

@@ -6,12 +6,9 @@
 #include <array>
 #include <cassert>
 #include <cctype>
-#include <cerrno>
 #include <cstddef>
 #include <cstdio>
-#include <cstdlib>
 #include <cstring>
-#include <iterator>
 #include <sstream>
 #include <type_traits>
 #include <unordered_set>
@@ -3920,676 +3917,6 @@ void cmGeneratorTarget::ConstructSourceFileFlags() const
   }
 }
 
-const cmGeneratorTarget::CompatibleInterfacesBase&
-cmGeneratorTarget::GetCompatibleInterfaces(std::string const& config) const
-{
-  cmGeneratorTarget::CompatibleInterfaces& compat =
-    this->CompatibleInterfacesMap[config];
-  if (!compat.Done) {
-    compat.Done = true;
-    compat.PropsBool.insert("POSITION_INDEPENDENT_CODE");
-    compat.PropsString.insert("AUTOUIC_OPTIONS");
-    std::vector<cmGeneratorTarget const*> const& deps =
-      this->GetLinkImplementationClosure(config);
-    for (cmGeneratorTarget const* li : deps) {
-#define CM_READ_COMPATIBLE_INTERFACE(X, x)                                    \
-  if (cmValue prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) {           \
-    cmList props(*prop);                                                      \
-    compat.Props##x.insert(props.begin(), props.end());                       \
-  }
-      CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool)
-      CM_READ_COMPATIBLE_INTERFACE(STRING, String)
-      CM_READ_COMPATIBLE_INTERFACE(NUMBER_MIN, NumberMin)
-      CM_READ_COMPATIBLE_INTERFACE(NUMBER_MAX, NumberMax)
-#undef CM_READ_COMPATIBLE_INTERFACE
-    }
-  }
-  return compat;
-}
-
-bool cmGeneratorTarget::IsLinkInterfaceDependentBoolProperty(
-  const std::string& p, const std::string& config) const
-{
-  if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
-      this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
-    return false;
-  }
-  return this->GetCompatibleInterfaces(config).PropsBool.count(p) > 0;
-}
-
-bool cmGeneratorTarget::IsLinkInterfaceDependentStringProperty(
-  const std::string& p, const std::string& config) const
-{
-  if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
-      this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
-    return false;
-  }
-  return this->GetCompatibleInterfaces(config).PropsString.count(p) > 0;
-}
-
-bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMinProperty(
-  const std::string& p, const std::string& config) const
-{
-  if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
-      this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
-    return false;
-  }
-  return this->GetCompatibleInterfaces(config).PropsNumberMin.count(p) > 0;
-}
-
-bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMaxProperty(
-  const std::string& p, const std::string& config) const
-{
-  if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
-      this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
-    return false;
-  }
-  return this->GetCompatibleInterfaces(config).PropsNumberMax.count(p) > 0;
-}
-
-enum CompatibleType
-{
-  BoolType,
-  StringType,
-  NumberMinType,
-  NumberMaxType
-};
-
-template <typename PropertyType>
-PropertyType getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
-                                               const std::string& prop,
-                                               const std::string& config,
-                                               CompatibleType, PropertyType*);
-
-template <>
-bool getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
-                                       const std::string& prop,
-                                       const std::string& config,
-                                       CompatibleType /*unused*/,
-                                       bool* /*unused*/)
-{
-  return tgt->GetLinkInterfaceDependentBoolProperty(prop, config);
-}
-
-template <>
-const char* getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
-                                              const std::string& prop,
-                                              const std::string& config,
-                                              CompatibleType t,
-                                              const char** /*unused*/)
-{
-  switch (t) {
-    case BoolType:
-      assert(false &&
-             "String compatibility check function called for boolean");
-      return nullptr;
-    case StringType:
-      return tgt->GetLinkInterfaceDependentStringProperty(prop, config);
-    case NumberMinType:
-      return tgt->GetLinkInterfaceDependentNumberMinProperty(prop, config);
-    case NumberMaxType:
-      return tgt->GetLinkInterfaceDependentNumberMaxProperty(prop, config);
-  }
-  assert(false && "Unreachable!");
-  return nullptr;
-}
-
-template <typename PropertyType>
-void checkPropertyConsistency(cmGeneratorTarget const* depender,
-                              cmGeneratorTarget const* dependee,
-                              const std::string& propName,
-                              std::set<std::string>& emitted,
-                              const std::string& config, CompatibleType t,
-                              PropertyType* /*unused*/)
-{
-  cmValue prop = dependee->GetProperty(propName);
-  if (!prop) {
-    return;
-  }
-
-  cmList props{ *prop };
-  std::string pdir =
-    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/prop_tgt/");
-
-  for (std::string const& p : props) {
-    std::string pname = cmSystemTools::HelpFileName(p);
-    std::string pfile = pdir + pname + ".rst";
-    if (cmSystemTools::FileExists(pfile, true)) {
-      std::ostringstream e;
-      e << "Target \"" << dependee->GetName() << "\" has property \"" << p
-        << "\" listed in its " << propName
-        << " property.  "
-           "This is not allowed.  Only user-defined properties may appear "
-           "listed in the "
-        << propName << " property.";
-      depender->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
-                                                  e.str());
-      return;
-    }
-    if (emitted.insert(p).second) {
-      getLinkInterfaceDependentProperty<PropertyType>(depender, p, config, t,
-                                                      nullptr);
-      if (cmSystemTools::GetErrorOccurredFlag()) {
-        return;
-      }
-    }
-  }
-}
-
-namespace {
-std::string intersect(const std::set<std::string>& s1,
-                      const std::set<std::string>& s2)
-{
-  std::set<std::string> intersect;
-  std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(),
-                        std::inserter(intersect, intersect.begin()));
-  if (!intersect.empty()) {
-    return *intersect.begin();
-  }
-  return "";
-}
-
-std::string intersect(const std::set<std::string>& s1,
-                      const std::set<std::string>& s2,
-                      const std::set<std::string>& s3)
-{
-  std::string result;
-  result = intersect(s1, s2);
-  if (!result.empty()) {
-    return result;
-  }
-  result = intersect(s1, s3);
-  if (!result.empty()) {
-    return result;
-  }
-  return intersect(s2, s3);
-}
-
-std::string intersect(const std::set<std::string>& s1,
-                      const std::set<std::string>& s2,
-                      const std::set<std::string>& s3,
-                      const std::set<std::string>& s4)
-{
-  std::string result;
-  result = intersect(s1, s2);
-  if (!result.empty()) {
-    return result;
-  }
-  result = intersect(s1, s3);
-  if (!result.empty()) {
-    return result;
-  }
-  result = intersect(s1, s4);
-  if (!result.empty()) {
-    return result;
-  }
-  return intersect(s2, s3, s4);
-}
-}
-
-void cmGeneratorTarget::CheckPropertyCompatibility(
-  cmComputeLinkInformation& info, const std::string& config) const
-{
-  const cmComputeLinkInformation::ItemVector& deps = info.GetItems();
-
-  std::set<std::string> emittedBools;
-  static const std::string strBool = "COMPATIBLE_INTERFACE_BOOL";
-  std::set<std::string> emittedStrings;
-  static const std::string strString = "COMPATIBLE_INTERFACE_STRING";
-  std::set<std::string> emittedMinNumbers;
-  static const std::string strNumMin = "COMPATIBLE_INTERFACE_NUMBER_MIN";
-  std::set<std::string> emittedMaxNumbers;
-  static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX";
-
-  for (auto const& dep : deps) {
-    if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
-      continue;
-    }
-
-    checkPropertyConsistency<bool>(this, dep.Target, strBool, emittedBools,
-                                   config, BoolType, nullptr);
-    if (cmSystemTools::GetErrorOccurredFlag()) {
-      return;
-    }
-    checkPropertyConsistency<const char*>(this, dep.Target, strString,
-                                          emittedStrings, config, StringType,
-                                          nullptr);
-    if (cmSystemTools::GetErrorOccurredFlag()) {
-      return;
-    }
-    checkPropertyConsistency<const char*>(this, dep.Target, strNumMin,
-                                          emittedMinNumbers, config,
-                                          NumberMinType, nullptr);
-    if (cmSystemTools::GetErrorOccurredFlag()) {
-      return;
-    }
-    checkPropertyConsistency<const char*>(this, dep.Target, strNumMax,
-                                          emittedMaxNumbers, config,
-                                          NumberMaxType, nullptr);
-    if (cmSystemTools::GetErrorOccurredFlag()) {
-      return;
-    }
-  }
-
-  std::string prop = intersect(emittedBools, emittedStrings, emittedMinNumbers,
-                               emittedMaxNumbers);
-
-  if (!prop.empty()) {
-    // Use a sorted std::vector to keep the error message sorted.
-    std::vector<std::string> props;
-    auto i = emittedBools.find(prop);
-    if (i != emittedBools.end()) {
-      props.push_back(strBool);
-    }
-    i = emittedStrings.find(prop);
-    if (i != emittedStrings.end()) {
-      props.push_back(strString);
-    }
-    i = emittedMinNumbers.find(prop);
-    if (i != emittedMinNumbers.end()) {
-      props.push_back(strNumMin);
-    }
-    i = emittedMaxNumbers.find(prop);
-    if (i != emittedMaxNumbers.end()) {
-      props.push_back(strNumMax);
-    }
-    std::sort(props.begin(), props.end());
-
-    std::string propsString = cmStrCat(
-      cmJoin(cmMakeRange(props).retreat(1), ", "), " and the ", props.back());
-
-    std::ostringstream e;
-    e << "Property \"" << prop << "\" appears in both the " << propsString
-      << " property in the dependencies of target \"" << this->GetName()
-      << "\".  This is not allowed. A property may only require "
-         "compatibility "
-         "in a boolean interpretation, a numeric minimum, a numeric maximum "
-         "or a "
-         "string interpretation, but not a mixture.";
-    this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
-  }
-}
-
-template <typename PropertyType>
-std::string valueAsString(PropertyType);
-template <>
-std::string valueAsString<bool>(bool value)
-{
-  return value ? "TRUE" : "FALSE";
-}
-template <>
-std::string valueAsString<const char*>(const char* value)
-{
-  return value ? value : "(unset)";
-}
-template <>
-std::string valueAsString<std::string>(std::string value)
-{
-  return value;
-}
-template <>
-std::string valueAsString<cmValue>(cmValue value)
-{
-  return value ? *value : std::string("(unset)");
-}
-template <>
-std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/)
-{
-  return "(unset)";
-}
-
-static std::string compatibilityType(CompatibleType t)
-{
-  switch (t) {
-    case BoolType:
-      return "Boolean compatibility";
-    case StringType:
-      return "String compatibility";
-    case NumberMaxType:
-      return "Numeric maximum compatibility";
-    case NumberMinType:
-      return "Numeric minimum compatibility";
-  }
-  assert(false && "Unreachable!");
-  return "";
-}
-
-static std::string compatibilityAgree(CompatibleType t, bool dominant)
-{
-  switch (t) {
-    case BoolType:
-    case StringType:
-      return dominant ? "(Disagree)\n" : "(Agree)\n";
-    case NumberMaxType:
-    case NumberMinType:
-      return dominant ? "(Dominant)\n" : "(Ignored)\n";
-  }
-  assert(false && "Unreachable!");
-  return "";
-}
-
-template <typename PropertyType>
-PropertyType getTypedProperty(
-  cmGeneratorTarget const* tgt, const std::string& prop,
-  cmGeneratorExpressionInterpreter* genexInterpreter = nullptr);
-
-template <>
-bool getTypedProperty<bool>(cmGeneratorTarget const* tgt,
-                            const std::string& prop,
-                            cmGeneratorExpressionInterpreter* genexInterpreter)
-{
-  if (genexInterpreter == nullptr) {
-    return tgt->GetPropertyAsBool(prop);
-  }
-
-  cmValue value = tgt->GetProperty(prop);
-  return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop));
-}
-
-template <>
-const char* getTypedProperty<const char*>(
-  cmGeneratorTarget const* tgt, const std::string& prop,
-  cmGeneratorExpressionInterpreter* genexInterpreter)
-{
-  cmValue value = tgt->GetProperty(prop);
-
-  if (genexInterpreter == nullptr) {
-    return value.GetCStr();
-  }
-
-  return genexInterpreter->Evaluate(value ? *value : "", prop).c_str();
-}
-
-template <>
-std::string getTypedProperty<std::string>(
-  cmGeneratorTarget const* tgt, const std::string& prop,
-  cmGeneratorExpressionInterpreter* genexInterpreter)
-{
-  cmValue value = tgt->GetProperty(prop);
-
-  if (genexInterpreter == nullptr) {
-    return valueAsString(value);
-  }
-
-  return genexInterpreter->Evaluate(value ? *value : "", prop);
-}
-
-template <typename PropertyType>
-PropertyType impliedValue(PropertyType);
-template <>
-bool impliedValue<bool>(bool /*unused*/)
-{
-  return false;
-}
-template <>
-const char* impliedValue<const char*>(const char* /*unused*/)
-{
-  return "";
-}
-template <>
-std::string impliedValue<std::string>(std::string /*unused*/) // NOLINT(*)
-{
-  return std::string();
-}
-
-template <typename PropertyType>
-std::pair<bool, PropertyType> consistentProperty(PropertyType lhs,
-                                                 PropertyType rhs,
-                                                 CompatibleType t);
-
-template <>
-std::pair<bool, bool> consistentProperty(bool lhs, bool rhs,
-                                         CompatibleType /*unused*/)
-{
-  return { lhs == rhs, lhs };
-}
-
-static std::pair<bool, const char*> consistentStringProperty(const char* lhs,
-                                                             const char* rhs)
-{
-  const bool b = strcmp(lhs, rhs) == 0;
-  return { b, b ? lhs : nullptr };
-}
-
-static std::pair<bool, std::string> consistentStringProperty(
-  const std::string& lhs, const std::string& rhs)
-{
-  const bool b = lhs == rhs;
-  return { b, b ? lhs : valueAsString(nullptr) };
-}
-
-static std::pair<bool, const char*> consistentNumberProperty(const char* lhs,
-                                                             const char* rhs,
-                                                             CompatibleType t)
-{
-  char* pEnd;
-
-  long lnum = strtol(lhs, &pEnd, 0);
-  if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) {
-    return { false, nullptr };
-  }
-
-  long rnum = strtol(rhs, &pEnd, 0);
-  if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) {
-    return { false, nullptr };
-  }
-
-  if (t == NumberMaxType) {
-    return { true, std::max(lnum, rnum) == lnum ? lhs : rhs };
-  }
-
-  return { true, std::min(lnum, rnum) == lnum ? lhs : rhs };
-}
-
-template <>
-std::pair<bool, const char*> consistentProperty(const char* lhs,
-                                                const char* rhs,
-                                                CompatibleType t)
-{
-  if (!lhs && !rhs) {
-    return { true, lhs };
-  }
-  if (!lhs) {
-    return { true, rhs };
-  }
-  if (!rhs) {
-    return { true, lhs };
-  }
-
-  switch (t) {
-    case BoolType: {
-      bool same = cmIsOn(lhs) == cmIsOn(rhs);
-      return { same, same ? lhs : nullptr };
-    }
-    case StringType:
-      return consistentStringProperty(lhs, rhs);
-    case NumberMinType:
-    case NumberMaxType:
-      return consistentNumberProperty(lhs, rhs, t);
-  }
-  assert(false && "Unreachable!");
-  return { false, nullptr };
-}
-
-static std::pair<bool, std::string> consistentProperty(const std::string& lhs,
-                                                       const std::string& rhs,
-                                                       CompatibleType t)
-{
-  const std::string null_ptr = valueAsString(nullptr);
-
-  if (lhs == null_ptr && rhs == null_ptr) {
-    return { true, lhs };
-  }
-  if (lhs == null_ptr) {
-    return { true, rhs };
-  }
-  if (rhs == null_ptr) {
-    return { true, lhs };
-  }
-
-  switch (t) {
-    case BoolType: {
-      bool same = cmIsOn(lhs) == cmIsOn(rhs);
-      return { same, same ? lhs : null_ptr };
-    }
-    case StringType:
-      return consistentStringProperty(lhs, rhs);
-    case NumberMinType:
-    case NumberMaxType: {
-      auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t);
-      return { value.first,
-               value.first ? std::string(value.second) : null_ptr };
-    }
-  }
-  assert(false && "Unreachable!");
-  return { false, null_ptr };
-}
-
-template <typename PropertyType>
-PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
-                                                 const std::string& p,
-                                                 const std::string& config,
-                                                 const char* defaultValue,
-                                                 CompatibleType t,
-                                                 PropertyType* /*unused*/)
-{
-  PropertyType propContent = getTypedProperty<PropertyType>(tgt, p);
-
-  std::vector<std::string> headPropKeys = tgt->GetPropertyKeys();
-  const bool explicitlySet = cm::contains(headPropKeys, p);
-
-  const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p);
-  assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet));
-
-  std::vector<cmGeneratorTarget const*> const& deps =
-    tgt->GetLinkImplementationClosure(config);
-
-  if (deps.empty()) {
-    return propContent;
-  }
-  bool propInitialized = explicitlySet;
-
-  std::string report = cmStrCat(" * Target \"", tgt->GetName());
-  if (explicitlySet) {
-    report += "\" has property content \"";
-    report += valueAsString<PropertyType>(propContent);
-    report += "\"\n";
-  } else if (impliedByUse) {
-    report += "\" property is implied by use.\n";
-  } else {
-    report += "\" property not set.\n";
-  }
-
-  std::string interfaceProperty = "INTERFACE_" + p;
-  std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter;
-  if (p == "POSITION_INDEPENDENT_CODE") {
-    // Corresponds to EvaluatingPICExpression.
-    genexInterpreter = cm::make_unique<cmGeneratorExpressionInterpreter>(
-      tgt->GetLocalGenerator(), config, tgt);
-  }
-
-  for (cmGeneratorTarget const* theTarget : deps) {
-    // An error should be reported if one dependency
-    // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other
-    // has INTERFACE_POSITION_INDEPENDENT_CODE OFF, or if the
-    // target itself has a POSITION_INDEPENDENT_CODE which disagrees
-    // with a dependency.
-
-    std::vector<std::string> propKeys = theTarget->GetPropertyKeys();
-
-    const bool ifaceIsSet = cm::contains(propKeys, interfaceProperty);
-    PropertyType ifacePropContent = getTypedProperty<PropertyType>(
-      theTarget, interfaceProperty, genexInterpreter.get());
-
-    std::string reportEntry;
-    if (ifaceIsSet) {
-      reportEntry += " * Target \"";
-      reportEntry += theTarget->GetName();
-      reportEntry += "\" property value \"";
-      reportEntry += valueAsString<PropertyType>(ifacePropContent);
-      reportEntry += "\" ";
-    }
-
-    if (explicitlySet) {
-      if (ifaceIsSet) {
-        std::pair<bool, PropertyType> consistent =
-          consistentProperty(propContent, ifacePropContent, t);
-        report += reportEntry;
-        report += compatibilityAgree(t, propContent != consistent.second);
-        if (!consistent.first) {
-          std::ostringstream e;
-          e << "Property " << p << " on target \"" << tgt->GetName()
-            << "\" does\nnot match the "
-               "INTERFACE_"
-            << p
-            << " property requirement\nof "
-               "dependency \""
-            << theTarget->GetName() << "\".\n";
-          cmSystemTools::Error(e.str());
-          break;
-        }
-        propContent = consistent.second;
-        continue;
-      }
-      // Explicitly set on target and not set in iface. Can't disagree.
-      continue;
-    }
-    if (impliedByUse) {
-      propContent = impliedValue<PropertyType>(propContent);
-
-      if (ifaceIsSet) {
-        std::pair<bool, PropertyType> consistent =
-          consistentProperty(propContent, ifacePropContent, t);
-        report += reportEntry;
-        report += compatibilityAgree(t, propContent != consistent.second);
-        if (!consistent.first) {
-          std::ostringstream e;
-          e << "Property " << p << " on target \"" << tgt->GetName()
-            << "\" is\nimplied to be " << defaultValue
-            << " because it was used to determine the link libraries\n"
-               "already. The INTERFACE_"
-            << p << " property on\ndependency \"" << theTarget->GetName()
-            << "\" is in conflict.\n";
-          cmSystemTools::Error(e.str());
-          break;
-        }
-        propContent = consistent.second;
-        continue;
-      }
-      // Implicitly set on target and not set in iface. Can't disagree.
-      continue;
-    }
-    if (ifaceIsSet) {
-      if (propInitialized) {
-        std::pair<bool, PropertyType> consistent =
-          consistentProperty(propContent, ifacePropContent, t);
-        report += reportEntry;
-        report += compatibilityAgree(t, propContent != consistent.second);
-        if (!consistent.first) {
-          std::ostringstream e;
-          e << "The INTERFACE_" << p << " property of \""
-            << theTarget->GetName() << "\" does\nnot agree with the value of "
-            << p << " already determined\nfor \"" << tgt->GetName() << "\".\n";
-          cmSystemTools::Error(e.str());
-          break;
-        }
-        propContent = consistent.second;
-        continue;
-      }
-      report += reportEntry + "(Interface set)\n";
-      propContent = ifacePropContent;
-      propInitialized = true;
-    } else {
-      // Not set. Nothing to agree on.
-      continue;
-    }
-  }
-
-  tgt->ReportPropertyOrigin(p, valueAsString<PropertyType>(propContent),
-                            report, compatibilityType(t));
-  return propContent;
-}
-
 bool cmGeneratorTarget::SetDeviceLink(bool deviceLink)
 {
   bool previous = this->DeviceLink;
@@ -4597,41 +3924,6 @@ bool cmGeneratorTarget::SetDeviceLink(bool deviceLink)
   return previous;
 }
 
-bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty(
-  const std::string& p, const std::string& config) const
-{
-  return checkInterfacePropertyCompatibility<bool>(this, p, config, "FALSE",
-                                                   BoolType, nullptr);
-}
-
-std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty(
-  const std::string& p, const std::string& config) const
-{
-  return checkInterfacePropertyCompatibility<std::string>(
-    this, p, config, "FALSE", BoolType, nullptr);
-}
-
-const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty(
-  const std::string& p, const std::string& config) const
-{
-  return checkInterfacePropertyCompatibility<const char*>(
-    this, p, config, "empty", StringType, nullptr);
-}
-
-const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMinProperty(
-  const std::string& p, const std::string& config) const
-{
-  return checkInterfacePropertyCompatibility<const char*>(
-    this, p, config, "empty", NumberMinType, nullptr);
-}
-
-const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMaxProperty(
-  const std::string& p, const std::string& config) const
-{
-  return checkInterfacePropertyCompatibility<const char*>(
-    this, p, config, "empty", NumberMaxType, nullptr);
-}
-
 cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation(
   const std::string& config) const
 {

+ 737 - 0
Source/cmGeneratorTarget_CompatibleInterface.cxx

@@ -0,0 +1,737 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+/* clang-format off */
+#include "cmGeneratorTarget.h"
+/* clang-format on */
+
+#include <algorithm>
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <iterator>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/memory>
+#include <cmext/algorithm>
+
+#include "cmComputeLinkInformation.h"
+#include "cmGeneratorExpression.h"
+#include "cmList.h"
+#include "cmLocalGenerator.h"
+#include "cmMessageType.h"
+#include "cmRange.h"
+#include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmValue.h"
+
+const cmGeneratorTarget::CompatibleInterfacesBase&
+cmGeneratorTarget::GetCompatibleInterfaces(std::string const& config) const
+{
+  cmGeneratorTarget::CompatibleInterfaces& compat =
+    this->CompatibleInterfacesMap[config];
+  if (!compat.Done) {
+    compat.Done = true;
+    compat.PropsBool.insert("POSITION_INDEPENDENT_CODE");
+    compat.PropsString.insert("AUTOUIC_OPTIONS");
+    std::vector<cmGeneratorTarget const*> const& deps =
+      this->GetLinkImplementationClosure(config);
+    for (cmGeneratorTarget const* li : deps) {
+#define CM_READ_COMPATIBLE_INTERFACE(X, x)                                    \
+  if (cmValue prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) {           \
+    cmList props(*prop);                                                      \
+    compat.Props##x.insert(props.begin(), props.end());                       \
+  }
+      CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool)
+      CM_READ_COMPATIBLE_INTERFACE(STRING, String)
+      CM_READ_COMPATIBLE_INTERFACE(NUMBER_MIN, NumberMin)
+      CM_READ_COMPATIBLE_INTERFACE(NUMBER_MAX, NumberMax)
+#undef CM_READ_COMPATIBLE_INTERFACE
+    }
+  }
+  return compat;
+}
+
+bool cmGeneratorTarget::IsLinkInterfaceDependentBoolProperty(
+  const std::string& p, const std::string& config) const
+{
+  if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
+      this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+    return false;
+  }
+  return this->GetCompatibleInterfaces(config).PropsBool.count(p) > 0;
+}
+
+bool cmGeneratorTarget::IsLinkInterfaceDependentStringProperty(
+  const std::string& p, const std::string& config) const
+{
+  if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
+      this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+    return false;
+  }
+  return this->GetCompatibleInterfaces(config).PropsString.count(p) > 0;
+}
+
+bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMinProperty(
+  const std::string& p, const std::string& config) const
+{
+  if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
+      this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+    return false;
+  }
+  return this->GetCompatibleInterfaces(config).PropsNumberMin.count(p) > 0;
+}
+
+bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMaxProperty(
+  const std::string& p, const std::string& config) const
+{
+  if (this->GetType() == cmStateEnums::OBJECT_LIBRARY ||
+      this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+    return false;
+  }
+  return this->GetCompatibleInterfaces(config).PropsNumberMax.count(p) > 0;
+}
+
+enum CompatibleType
+{
+  BoolType,
+  StringType,
+  NumberMinType,
+  NumberMaxType
+};
+
+template <typename PropertyType>
+PropertyType getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
+                                               const std::string& prop,
+                                               const std::string& config,
+                                               CompatibleType, PropertyType*);
+
+template <>
+bool getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
+                                       const std::string& prop,
+                                       const std::string& config,
+                                       CompatibleType /*unused*/,
+                                       bool* /*unused*/)
+{
+  return tgt->GetLinkInterfaceDependentBoolProperty(prop, config);
+}
+
+template <>
+const char* getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt,
+                                              const std::string& prop,
+                                              const std::string& config,
+                                              CompatibleType t,
+                                              const char** /*unused*/)
+{
+  switch (t) {
+    case BoolType:
+      assert(false &&
+             "String compatibility check function called for boolean");
+      return nullptr;
+    case StringType:
+      return tgt->GetLinkInterfaceDependentStringProperty(prop, config);
+    case NumberMinType:
+      return tgt->GetLinkInterfaceDependentNumberMinProperty(prop, config);
+    case NumberMaxType:
+      return tgt->GetLinkInterfaceDependentNumberMaxProperty(prop, config);
+  }
+  assert(false && "Unreachable!");
+  return nullptr;
+}
+
+template <typename PropertyType>
+void checkPropertyConsistency(cmGeneratorTarget const* depender,
+                              cmGeneratorTarget const* dependee,
+                              const std::string& propName,
+                              std::set<std::string>& emitted,
+                              const std::string& config, CompatibleType t,
+                              PropertyType* /*unused*/)
+{
+  cmValue prop = dependee->GetProperty(propName);
+  if (!prop) {
+    return;
+  }
+
+  cmList props{ *prop };
+  std::string pdir =
+    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/prop_tgt/");
+
+  for (std::string const& p : props) {
+    std::string pname = cmSystemTools::HelpFileName(p);
+    std::string pfile = pdir + pname + ".rst";
+    if (cmSystemTools::FileExists(pfile, true)) {
+      std::ostringstream e;
+      e << "Target \"" << dependee->GetName() << "\" has property \"" << p
+        << "\" listed in its " << propName
+        << " property.  "
+           "This is not allowed.  Only user-defined properties may appear "
+           "listed in the "
+        << propName << " property.";
+      depender->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
+                                                  e.str());
+      return;
+    }
+    if (emitted.insert(p).second) {
+      getLinkInterfaceDependentProperty<PropertyType>(depender, p, config, t,
+                                                      nullptr);
+      if (cmSystemTools::GetErrorOccurredFlag()) {
+        return;
+      }
+    }
+  }
+}
+
+namespace {
+std::string intersect(const std::set<std::string>& s1,
+                      const std::set<std::string>& s2)
+{
+  std::set<std::string> intersect;
+  std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(),
+                        std::inserter(intersect, intersect.begin()));
+  if (!intersect.empty()) {
+    return *intersect.begin();
+  }
+  return "";
+}
+
+std::string intersect(const std::set<std::string>& s1,
+                      const std::set<std::string>& s2,
+                      const std::set<std::string>& s3)
+{
+  std::string result;
+  result = intersect(s1, s2);
+  if (!result.empty()) {
+    return result;
+  }
+  result = intersect(s1, s3);
+  if (!result.empty()) {
+    return result;
+  }
+  return intersect(s2, s3);
+}
+
+std::string intersect(const std::set<std::string>& s1,
+                      const std::set<std::string>& s2,
+                      const std::set<std::string>& s3,
+                      const std::set<std::string>& s4)
+{
+  std::string result;
+  result = intersect(s1, s2);
+  if (!result.empty()) {
+    return result;
+  }
+  result = intersect(s1, s3);
+  if (!result.empty()) {
+    return result;
+  }
+  result = intersect(s1, s4);
+  if (!result.empty()) {
+    return result;
+  }
+  return intersect(s2, s3, s4);
+}
+}
+
+void cmGeneratorTarget::CheckPropertyCompatibility(
+  cmComputeLinkInformation& info, const std::string& config) const
+{
+  const cmComputeLinkInformation::ItemVector& deps = info.GetItems();
+
+  std::set<std::string> emittedBools;
+  static const std::string strBool = "COMPATIBLE_INTERFACE_BOOL";
+  std::set<std::string> emittedStrings;
+  static const std::string strString = "COMPATIBLE_INTERFACE_STRING";
+  std::set<std::string> emittedMinNumbers;
+  static const std::string strNumMin = "COMPATIBLE_INTERFACE_NUMBER_MIN";
+  std::set<std::string> emittedMaxNumbers;
+  static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX";
+
+  for (auto const& dep : deps) {
+    if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
+      continue;
+    }
+
+    checkPropertyConsistency<bool>(this, dep.Target, strBool, emittedBools,
+                                   config, BoolType, nullptr);
+    if (cmSystemTools::GetErrorOccurredFlag()) {
+      return;
+    }
+    checkPropertyConsistency<const char*>(this, dep.Target, strString,
+                                          emittedStrings, config, StringType,
+                                          nullptr);
+    if (cmSystemTools::GetErrorOccurredFlag()) {
+      return;
+    }
+    checkPropertyConsistency<const char*>(this, dep.Target, strNumMin,
+                                          emittedMinNumbers, config,
+                                          NumberMinType, nullptr);
+    if (cmSystemTools::GetErrorOccurredFlag()) {
+      return;
+    }
+    checkPropertyConsistency<const char*>(this, dep.Target, strNumMax,
+                                          emittedMaxNumbers, config,
+                                          NumberMaxType, nullptr);
+    if (cmSystemTools::GetErrorOccurredFlag()) {
+      return;
+    }
+  }
+
+  std::string prop = intersect(emittedBools, emittedStrings, emittedMinNumbers,
+                               emittedMaxNumbers);
+
+  if (!prop.empty()) {
+    // Use a sorted std::vector to keep the error message sorted.
+    std::vector<std::string> props;
+    auto i = emittedBools.find(prop);
+    if (i != emittedBools.end()) {
+      props.push_back(strBool);
+    }
+    i = emittedStrings.find(prop);
+    if (i != emittedStrings.end()) {
+      props.push_back(strString);
+    }
+    i = emittedMinNumbers.find(prop);
+    if (i != emittedMinNumbers.end()) {
+      props.push_back(strNumMin);
+    }
+    i = emittedMaxNumbers.find(prop);
+    if (i != emittedMaxNumbers.end()) {
+      props.push_back(strNumMax);
+    }
+    std::sort(props.begin(), props.end());
+
+    std::string propsString = cmStrCat(
+      cmJoin(cmMakeRange(props).retreat(1), ", "), " and the ", props.back());
+
+    std::ostringstream e;
+    e << "Property \"" << prop << "\" appears in both the " << propsString
+      << " property in the dependencies of target \"" << this->GetName()
+      << "\".  This is not allowed. A property may only require "
+         "compatibility "
+         "in a boolean interpretation, a numeric minimum, a numeric maximum "
+         "or a "
+         "string interpretation, but not a mixture.";
+    this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  }
+}
+
+template <typename PropertyType>
+std::string valueAsString(PropertyType);
+template <>
+std::string valueAsString<bool>(bool value)
+{
+  return value ? "TRUE" : "FALSE";
+}
+template <>
+std::string valueAsString<const char*>(const char* value)
+{
+  return value ? value : "(unset)";
+}
+template <>
+std::string valueAsString<std::string>(std::string value)
+{
+  return value;
+}
+template <>
+std::string valueAsString<cmValue>(cmValue value)
+{
+  return value ? *value : std::string("(unset)");
+}
+template <>
+std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/)
+{
+  return "(unset)";
+}
+
+static std::string compatibilityType(CompatibleType t)
+{
+  switch (t) {
+    case BoolType:
+      return "Boolean compatibility";
+    case StringType:
+      return "String compatibility";
+    case NumberMaxType:
+      return "Numeric maximum compatibility";
+    case NumberMinType:
+      return "Numeric minimum compatibility";
+  }
+  assert(false && "Unreachable!");
+  return "";
+}
+
+static std::string compatibilityAgree(CompatibleType t, bool dominant)
+{
+  switch (t) {
+    case BoolType:
+    case StringType:
+      return dominant ? "(Disagree)\n" : "(Agree)\n";
+    case NumberMaxType:
+    case NumberMinType:
+      return dominant ? "(Dominant)\n" : "(Ignored)\n";
+  }
+  assert(false && "Unreachable!");
+  return "";
+}
+
+template <typename PropertyType>
+PropertyType getTypedProperty(
+  cmGeneratorTarget const* tgt, const std::string& prop,
+  cmGeneratorExpressionInterpreter* genexInterpreter = nullptr);
+
+template <>
+bool getTypedProperty<bool>(cmGeneratorTarget const* tgt,
+                            const std::string& prop,
+                            cmGeneratorExpressionInterpreter* genexInterpreter)
+{
+  if (genexInterpreter == nullptr) {
+    return tgt->GetPropertyAsBool(prop);
+  }
+
+  cmValue value = tgt->GetProperty(prop);
+  return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop));
+}
+
+template <>
+const char* getTypedProperty<const char*>(
+  cmGeneratorTarget const* tgt, const std::string& prop,
+  cmGeneratorExpressionInterpreter* genexInterpreter)
+{
+  cmValue value = tgt->GetProperty(prop);
+
+  if (genexInterpreter == nullptr) {
+    return value.GetCStr();
+  }
+
+  return genexInterpreter->Evaluate(value ? *value : "", prop).c_str();
+}
+
+template <>
+std::string getTypedProperty<std::string>(
+  cmGeneratorTarget const* tgt, const std::string& prop,
+  cmGeneratorExpressionInterpreter* genexInterpreter)
+{
+  cmValue value = tgt->GetProperty(prop);
+
+  if (genexInterpreter == nullptr) {
+    return valueAsString(value);
+  }
+
+  return genexInterpreter->Evaluate(value ? *value : "", prop);
+}
+
+template <typename PropertyType>
+PropertyType impliedValue(PropertyType);
+template <>
+bool impliedValue<bool>(bool /*unused*/)
+{
+  return false;
+}
+template <>
+const char* impliedValue<const char*>(const char* /*unused*/)
+{
+  return "";
+}
+template <>
+std::string impliedValue<std::string>(std::string /*unused*/) // NOLINT(*)
+{
+  return std::string();
+}
+
+template <typename PropertyType>
+std::pair<bool, PropertyType> consistentProperty(PropertyType lhs,
+                                                 PropertyType rhs,
+                                                 CompatibleType t);
+
+template <>
+std::pair<bool, bool> consistentProperty(bool lhs, bool rhs,
+                                         CompatibleType /*unused*/)
+{
+  return { lhs == rhs, lhs };
+}
+
+static std::pair<bool, const char*> consistentStringProperty(const char* lhs,
+                                                             const char* rhs)
+{
+  const bool b = strcmp(lhs, rhs) == 0;
+  return { b, b ? lhs : nullptr };
+}
+
+static std::pair<bool, std::string> consistentStringProperty(
+  const std::string& lhs, const std::string& rhs)
+{
+  const bool b = lhs == rhs;
+  return { b, b ? lhs : valueAsString(nullptr) };
+}
+
+static std::pair<bool, const char*> consistentNumberProperty(const char* lhs,
+                                                             const char* rhs,
+                                                             CompatibleType t)
+{
+  char* pEnd;
+
+  long lnum = strtol(lhs, &pEnd, 0);
+  if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) {
+    return { false, nullptr };
+  }
+
+  long rnum = strtol(rhs, &pEnd, 0);
+  if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) {
+    return { false, nullptr };
+  }
+
+  if (t == NumberMaxType) {
+    return { true, std::max(lnum, rnum) == lnum ? lhs : rhs };
+  }
+
+  return { true, std::min(lnum, rnum) == lnum ? lhs : rhs };
+}
+
+template <>
+std::pair<bool, const char*> consistentProperty(const char* lhs,
+                                                const char* rhs,
+                                                CompatibleType t)
+{
+  if (!lhs && !rhs) {
+    return { true, lhs };
+  }
+  if (!lhs) {
+    return { true, rhs };
+  }
+  if (!rhs) {
+    return { true, lhs };
+  }
+
+  switch (t) {
+    case BoolType: {
+      bool same = cmIsOn(lhs) == cmIsOn(rhs);
+      return { same, same ? lhs : nullptr };
+    }
+    case StringType:
+      return consistentStringProperty(lhs, rhs);
+    case NumberMinType:
+    case NumberMaxType:
+      return consistentNumberProperty(lhs, rhs, t);
+  }
+  assert(false && "Unreachable!");
+  return { false, nullptr };
+}
+
+static std::pair<bool, std::string> consistentProperty(const std::string& lhs,
+                                                       const std::string& rhs,
+                                                       CompatibleType t)
+{
+  const std::string null_ptr = valueAsString(nullptr);
+
+  if (lhs == null_ptr && rhs == null_ptr) {
+    return { true, lhs };
+  }
+  if (lhs == null_ptr) {
+    return { true, rhs };
+  }
+  if (rhs == null_ptr) {
+    return { true, lhs };
+  }
+
+  switch (t) {
+    case BoolType: {
+      bool same = cmIsOn(lhs) == cmIsOn(rhs);
+      return { same, same ? lhs : null_ptr };
+    }
+    case StringType:
+      return consistentStringProperty(lhs, rhs);
+    case NumberMinType:
+    case NumberMaxType: {
+      auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t);
+      return { value.first,
+               value.first ? std::string(value.second) : null_ptr };
+    }
+  }
+  assert(false && "Unreachable!");
+  return { false, null_ptr };
+}
+
+template <typename PropertyType>
+PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
+                                                 const std::string& p,
+                                                 const std::string& config,
+                                                 const char* defaultValue,
+                                                 CompatibleType t,
+                                                 PropertyType* /*unused*/)
+{
+  PropertyType propContent = getTypedProperty<PropertyType>(tgt, p);
+
+  std::vector<std::string> headPropKeys = tgt->GetPropertyKeys();
+  const bool explicitlySet = cm::contains(headPropKeys, p);
+
+  const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p);
+  assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet));
+
+  std::vector<cmGeneratorTarget const*> const& deps =
+    tgt->GetLinkImplementationClosure(config);
+
+  if (deps.empty()) {
+    return propContent;
+  }
+  bool propInitialized = explicitlySet;
+
+  std::string report = cmStrCat(" * Target \"", tgt->GetName());
+  if (explicitlySet) {
+    report += "\" has property content \"";
+    report += valueAsString<PropertyType>(propContent);
+    report += "\"\n";
+  } else if (impliedByUse) {
+    report += "\" property is implied by use.\n";
+  } else {
+    report += "\" property not set.\n";
+  }
+
+  std::string interfaceProperty = "INTERFACE_" + p;
+  std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter;
+  if (p == "POSITION_INDEPENDENT_CODE") {
+    // Corresponds to EvaluatingPICExpression.
+    genexInterpreter = cm::make_unique<cmGeneratorExpressionInterpreter>(
+      tgt->GetLocalGenerator(), config, tgt);
+  }
+
+  for (cmGeneratorTarget const* theTarget : deps) {
+    // An error should be reported if one dependency
+    // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other
+    // has INTERFACE_POSITION_INDEPENDENT_CODE OFF, or if the
+    // target itself has a POSITION_INDEPENDENT_CODE which disagrees
+    // with a dependency.
+
+    std::vector<std::string> propKeys = theTarget->GetPropertyKeys();
+
+    const bool ifaceIsSet = cm::contains(propKeys, interfaceProperty);
+    PropertyType ifacePropContent = getTypedProperty<PropertyType>(
+      theTarget, interfaceProperty, genexInterpreter.get());
+
+    std::string reportEntry;
+    if (ifaceIsSet) {
+      reportEntry += " * Target \"";
+      reportEntry += theTarget->GetName();
+      reportEntry += "\" property value \"";
+      reportEntry += valueAsString<PropertyType>(ifacePropContent);
+      reportEntry += "\" ";
+    }
+
+    if (explicitlySet) {
+      if (ifaceIsSet) {
+        std::pair<bool, PropertyType> consistent =
+          consistentProperty(propContent, ifacePropContent, t);
+        report += reportEntry;
+        report += compatibilityAgree(t, propContent != consistent.second);
+        if (!consistent.first) {
+          std::ostringstream e;
+          e << "Property " << p << " on target \"" << tgt->GetName()
+            << "\" does\nnot match the "
+               "INTERFACE_"
+            << p
+            << " property requirement\nof "
+               "dependency \""
+            << theTarget->GetName() << "\".\n";
+          cmSystemTools::Error(e.str());
+          break;
+        }
+        propContent = consistent.second;
+        continue;
+      }
+      // Explicitly set on target and not set in iface. Can't disagree.
+      continue;
+    }
+    if (impliedByUse) {
+      propContent = impliedValue<PropertyType>(propContent);
+
+      if (ifaceIsSet) {
+        std::pair<bool, PropertyType> consistent =
+          consistentProperty(propContent, ifacePropContent, t);
+        report += reportEntry;
+        report += compatibilityAgree(t, propContent != consistent.second);
+        if (!consistent.first) {
+          std::ostringstream e;
+          e << "Property " << p << " on target \"" << tgt->GetName()
+            << "\" is\nimplied to be " << defaultValue
+            << " because it was used to determine the link libraries\n"
+               "already. The INTERFACE_"
+            << p << " property on\ndependency \"" << theTarget->GetName()
+            << "\" is in conflict.\n";
+          cmSystemTools::Error(e.str());
+          break;
+        }
+        propContent = consistent.second;
+        continue;
+      }
+      // Implicitly set on target and not set in iface. Can't disagree.
+      continue;
+    }
+    if (ifaceIsSet) {
+      if (propInitialized) {
+        std::pair<bool, PropertyType> consistent =
+          consistentProperty(propContent, ifacePropContent, t);
+        report += reportEntry;
+        report += compatibilityAgree(t, propContent != consistent.second);
+        if (!consistent.first) {
+          std::ostringstream e;
+          e << "The INTERFACE_" << p << " property of \""
+            << theTarget->GetName() << "\" does\nnot agree with the value of "
+            << p << " already determined\nfor \"" << tgt->GetName() << "\".\n";
+          cmSystemTools::Error(e.str());
+          break;
+        }
+        propContent = consistent.second;
+        continue;
+      }
+      report += reportEntry + "(Interface set)\n";
+      propContent = ifacePropContent;
+      propInitialized = true;
+    } else {
+      // Not set. Nothing to agree on.
+      continue;
+    }
+  }
+
+  tgt->ReportPropertyOrigin(p, valueAsString<PropertyType>(propContent),
+                            report, compatibilityType(t));
+  return propContent;
+}
+
+bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty(
+  const std::string& p, const std::string& config) const
+{
+  return checkInterfacePropertyCompatibility<bool>(this, p, config, "FALSE",
+                                                   BoolType, nullptr);
+}
+
+std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty(
+  const std::string& p, const std::string& config) const
+{
+  return checkInterfacePropertyCompatibility<std::string>(
+    this, p, config, "FALSE", BoolType, nullptr);
+}
+
+const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty(
+  const std::string& p, const std::string& config) const
+{
+  return checkInterfacePropertyCompatibility<const char*>(
+    this, p, config, "empty", StringType, nullptr);
+}
+
+const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMinProperty(
+  const std::string& p, const std::string& config) const
+{
+  return checkInterfacePropertyCompatibility<const char*>(
+    this, p, config, "empty", NumberMinType, nullptr);
+}
+
+const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMaxProperty(
+  const std::string& p, const std::string& config) const
+{
+  return checkInterfacePropertyCompatibility<const char*>(
+    this, p, config, "empty", NumberMaxType, nullptr);
+}

+ 1 - 0
bootstrap

@@ -381,6 +381,7 @@ CMAKE_CXX_SOURCES="\
   cmGeneratorExpressionNode \
   cmGeneratorExpressionParser \
   cmGeneratorTarget \
+  cmGeneratorTarget_CompatibleInterface \
   cmGeneratorTarget_IncludeDirectories \
   cmGeneratorTarget_LinkDirectories \
   cmGeneratorTarget_Options \