mirror of
https://github.com/reactos/CMake.git
synced 2024-12-14 15:19:39 +00:00
f35be59961
If two imported targets in different directories have the same name we should still be able to propagate transitive usage requirements from both. Fix the DAG checker to work with target pointers instead of target names since the pointers will not be duplicated even if the names are. Fixes: #18345
238 lines
7.0 KiB
C++
238 lines
7.0 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 "cmAlgorithms.h"
|
|
#include "cmGeneratorExpressionContext.h"
|
|
#include "cmGeneratorExpressionEvaluator.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmake.h"
|
|
|
|
#include <sstream>
|
|
#include <string.h>
|
|
#include <utility>
|
|
|
|
cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
|
|
const cmListFileBacktrace& backtrace, cmGeneratorTarget const* target,
|
|
const std::string& property, const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* parent)
|
|
: Parent(parent)
|
|
, Target(target)
|
|
, Property(property)
|
|
, Content(content)
|
|
, Backtrace(backtrace)
|
|
, TransitivePropertiesOnly(false)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
|
|
cmGeneratorTarget const* target, const std::string& property,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* parent)
|
|
: Parent(parent)
|
|
, Target(target)
|
|
, Property(property)
|
|
, Content(content)
|
|
, Backtrace()
|
|
, TransitivePropertiesOnly(false)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
void cmGeneratorExpressionDAGChecker::Initialize()
|
|
{
|
|
const cmGeneratorExpressionDAGChecker* top = this;
|
|
const cmGeneratorExpressionDAGChecker* p = this->Parent;
|
|
while (p) {
|
|
top = p;
|
|
p = p->Parent;
|
|
}
|
|
this->CheckResult = this->CheckGraph();
|
|
|
|
#define TEST_TRANSITIVE_PROPERTY_METHOD(METHOD) top->METHOD() ||
|
|
|
|
if (CheckResult == DAG &&
|
|
(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
|
|
TEST_TRANSITIVE_PROPERTY_METHOD) false)) // NOLINT(clang-tidy)
|
|
#undef TEST_TRANSITIVE_PROPERTY_METHOD
|
|
{
|
|
std::map<cmGeneratorTarget const*, std::set<std::string>>::const_iterator
|
|
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;
|
|
}
|
|
}
|
|
const_cast<cmGeneratorExpressionDAGChecker*>(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(cmake::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(cmake::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(cmake::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 cmGeneratorExpressionDAGChecker* top = this;
|
|
const cmGeneratorExpressionDAGChecker* parent = this->Parent;
|
|
while (parent) {
|
|
top = parent;
|
|
parent = parent->Parent;
|
|
}
|
|
|
|
return top->TransitivePropertiesOnly;
|
|
}
|
|
|
|
bool cmGeneratorExpressionDAGChecker::EvaluatingGenexExpression()
|
|
{
|
|
const cmGeneratorExpressionDAGChecker* top = this;
|
|
const cmGeneratorExpressionDAGChecker* parent = this->Parent;
|
|
while (parent) {
|
|
top = parent;
|
|
parent = parent->Parent;
|
|
}
|
|
|
|
return top->Property == "TARGET_GENEX_EVAL" || top->Property == "GENEX_EVAL";
|
|
}
|
|
|
|
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
|
|
cmGeneratorTarget const* tgt)
|
|
{
|
|
const cmGeneratorExpressionDAGChecker* top = this;
|
|
const cmGeneratorExpressionDAGChecker* parent = this->Parent;
|
|
while (parent) {
|
|
top = parent;
|
|
parent = parent->Parent;
|
|
}
|
|
|
|
const char* prop = top->Property.c_str();
|
|
|
|
if (tgt) {
|
|
return top->Target == tgt && strcmp(prop, "LINK_LIBRARIES") == 0;
|
|
}
|
|
|
|
return (strcmp(prop, "LINK_LIBRARIES") == 0 ||
|
|
strcmp(prop, "LINK_INTERFACE_LIBRARIES") == 0 ||
|
|
strcmp(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES") == 0 ||
|
|
cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") ||
|
|
cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_")) ||
|
|
strcmp(prop, "INTERFACE_LINK_LIBRARIES") == 0;
|
|
}
|
|
|
|
cmGeneratorTarget const* cmGeneratorExpressionDAGChecker::TopTarget() const
|
|
{
|
|
const cmGeneratorExpressionDAGChecker* top = this;
|
|
const cmGeneratorExpressionDAGChecker* parent = this->Parent;
|
|
while (parent) {
|
|
top = parent;
|
|
parent = parent->Parent;
|
|
}
|
|
return 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
|