CMake/Source/cmGeneratorExpressionDAGChecker.cxx
Brad King 687a91967f Revert INTERFACE_LINK_LIBRARIES_DIRECT feature for 3.23 branch
Revert commit f3ad061858 (Add usage requirements to update direct link
dependencies, 2022-01-12, v3.23.0-rc1~44^2) and the property storage
updates in its predecessor commit 193a999cd5 (cmTarget: Add
INTERFACE_LINK_LIBRARIES_DIRECT{,_EXCLUDE} backtrace storage,
2022-01-06, v3.23.0-rc1~44^2~1) from the 3.23 release branch.

After initial experience using the feature in practice, additional
design considerations have been raised for discussion in the original
issue.  To avoid rushing this for the 3.23 series, we've decided to
revert the feature for now so it can be revised for a future release.

Issue: #22496
2022-03-21 13:17:32 -04:00

251 lines
7.4 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmGeneratorExpressionDAGChecker.h"
#include <cstring>
#include <sstream>
#include <utility>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmGeneratorExpressionContext.h"
#include "cmGeneratorExpressionEvaluator.h"
#include "cmGeneratorTarget.h"
#include "cmLocalGenerator.h"
#include "cmMessageType.h"
#include "cmStringAlgorithms.h"
#include "cmake.h"
cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
cmListFileBacktrace backtrace, cmGeneratorTarget const* target,
std::string property, const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* parent)
: Parent(parent)
, Target(target)
, Property(std::move(property))
, Content(content)
, Backtrace(std::move(backtrace))
, TransitivePropertiesOnly(false)
{
this->Initialize();
}
cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
cmGeneratorTarget const* target, std::string property,
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* parent)
: Parent(parent)
, Target(target)
, Property(std::move(property))
, Content(content)
, Backtrace()
, TransitivePropertiesOnly(false)
{
this->Initialize();
}
void cmGeneratorExpressionDAGChecker::Initialize()
{
const auto* top = this->Top();
this->CheckResult = this->CheckGraph();
#define TEST_TRANSITIVE_PROPERTY_METHOD(METHOD) top->METHOD() ||
if (this->CheckResult == DAG &&
(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
TEST_TRANSITIVE_PROPERTY_METHOD) false)) // NOLINT(*)
#undef TEST_TRANSITIVE_PROPERTY_METHOD
{
auto it = top->Seen.find(this->Target);
if (it != top->Seen.end()) {
const std::set<std::string>& propSet = it->second;
if (propSet.find(this->Property) != propSet.end()) {
this->CheckResult = ALREADY_SEEN;
return;
}
}
top->Seen[this->Target].insert(this->Property);
}
}
cmGeneratorExpressionDAGChecker::Result
cmGeneratorExpressionDAGChecker::Check() const
{
return this->CheckResult;
}
void cmGeneratorExpressionDAGChecker::ReportError(
cmGeneratorExpressionContext* context, const std::string& expr)
{
if (this->CheckResult == DAG) {
return;
}
context->HadError = true;
if (context->Quiet) {
return;
}
const cmGeneratorExpressionDAGChecker* parent = this->Parent;
if (parent && !parent->Parent) {
std::ostringstream e;
e << "Error evaluating generator expression:\n"
<< " " << expr << "\n"
<< "Self reference on target \"" << context->HeadTarget->GetName()
<< "\".\n";
context->LG->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
e.str(), parent->Backtrace);
return;
}
{
std::ostringstream e;
/* clang-format off */
e << "Error evaluating generator expression:\n"
<< " " << expr << "\n"
<< "Dependency loop found.";
/* clang-format on */
context->LG->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
e.str(), context->Backtrace);
}
int loopStep = 1;
while (parent) {
std::ostringstream e;
e << "Loop step " << loopStep << "\n"
<< " "
<< (parent->Content ? parent->Content->GetOriginalExpression() : expr)
<< "\n";
context->LG->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
e.str(), parent->Backtrace);
parent = parent->Parent;
++loopStep;
}
}
cmGeneratorExpressionDAGChecker::Result
cmGeneratorExpressionDAGChecker::CheckGraph() const
{
const cmGeneratorExpressionDAGChecker* parent = this->Parent;
while (parent) {
if (this->Target == parent->Target && this->Property == parent->Property) {
return (parent == this->Parent) ? SELF_REFERENCE : CYCLIC_REFERENCE;
}
parent = parent->Parent;
}
return DAG;
}
bool cmGeneratorExpressionDAGChecker::GetTransitivePropertiesOnly() const
{
return this->Top()->TransitivePropertiesOnly;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingGenexExpression() const
{
return cmHasLiteralPrefix(this->Property, "TARGET_GENEX_EVAL:") ||
cmHasLiteralPrefix(this->Property, "GENEX_EVAL:");
}
bool cmGeneratorExpressionDAGChecker::EvaluatingPICExpression() const
{
return this->Top()->Property == "INTERFACE_POSITION_INDEPENDENT_CODE";
}
bool cmGeneratorExpressionDAGChecker::EvaluatingCompileExpression() const
{
cm::string_view property(this->Top()->Property);
return property == "INCLUDE_DIRECTORIES"_s ||
property == "COMPILE_DEFINITIONS"_s || property == "COMPILE_OPTIONS"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const
{
cm::string_view property(this->Top()->Property);
return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s ||
property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const
{
cm::string_view property(this->Top()->Property);
return property == "LINK_OPTIONS"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
cmGeneratorTarget const* tgt) const
{
const auto* top = this->Top();
cm::string_view prop(top->Property);
if (tgt) {
return top->Target == tgt && prop == "LINK_LIBRARIES"_s;
}
return prop == "LINK_LIBRARIES"_s || prop == "INTERFACE_LINK_LIBRARIES"_s ||
prop == "INTERFACE_LINK_LIBRARIES_DIRECT"_s ||
prop == "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"_s ||
prop == "LINK_INTERFACE_LIBRARIES"_s ||
prop == "IMPORTED_LINK_INTERFACE_LIBRARIES"_s ||
cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") ||
cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_");
}
cmGeneratorExpressionDAGChecker const* cmGeneratorExpressionDAGChecker::Top()
const
{
const cmGeneratorExpressionDAGChecker* top = this;
const cmGeneratorExpressionDAGChecker* parent = this->Parent;
while (parent) {
top = parent;
parent = parent->Parent;
}
return top;
}
cmGeneratorTarget const* cmGeneratorExpressionDAGChecker::TopTarget() const
{
return this->Top()->Target;
}
enum TransitiveProperty
{
#define DEFINE_ENUM_ENTRY(NAME) NAME,
CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(DEFINE_ENUM_ENTRY)
#undef DEFINE_ENUM_ENTRY
TransitivePropertyTerminal
};
template <TransitiveProperty>
bool additionalTest(const char* const /*unused*/)
{
return false;
}
template <>
bool additionalTest<COMPILE_DEFINITIONS>(const char* const prop)
{
return cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_");
}
#define DEFINE_TRANSITIVE_PROPERTY_METHOD(METHOD, PROPERTY) \
bool cmGeneratorExpressionDAGChecker::METHOD() const \
{ \
const char* const prop = this->Property.c_str(); \
if (strcmp(prop, #PROPERTY) == 0 || \
strcmp(prop, "INTERFACE_" #PROPERTY) == 0) { \
return true; \
} \
return additionalTest<PROPERTY>(prop); \
}
CM_FOR_EACH_TRANSITIVE_PROPERTY(DEFINE_TRANSITIVE_PROPERTY_METHOD)
#undef DEFINE_TRANSITIVE_PROPERTY_METHOD