Merge topic 'doc-3.31-relnotes'
[kiteware-cmake.git] / Source / cmGeneratorExpression.cxx
blob553ea1fb9c9ba0616903ef5cd1778c26bc421166
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmGeneratorExpression.h"
5 #include <algorithm>
6 #include <cassert>
7 #include <memory>
8 #include <utility>
10 #include <cm/string_view>
12 #include "cmsys/RegularExpression.hxx"
14 #include "cmGeneratorExpressionContext.h"
15 #include "cmGeneratorExpressionDAGChecker.h"
16 #include "cmGeneratorExpressionEvaluator.h"
17 #include "cmGeneratorExpressionLexer.h"
18 #include "cmGeneratorExpressionParser.h"
19 #include "cmList.h"
20 #include "cmLocalGenerator.h"
21 #include "cmStringAlgorithms.h"
22 #include "cmSystemTools.h"
23 #include "cmake.h"
25 cmGeneratorExpression::cmGeneratorExpression(cmake& cmakeInstance,
26 cmListFileBacktrace backtrace)
27 : CMakeInstance(cmakeInstance)
28 , Backtrace(std::move(backtrace))
32 cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression() = default;
34 cmGeneratorExpression::~cmGeneratorExpression() = default;
36 std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse(
37 std::string input) const
39 return std::unique_ptr<cmCompiledGeneratorExpression>(
40 new cmCompiledGeneratorExpression(this->CMakeInstance, this->Backtrace,
41 std::move(input)));
44 std::string cmGeneratorExpression::Evaluate(
45 std::string input, cmLocalGenerator* lg, const std::string& config,
46 cmGeneratorTarget const* headTarget,
47 cmGeneratorExpressionDAGChecker* dagChecker,
48 cmGeneratorTarget const* currentTarget, std::string const& language)
50 if (Find(input) != std::string::npos) {
51 #ifndef CMAKE_BOOTSTRAP
52 auto profilingRAII = lg->GetCMakeInstance()->CreateProfilingEntry(
53 "genex_compile_eval", input);
54 #endif
56 cmCompiledGeneratorExpression cge(*lg->GetCMakeInstance(),
57 cmListFileBacktrace(), std::move(input));
58 return cge.Evaluate(lg, config, headTarget, dagChecker, currentTarget,
59 language);
61 return input;
64 const std::string& cmCompiledGeneratorExpression::Evaluate(
65 cmLocalGenerator* lg, const std::string& config,
66 const cmGeneratorTarget* headTarget,
67 cmGeneratorExpressionDAGChecker* dagChecker,
68 const cmGeneratorTarget* currentTarget, std::string const& language) const
70 cmGeneratorExpressionContext context(
71 lg, config, this->Quiet, headTarget,
72 currentTarget ? currentTarget : headTarget, this->EvaluateForBuildsystem,
73 this->Backtrace, language);
75 if (!this->NeedsEvaluation) {
76 return this->Input;
79 this->Output.clear();
81 for (const auto& it : this->Evaluators) {
82 this->Output += it->Evaluate(&context, dagChecker);
84 this->SeenTargetProperties.insert(context.SeenTargetProperties.cbegin(),
85 context.SeenTargetProperties.cend());
86 if (context.HadError) {
87 this->Output.clear();
88 break;
92 this->MaxLanguageStandard = context.MaxLanguageStandard;
94 if (!context.HadError) {
95 this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
96 this->HadHeadSensitiveCondition = context.HadHeadSensitiveCondition;
97 this->HadLinkLanguageSensitiveCondition =
98 context.HadLinkLanguageSensitiveCondition;
99 this->SourceSensitiveTargets = context.SourceSensitiveTargets;
102 this->DependTargets = context.DependTargets;
103 this->AllTargetsSeen = context.AllTargets;
104 return this->Output;
107 cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
108 cmake& cmakeInstance, cmListFileBacktrace backtrace, std::string input)
109 : Backtrace(std::move(backtrace))
110 , Input(std::move(input))
112 #ifndef CMAKE_BOOTSTRAP
113 auto profilingRAII =
114 cmakeInstance.CreateProfilingEntry("genex_compile", this->Input);
115 #endif
117 cmGeneratorExpressionLexer l;
118 std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);
119 this->NeedsEvaluation = l.GetSawGeneratorExpression();
121 if (this->NeedsEvaluation) {
122 cmGeneratorExpressionParser p(tokens);
123 p.Parse(this->Evaluators);
127 std::string cmGeneratorExpression::StripEmptyListElements(
128 const std::string& input)
130 if (input.find(';') == std::string::npos) {
131 return input;
133 std::string result;
134 result.reserve(input.size());
136 const char* c = input.c_str();
137 const char* last = c;
138 bool skipSemiColons = true;
139 for (; *c; ++c) {
140 if (*c == ';') {
141 if (skipSemiColons) {
142 result.append(last, c - last);
143 last = c + 1;
145 skipSemiColons = true;
146 } else {
147 skipSemiColons = false;
150 result.append(last);
152 if (!result.empty() && *(result.end() - 1) == ';') {
153 result.resize(result.size() - 1);
156 return result;
159 static std::string stripAllGeneratorExpressions(const std::string& input)
161 std::string result;
162 std::string::size_type pos = 0;
163 std::string::size_type lastPos = pos;
164 int nestingLevel = 0;
165 while ((pos = input.find("$<", lastPos)) != std::string::npos) {
166 result += input.substr(lastPos, pos - lastPos);
167 pos += 2;
168 nestingLevel = 1;
169 const char* c = input.c_str() + pos;
170 const char* const cStart = c;
171 for (; *c; ++c) {
172 if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
173 ++nestingLevel;
174 ++c;
175 continue;
177 if (c[0] == '>') {
178 --nestingLevel;
179 if (nestingLevel == 0) {
180 break;
184 const std::string::size_type traversed = (c - cStart) + 1;
185 if (!*c) {
186 result += "$<" + input.substr(pos, traversed);
188 pos += traversed;
189 lastPos = pos;
191 if (nestingLevel == 0) {
192 result += input.substr(lastPos);
194 return cmGeneratorExpression::StripEmptyListElements(result);
197 static void prefixItems(const std::string& content, std::string& result,
198 const cm::string_view& prefix)
200 std::vector<std::string> entries;
201 cmGeneratorExpression::Split(content, entries);
202 const char* sep = "";
203 for (std::string const& e : entries) {
204 result += sep;
205 sep = ";";
206 if (!cmSystemTools::FileIsFullPath(e) &&
207 cmGeneratorExpression::Find(e) != 0) {
208 result += prefix;
210 result += e;
214 static std::string stripExportInterface(
215 const std::string& input, cmGeneratorExpression::PreprocessContext context,
216 cm::string_view importPrefix)
218 std::string result;
220 int nestingLevel = 0;
221 std::string::size_type pos = 0;
222 std::string::size_type lastPos = pos;
223 while (true) {
224 std::string::size_type bPos = input.find("$<BUILD_INTERFACE:", lastPos);
225 std::string::size_type iPos = input.find("$<INSTALL_INTERFACE:", lastPos);
226 std::string::size_type lPos =
227 input.find("$<BUILD_LOCAL_INTERFACE:", lastPos);
229 pos = std::min({ bPos, iPos, lPos });
230 if (pos == std::string::npos) {
231 break;
234 result += input.substr(lastPos, pos - lastPos);
235 enum class FoundGenex
237 BuildInterface,
238 InstallInterface,
239 BuildLocalInterface,
240 } foundGenex = FoundGenex::BuildInterface;
241 if (pos == bPos) {
242 foundGenex = FoundGenex::BuildInterface;
243 pos += cmStrLen("$<BUILD_INTERFACE:");
244 } else if (pos == iPos) {
245 foundGenex = FoundGenex::InstallInterface;
246 pos += cmStrLen("$<INSTALL_INTERFACE:");
247 } else if (pos == lPos) {
248 foundGenex = FoundGenex::BuildLocalInterface;
249 pos += cmStrLen("$<BUILD_LOCAL_INTERFACE:");
250 } else {
251 assert(false && "Invalid position found");
253 nestingLevel = 1;
254 const char* c = input.c_str() + pos;
255 const char* const cStart = c;
256 for (; *c; ++c) {
257 if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
258 ++nestingLevel;
259 ++c;
260 continue;
262 if (c[0] == '>') {
263 --nestingLevel;
264 if (nestingLevel != 0) {
265 continue;
267 if (context == cmGeneratorExpression::BuildInterface &&
268 foundGenex == FoundGenex::BuildInterface) {
269 result += input.substr(pos, c - cStart);
270 } else if (context == cmGeneratorExpression::InstallInterface &&
271 foundGenex == FoundGenex::InstallInterface) {
272 const std::string content = input.substr(pos, c - cStart);
273 if (!importPrefix.empty()) {
274 prefixItems(content, result, importPrefix);
275 } else {
276 result += content;
279 break;
282 const std::string::size_type traversed = (c - cStart) + 1;
283 if (!*c) {
284 auto remaining = input.substr(pos, traversed);
285 switch (foundGenex) {
286 case FoundGenex::BuildInterface:
287 result = cmStrCat(result, "$<BUILD_INTERFACE:", remaining);
288 break;
289 case FoundGenex::InstallInterface:
290 result = cmStrCat(result, "$<INSTALL_INTERFACE:", remaining);
291 break;
292 case FoundGenex::BuildLocalInterface:
293 result = cmStrCat(result, "$<BUILD_LOCAL_INTERFACE:", remaining);
294 break;
297 pos += traversed;
298 lastPos = pos;
300 if (nestingLevel == 0) {
301 result += input.substr(lastPos);
304 return cmGeneratorExpression::StripEmptyListElements(result);
307 void cmGeneratorExpression::Split(const std::string& input,
308 std::vector<std::string>& output)
310 std::string::size_type pos = 0;
311 std::string::size_type lastPos = pos;
312 while ((pos = input.find("$<", lastPos)) != std::string::npos) {
313 std::string part = input.substr(lastPos, pos - lastPos);
314 std::string preGenex;
315 if (!part.empty()) {
316 std::string::size_type startPos = input.rfind(';', pos);
317 if (startPos == std::string::npos) {
318 preGenex = part;
319 part.clear();
320 } else if (startPos != pos - 1 && startPos >= lastPos) {
321 part = input.substr(lastPos, startPos - lastPos);
322 preGenex = input.substr(startPos + 1, pos - startPos - 1);
324 if (!part.empty()) {
325 cmExpandList(part, output);
328 pos += 2;
329 int nestingLevel = 1;
330 const char* c = input.c_str() + pos;
331 const char* const cStart = c;
332 for (; *c; ++c) {
333 if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
334 ++nestingLevel;
335 ++c;
336 continue;
338 if (c[0] == '>') {
339 --nestingLevel;
340 if (nestingLevel == 0) {
341 break;
345 for (; *c; ++c) {
346 // Capture the part after the genex and before the next ';'
347 if (c[0] == ';') {
348 --c;
349 break;
352 const std::string::size_type traversed = (c - cStart) + 1;
353 output.push_back(preGenex + "$<" + input.substr(pos, traversed));
354 pos += traversed;
355 lastPos = pos;
357 if (lastPos < input.size()) {
358 cmExpandList(input.substr(lastPos), output);
362 std::string cmGeneratorExpression::Preprocess(const std::string& input,
363 PreprocessContext context,
364 cm::string_view importPrefix)
366 if (context == StripAllGeneratorExpressions) {
367 return stripAllGeneratorExpressions(input);
369 if (context == BuildInterface || context == InstallInterface) {
370 return stripExportInterface(input, context, importPrefix);
373 assert(false &&
374 "cmGeneratorExpression::Preprocess called with invalid args");
375 return std::string();
378 cm::string_view::size_type cmGeneratorExpression::Find(
379 const cm::string_view& input)
381 const cm::string_view::size_type openpos = input.find("$<");
382 if (openpos != cm::string_view::npos &&
383 input.find('>', openpos) != cm::string_view::npos) {
384 return openpos;
386 return cm::string_view::npos;
389 bool cmGeneratorExpression::IsValidTargetName(const std::string& input)
391 // The ':' is supported to allow use with IMPORTED targets. At least
392 // Qt 4 and 5 IMPORTED targets use ':' as the namespace delimiter.
393 static cmsys::RegularExpression targetNameValidator("^[A-Za-z0-9_.:+-]+$");
395 return targetNameValidator.find(input);
398 void cmGeneratorExpression::ReplaceInstallPrefix(
399 std::string& input, const std::string& replacement)
401 std::string::size_type pos = 0;
402 std::string::size_type lastPos = pos;
404 while ((pos = input.find("$<INSTALL_PREFIX>", lastPos)) !=
405 std::string::npos) {
406 std::string::size_type endPos = pos + cmStrLen("$<INSTALL_PREFIX>");
407 input.replace(pos, endPos - pos, replacement);
408 lastPos = endPos;
412 void cmCompiledGeneratorExpression::GetMaxLanguageStandard(
413 const cmGeneratorTarget* tgt, std::map<std::string, std::string>& mapping)
415 auto it = this->MaxLanguageStandard.find(tgt);
416 if (it != this->MaxLanguageStandard.end()) {
417 mapping = it->second;
421 const std::string& cmGeneratorExpressionInterpreter::Evaluate(
422 std::string expression, const std::string& property)
424 this->CompiledGeneratorExpression =
425 this->GeneratorExpression.Parse(std::move(expression));
427 // Specify COMPILE_OPTIONS to DAGchecker, same semantic as COMPILE_FLAGS
428 cmGeneratorExpressionDAGChecker dagChecker(
429 this->HeadTarget,
430 property == "COMPILE_FLAGS" ? "COMPILE_OPTIONS" : property, nullptr,
431 nullptr, this->LocalGenerator, this->Config);
433 return this->CompiledGeneratorExpression->Evaluate(
434 this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr,
435 this->Language);