summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2024-05-17 15:58:46 (GMT)
committerBrad King <brad.king@kitware.com>2024-05-20 23:26:05 (GMT)
commitb4924c562a9197b84b1f511043a5b2775cb865b3 (patch)
treeeb692dd119b444377324e8f80427e78ff12bc57a
parentcdd7112a5d3ad4b5bd30aae33e82f779a6b77cfe (diff)
downloadCMake-b4924c562a9197b84b1f511043a5b2775cb865b3.zip
CMake-b4924c562a9197b84b1f511043a5b2775cb865b3.tar.gz
CMake-b4924c562a9197b84b1f511043a5b2775cb865b3.tar.bz2
cmGeneratorTarget: Factor compatible interface properties into own source
-rw-r--r--Source/CMakeLists.txt1
-rw-r--r--Source/cmGeneratorTarget.cxx708
-rw-r--r--Source/cmGeneratorTarget_CompatibleInterface.cxx737
-rwxr-xr-xbootstrap1
4 files changed, 739 insertions, 708 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 6e41fcb..5962719 100644
--- a/Source/CMakeLists.txt
+++ b/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
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index ad821b6..6238ed2 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/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
{
diff --git a/Source/cmGeneratorTarget_CompatibleInterface.cxx b/Source/cmGeneratorTarget_CompatibleInterface.cxx
new file mode 100644
index 0000000..b6eb373
--- /dev/null
+++ b/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);
+}
diff --git a/bootstrap b/bootstrap
index 9fbec59..f1d3e44 100755
--- a/bootstrap
+++ b/bootstrap
@@ -381,6 +381,7 @@ CMAKE_CXX_SOURCES="\
cmGeneratorExpressionNode \
cmGeneratorExpressionParser \
cmGeneratorTarget \
+ cmGeneratorTarget_CompatibleInterface \
cmGeneratorTarget_IncludeDirectories \
cmGeneratorTarget_LinkDirectories \
cmGeneratorTarget_Options \