1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-06-11 08:32:37 +08:00
CMake/Source/cmGeneratorExpression.cxx
Matthew Woehlke 8178fd43e9 export: Fix handling of import prefix
Fix some additional places in export generation logic that were still
hard-coding the import prefix. Change cmGeneratorExpression::Preprocess
to take the desired prefix as an argument. (This replaces taking a
boolean whether to resolve relative paths; if a non-empty prefix is
given, that is used to resolve relative paths, otherwise relative paths
are left alone.) This should ensure that import properties always spell
the prefix according to the format being generated.
2024-07-26 16:40:03 -04:00

436 lines
13 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmGeneratorExpression.h"
#include <algorithm>
#include <cassert>
#include <memory>
#include <utility>
#include <cm/string_view>
#include "cmsys/RegularExpression.hxx"
#include "cmGeneratorExpressionContext.h"
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorExpressionEvaluator.h"
#include "cmGeneratorExpressionLexer.h"
#include "cmGeneratorExpressionParser.h"
#include "cmList.h"
#include "cmLocalGenerator.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmake.h"
cmGeneratorExpression::cmGeneratorExpression(cmake& cmakeInstance,
cmListFileBacktrace backtrace)
: CMakeInstance(cmakeInstance)
, Backtrace(std::move(backtrace))
{
}
cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression() = default;
cmGeneratorExpression::~cmGeneratorExpression() = default;
std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse(
std::string input) const
{
return std::unique_ptr<cmCompiledGeneratorExpression>(
new cmCompiledGeneratorExpression(this->CMakeInstance, this->Backtrace,
std::move(input)));
}
std::string cmGeneratorExpression::Evaluate(
std::string input, cmLocalGenerator* lg, const std::string& config,
cmGeneratorTarget const* headTarget,
cmGeneratorExpressionDAGChecker* dagChecker,
cmGeneratorTarget const* currentTarget, std::string const& language)
{
if (Find(input) != std::string::npos) {
#ifndef CMAKE_BOOTSTRAP
auto profilingRAII = lg->GetCMakeInstance()->CreateProfilingEntry(
"genex_compile_eval", input);
#endif
cmCompiledGeneratorExpression cge(*lg->GetCMakeInstance(),
cmListFileBacktrace(), std::move(input));
return cge.Evaluate(lg, config, headTarget, dagChecker, currentTarget,
language);
}
return input;
}
const std::string& cmCompiledGeneratorExpression::Evaluate(
cmLocalGenerator* lg, const std::string& config,
const cmGeneratorTarget* headTarget,
cmGeneratorExpressionDAGChecker* dagChecker,
const cmGeneratorTarget* currentTarget, std::string const& language) const
{
cmGeneratorExpressionContext context(
lg, config, this->Quiet, headTarget,
currentTarget ? currentTarget : headTarget, this->EvaluateForBuildsystem,
this->Backtrace, language);
if (!this->NeedsEvaluation) {
return this->Input;
}
this->Output.clear();
for (const auto& it : this->Evaluators) {
this->Output += it->Evaluate(&context, dagChecker);
this->SeenTargetProperties.insert(context.SeenTargetProperties.cbegin(),
context.SeenTargetProperties.cend());
if (context.HadError) {
this->Output.clear();
break;
}
}
this->MaxLanguageStandard = context.MaxLanguageStandard;
if (!context.HadError) {
this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
this->HadHeadSensitiveCondition = context.HadHeadSensitiveCondition;
this->HadLinkLanguageSensitiveCondition =
context.HadLinkLanguageSensitiveCondition;
this->SourceSensitiveTargets = context.SourceSensitiveTargets;
}
this->DependTargets = context.DependTargets;
this->AllTargetsSeen = context.AllTargets;
return this->Output;
}
cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
cmake& cmakeInstance, cmListFileBacktrace backtrace, std::string input)
: Backtrace(std::move(backtrace))
, Input(std::move(input))
{
#ifndef CMAKE_BOOTSTRAP
auto profilingRAII =
cmakeInstance.CreateProfilingEntry("genex_compile", this->Input);
#endif
cmGeneratorExpressionLexer l;
std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);
this->NeedsEvaluation = l.GetSawGeneratorExpression();
if (this->NeedsEvaluation) {
cmGeneratorExpressionParser p(tokens);
p.Parse(this->Evaluators);
}
}
std::string cmGeneratorExpression::StripEmptyListElements(
const std::string& input)
{
if (input.find(';') == std::string::npos) {
return input;
}
std::string result;
result.reserve(input.size());
const char* c = input.c_str();
const char* last = c;
bool skipSemiColons = true;
for (; *c; ++c) {
if (*c == ';') {
if (skipSemiColons) {
result.append(last, c - last);
last = c + 1;
}
skipSemiColons = true;
} else {
skipSemiColons = false;
}
}
result.append(last);
if (!result.empty() && *(result.end() - 1) == ';') {
result.resize(result.size() - 1);
}
return result;
}
static std::string stripAllGeneratorExpressions(const std::string& input)
{
std::string result;
std::string::size_type pos = 0;
std::string::size_type lastPos = pos;
int nestingLevel = 0;
while ((pos = input.find("$<", lastPos)) != std::string::npos) {
result += input.substr(lastPos, pos - lastPos);
pos += 2;
nestingLevel = 1;
const char* c = input.c_str() + pos;
const char* const cStart = c;
for (; *c; ++c) {
if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
++nestingLevel;
++c;
continue;
}
if (c[0] == '>') {
--nestingLevel;
if (nestingLevel == 0) {
break;
}
}
}
const std::string::size_type traversed = (c - cStart) + 1;
if (!*c) {
result += "$<" + input.substr(pos, traversed);
}
pos += traversed;
lastPos = pos;
}
if (nestingLevel == 0) {
result += input.substr(lastPos);
}
return cmGeneratorExpression::StripEmptyListElements(result);
}
static void prefixItems(const std::string& content, std::string& result,
const cm::string_view& prefix)
{
std::vector<std::string> entries;
cmGeneratorExpression::Split(content, entries);
const char* sep = "";
for (std::string const& e : entries) {
result += sep;
sep = ";";
if (!cmSystemTools::FileIsFullPath(e) &&
cmGeneratorExpression::Find(e) != 0) {
result += prefix;
}
result += e;
}
}
static std::string stripExportInterface(
const std::string& input, cmGeneratorExpression::PreprocessContext context,
cm::string_view importPrefix)
{
std::string result;
int nestingLevel = 0;
std::string::size_type pos = 0;
std::string::size_type lastPos = pos;
while (true) {
std::string::size_type bPos = input.find("$<BUILD_INTERFACE:", lastPos);
std::string::size_type iPos = input.find("$<INSTALL_INTERFACE:", lastPos);
std::string::size_type lPos =
input.find("$<BUILD_LOCAL_INTERFACE:", lastPos);
pos = std::min({ bPos, iPos, lPos });
if (pos == std::string::npos) {
break;
}
result += input.substr(lastPos, pos - lastPos);
enum class FoundGenex
{
BuildInterface,
InstallInterface,
BuildLocalInterface,
} foundGenex = FoundGenex::BuildInterface;
if (pos == bPos) {
foundGenex = FoundGenex::BuildInterface;
pos += cmStrLen("$<BUILD_INTERFACE:");
} else if (pos == iPos) {
foundGenex = FoundGenex::InstallInterface;
pos += cmStrLen("$<INSTALL_INTERFACE:");
} else if (pos == lPos) {
foundGenex = FoundGenex::BuildLocalInterface;
pos += cmStrLen("$<BUILD_LOCAL_INTERFACE:");
} else {
assert(false && "Invalid position found");
}
nestingLevel = 1;
const char* c = input.c_str() + pos;
const char* const cStart = c;
for (; *c; ++c) {
if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
++nestingLevel;
++c;
continue;
}
if (c[0] == '>') {
--nestingLevel;
if (nestingLevel != 0) {
continue;
}
if (context == cmGeneratorExpression::BuildInterface &&
foundGenex == FoundGenex::BuildInterface) {
result += input.substr(pos, c - cStart);
} else if (context == cmGeneratorExpression::InstallInterface &&
foundGenex == FoundGenex::InstallInterface) {
const std::string content = input.substr(pos, c - cStart);
if (!importPrefix.empty()) {
prefixItems(content, result, importPrefix);
} else {
result += content;
}
}
break;
}
}
const std::string::size_type traversed = (c - cStart) + 1;
if (!*c) {
auto remaining = input.substr(pos, traversed);
switch (foundGenex) {
case FoundGenex::BuildInterface:
result = cmStrCat(result, "$<BUILD_INTERFACE:", remaining);
break;
case FoundGenex::InstallInterface:
result = cmStrCat(result, "$<INSTALL_INTERFACE:", remaining);
break;
case FoundGenex::BuildLocalInterface:
result = cmStrCat(result, "$<BUILD_LOCAL_INTERFACE:", remaining);
break;
}
}
pos += traversed;
lastPos = pos;
}
if (nestingLevel == 0) {
result += input.substr(lastPos);
}
return cmGeneratorExpression::StripEmptyListElements(result);
}
void cmGeneratorExpression::Split(const std::string& input,
std::vector<std::string>& output)
{
std::string::size_type pos = 0;
std::string::size_type lastPos = pos;
while ((pos = input.find("$<", lastPos)) != std::string::npos) {
std::string part = input.substr(lastPos, pos - lastPos);
std::string preGenex;
if (!part.empty()) {
std::string::size_type startPos = input.rfind(';', pos);
if (startPos == std::string::npos) {
preGenex = part;
part.clear();
} else if (startPos != pos - 1 && startPos >= lastPos) {
part = input.substr(lastPos, startPos - lastPos);
preGenex = input.substr(startPos + 1, pos - startPos - 1);
}
if (!part.empty()) {
cmExpandList(part, output);
}
}
pos += 2;
int nestingLevel = 1;
const char* c = input.c_str() + pos;
const char* const cStart = c;
for (; *c; ++c) {
if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
++nestingLevel;
++c;
continue;
}
if (c[0] == '>') {
--nestingLevel;
if (nestingLevel == 0) {
break;
}
}
}
for (; *c; ++c) {
// Capture the part after the genex and before the next ';'
if (c[0] == ';') {
--c;
break;
}
}
const std::string::size_type traversed = (c - cStart) + 1;
output.push_back(preGenex + "$<" + input.substr(pos, traversed));
pos += traversed;
lastPos = pos;
}
if (lastPos < input.size()) {
cmExpandList(input.substr(lastPos), output);
}
}
std::string cmGeneratorExpression::Preprocess(const std::string& input,
PreprocessContext context,
cm::string_view importPrefix)
{
if (context == StripAllGeneratorExpressions) {
return stripAllGeneratorExpressions(input);
}
if (context == BuildInterface || context == InstallInterface) {
return stripExportInterface(input, context, importPrefix);
}
assert(false &&
"cmGeneratorExpression::Preprocess called with invalid args");
return std::string();
}
std::string::size_type cmGeneratorExpression::Find(const std::string& input)
{
const std::string::size_type openpos = input.find("$<");
if (openpos != std::string::npos &&
input.find('>', openpos) != std::string::npos) {
return openpos;
}
return std::string::npos;
}
bool cmGeneratorExpression::IsValidTargetName(const std::string& input)
{
// The ':' is supported to allow use with IMPORTED targets. At least
// Qt 4 and 5 IMPORTED targets use ':' as the namespace delimiter.
static cmsys::RegularExpression targetNameValidator("^[A-Za-z0-9_.:+-]+$");
return targetNameValidator.find(input);
}
void cmGeneratorExpression::ReplaceInstallPrefix(
std::string& input, const std::string& replacement)
{
std::string::size_type pos = 0;
std::string::size_type lastPos = pos;
while ((pos = input.find("$<INSTALL_PREFIX>", lastPos)) !=
std::string::npos) {
std::string::size_type endPos = pos + cmStrLen("$<INSTALL_PREFIX>");
input.replace(pos, endPos - pos, replacement);
lastPos = endPos;
}
}
void cmCompiledGeneratorExpression::GetMaxLanguageStandard(
const cmGeneratorTarget* tgt, std::map<std::string, std::string>& mapping)
{
auto it = this->MaxLanguageStandard.find(tgt);
if (it != this->MaxLanguageStandard.end()) {
mapping = it->second;
}
}
const std::string& cmGeneratorExpressionInterpreter::Evaluate(
std::string expression, const std::string& property)
{
this->CompiledGeneratorExpression =
this->GeneratorExpression.Parse(std::move(expression));
// Specify COMPILE_OPTIONS to DAGchecker, same semantic as COMPILE_FLAGS
cmGeneratorExpressionDAGChecker dagChecker(
this->HeadTarget,
property == "COMPILE_FLAGS" ? "COMPILE_OPTIONS" : property, nullptr,
nullptr, this->LocalGenerator, this->Config);
return this->CompiledGeneratorExpression->Evaluate(
this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr,
this->Language);
}