blob: d3fadd6c6a95cfea1c630116af1ccf5a30ddd624 [file]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmRuleHelper.h"
#include <utility>
#include <cmext/algorithm>
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmStringAlgorithms.h"
namespace cm {
class RulePatternDAGChecker
{
public:
RulePatternDAGChecker(std::string pattern, RulePatternDAGChecker* parent)
: Parent(parent)
, Top(parent ? parent->Top : this)
, Pattern(std::move(pattern))
{
this->CheckResult = this->CheckGraph();
}
enum Result
{
DAG,
SELF_REFERENCE,
CYCLIC_REFERENCE
};
Result Check() const { return this->CheckResult; }
void ReportError(cmMakefile& makefile, std ::string const& rule,
std::string const& expression)
{
if (this->CheckResult == DAG) {
return;
}
RulePatternDAGChecker const* parent = this->Parent;
if (parent && !parent->Parent) {
makefile.IssueMessage(MessageType::FATAL_ERROR,
cmStrCat("Error evaluating the pattern \"",
this->Pattern,
"\" in the expression:\n"
" ",
expression,
"\n"
"Self reference on rule \"",
rule, "\".\n"));
return;
}
{
makefile.IssueMessage(MessageType::FATAL_ERROR,
cmStrCat("Error evaluating the pattern \"",
this->Pattern,
"\" in the expression:\n"
" ",
expression,
"\n"
"Dependency loop found."));
}
}
private:
Result CheckGraph() const
{
RulePatternDAGChecker const* parent = this->Parent;
while (parent) {
if (this->Pattern == parent->Pattern) {
return (parent == this->Parent) ? SELF_REFERENCE : CYCLIC_REFERENCE;
}
parent = parent->Parent;
}
return DAG;
}
RulePatternDAGChecker const* const Parent;
RulePatternDAGChecker const* const Top;
std::string const Pattern;
Result CheckResult;
};
std::string RulePatternPlaceholderExpander::ExpandVariables(
std::string const& string)
{
std::string value{ string };
return cmPlaceholderExpander::ExpandVariables(
value, cmPlaceholderExpander::HandleGenex::Yes);
}
std::string RulePatternPlaceholderExpander::ExpandVariable(
std::string const& variable)
{
if (cm::contains(this->Patterns, variable)) {
RulePatternDAGChecker dagChecker{
variable, this->DAGStack.empty() ? nullptr : this->DAGStack.top()
};
switch (dagChecker.Check()) {
case RulePatternDAGChecker::SELF_REFERENCE:
case RulePatternDAGChecker::CYCLIC_REFERENCE: {
dagChecker.ReportError(this->Makefile, this->Rule, this->Expression);
this->InError = true;
return cmStrCat('<', variable, '>');
}
case RulePatternDAGChecker::DAG:
break;
}
this->DAGStack.push(&dagChecker);
auto result = this->ExpandVariables(this->Patterns.at(variable));
this->DAGStack.pop();
return result;
}
this->InError = true;
this->Makefile.IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The pattern \"", variable, "\" is not defined."));
// If there is no variable defined, mark unresolved variable by '<' and
// '>'
return cmStrCat('<', variable, '>');
}
}