You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
742 lines
23 KiB
742 lines
23 KiB
6 months ago
|
/* 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"
|
||
|
|
||
|
namespace {
|
||
|
using UseTo = cmGeneratorTarget::UseTo;
|
||
|
}
|
||
|
|
||
|
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, UseTo::Compile);
|
||
|
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, UseTo::Compile);
|
||
|
|
||
|
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);
|
||
|
}
|