CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmGeneratorExpression.cxx
blob8e590fa8c74fddc7d97b93d8398a0e254212ff50
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 "cmsys/RegularExpression.hxx"
12 #include "cmGeneratorExpressionContext.h"
13 #include "cmGeneratorExpressionDAGChecker.h"
14 #include "cmGeneratorExpressionEvaluator.h"
15 #include "cmGeneratorExpressionLexer.h"
16 #include "cmGeneratorExpressionParser.h"
17 #include "cmList.h"
18 #include "cmLocalGenerator.h"
19 #include "cmStringAlgorithms.h"
20 #include "cmSystemTools.h"
21 #include "cmake.h"
23 cmGeneratorExpression::cmGeneratorExpression(cmake& cmakeInstance,
24 cmListFileBacktrace backtrace)
25 : CMakeInstance(cmakeInstance)
26 , Backtrace(std::move(backtrace))
30 cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression() = default;
32 cmGeneratorExpression::~cmGeneratorExpression() = default;
34 std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse(
35 std::string input) const
37 return std::unique_ptr<cmCompiledGeneratorExpression>(
38 new cmCompiledGeneratorExpression(this->CMakeInstance, this->Backtrace,
39 std::move(input)));
42 std::string cmGeneratorExpression::Evaluate(
43 std::string input, cmLocalGenerator* lg, const std::string& config,
44 cmGeneratorTarget const* headTarget,
45 cmGeneratorExpressionDAGChecker* dagChecker,
46 cmGeneratorTarget const* currentTarget, std::string const& language)
48 if (Find(input) != std::string::npos) {
49 #ifndef CMAKE_BOOTSTRAP
50 auto profilingRAII = lg->GetCMakeInstance()->CreateProfilingEntry(
51 "genex_compile_eval", input);
52 #endif
54 cmCompiledGeneratorExpression cge(*lg->GetCMakeInstance(),
55 cmListFileBacktrace(), std::move(input));
56 return cge.Evaluate(lg, config, headTarget, dagChecker, currentTarget,
57 language);
59 return input;
62 const std::string& cmCompiledGeneratorExpression::Evaluate(
63 cmLocalGenerator* lg, const std::string& config,
64 const cmGeneratorTarget* headTarget,
65 cmGeneratorExpressionDAGChecker* dagChecker,
66 const cmGeneratorTarget* currentTarget, std::string const& language) const
68 cmGeneratorExpressionContext context(
69 lg, config, this->Quiet, headTarget,
70 currentTarget ? currentTarget : headTarget, this->EvaluateForBuildsystem,
71 this->Backtrace, language);
73 if (!this->NeedsEvaluation) {
74 return this->Input;
77 this->Output.clear();
79 for (const auto& it : this->Evaluators) {
80 this->Output += it->Evaluate(&context, dagChecker);
82 this->SeenTargetProperties.insert(context.SeenTargetProperties.cbegin(),
83 context.SeenTargetProperties.cend());
84 if (context.HadError) {
85 this->Output.clear();
86 break;
90 this->MaxLanguageStandard = context.MaxLanguageStandard;
92 if (!context.HadError) {
93 this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
94 this->HadHeadSensitiveCondition = context.HadHeadSensitiveCondition;
95 this->HadLinkLanguageSensitiveCondition =
96 context.HadLinkLanguageSensitiveCondition;
97 this->SourceSensitiveTargets = context.SourceSensitiveTargets;
100 this->DependTargets = context.DependTargets;
101 this->AllTargetsSeen = context.AllTargets;
102 return this->Output;
105 cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
106 cmake& cmakeInstance, cmListFileBacktrace backtrace, std::string input)
107 : Backtrace(std::move(backtrace))
108 , Input(std::move(input))
110 #ifndef CMAKE_BOOTSTRAP
111 auto profilingRAII =
112 cmakeInstance.CreateProfilingEntry("genex_compile", this->Input);
113 #endif
115 cmGeneratorExpressionLexer l;
116 std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);
117 this->NeedsEvaluation = l.GetSawGeneratorExpression();
119 if (this->NeedsEvaluation) {
120 cmGeneratorExpressionParser p(tokens);
121 p.Parse(this->Evaluators);
125 std::string cmGeneratorExpression::StripEmptyListElements(
126 const std::string& input)
128 if (input.find(';') == std::string::npos) {
129 return input;
131 std::string result;
132 result.reserve(input.size());
134 const char* c = input.c_str();
135 const char* last = c;
136 bool skipSemiColons = true;
137 for (; *c; ++c) {
138 if (*c == ';') {
139 if (skipSemiColons) {
140 result.append(last, c - last);
141 last = c + 1;
143 skipSemiColons = true;
144 } else {
145 skipSemiColons = false;
148 result.append(last);
150 if (!result.empty() && *(result.end() - 1) == ';') {
151 result.resize(result.size() - 1);
154 return result;
157 static std::string stripAllGeneratorExpressions(const std::string& input)
159 std::string result;
160 std::string::size_type pos = 0;
161 std::string::size_type lastPos = pos;
162 int nestingLevel = 0;
163 while ((pos = input.find("$<", lastPos)) != std::string::npos) {
164 result += input.substr(lastPos, pos - lastPos);
165 pos += 2;
166 nestingLevel = 1;
167 const char* c = input.c_str() + pos;
168 const char* const cStart = c;
169 for (; *c; ++c) {
170 if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
171 ++nestingLevel;
172 ++c;
173 continue;
175 if (c[0] == '>') {
176 --nestingLevel;
177 if (nestingLevel == 0) {
178 break;
182 const std::string::size_type traversed = (c - cStart) + 1;
183 if (!*c) {
184 result += "$<" + input.substr(pos, traversed);
186 pos += traversed;
187 lastPos = pos;
189 if (nestingLevel == 0) {
190 result += input.substr(lastPos);
192 return cmGeneratorExpression::StripEmptyListElements(result);
195 static void prefixItems(const std::string& content, std::string& result,
196 const std::string& prefix)
198 std::vector<std::string> entries;
199 cmGeneratorExpression::Split(content, entries);
200 const char* sep = "";
201 for (std::string const& e : entries) {
202 result += sep;
203 sep = ";";
204 if (!cmSystemTools::FileIsFullPath(e) &&
205 cmGeneratorExpression::Find(e) != 0) {
206 result += prefix;
208 result += e;
212 static std::string stripExportInterface(
213 const std::string& input, cmGeneratorExpression::PreprocessContext context,
214 bool resolveRelative)
216 std::string result;
218 int nestingLevel = 0;
219 std::string::size_type pos = 0;
220 std::string::size_type lastPos = pos;
221 while (true) {
222 std::string::size_type bPos = input.find("$<BUILD_INTERFACE:", lastPos);
223 std::string::size_type iPos = input.find("$<INSTALL_INTERFACE:", lastPos);
224 std::string::size_type lPos =
225 input.find("$<BUILD_LOCAL_INTERFACE:", lastPos);
227 pos = std::min({ bPos, iPos, lPos });
228 if (pos == std::string::npos) {
229 break;
232 result += input.substr(lastPos, pos - lastPos);
233 enum class FoundGenex
235 BuildInterface,
236 InstallInterface,
237 BuildLocalInterface,
238 } foundGenex = FoundGenex::BuildInterface;
239 if (pos == bPos) {
240 foundGenex = FoundGenex::BuildInterface;
241 pos += cmStrLen("$<BUILD_INTERFACE:");
242 } else if (pos == iPos) {
243 foundGenex = FoundGenex::InstallInterface;
244 pos += cmStrLen("$<INSTALL_INTERFACE:");
245 } else if (pos == lPos) {
246 foundGenex = FoundGenex::BuildLocalInterface;
247 pos += cmStrLen("$<BUILD_LOCAL_INTERFACE:");
248 } else {
249 assert(false && "Invalid position found");
251 nestingLevel = 1;
252 const char* c = input.c_str() + pos;
253 const char* const cStart = c;
254 for (; *c; ++c) {
255 if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
256 ++nestingLevel;
257 ++c;
258 continue;
260 if (c[0] == '>') {
261 --nestingLevel;
262 if (nestingLevel != 0) {
263 continue;
265 if (context == cmGeneratorExpression::BuildInterface &&
266 foundGenex == FoundGenex::BuildInterface) {
267 result += input.substr(pos, c - cStart);
268 } else if (context == cmGeneratorExpression::InstallInterface &&
269 foundGenex == FoundGenex::InstallInterface) {
270 const std::string content = input.substr(pos, c - cStart);
271 if (resolveRelative) {
272 prefixItems(content, result, "${_IMPORT_PREFIX}/");
273 } else {
274 result += content;
277 break;
280 const std::string::size_type traversed = (c - cStart) + 1;
281 if (!*c) {
282 auto remaining = input.substr(pos, traversed);
283 switch (foundGenex) {
284 case FoundGenex::BuildInterface:
285 result = cmStrCat(result, "$<BUILD_INTERFACE:", remaining);
286 break;
287 case FoundGenex::InstallInterface:
288 result = cmStrCat(result, "$<INSTALL_INTERFACE:", remaining);
289 break;
290 case FoundGenex::BuildLocalInterface:
291 result = cmStrCat(result, "$<BUILD_LOCAL_INTERFACE:", remaining);
292 break;
295 pos += traversed;
296 lastPos = pos;
298 if (nestingLevel == 0) {
299 result += input.substr(lastPos);
302 return cmGeneratorExpression::StripEmptyListElements(result);
305 void cmGeneratorExpression::Split(const std::string& input,
306 std::vector<std::string>& output)
308 std::string::size_type pos = 0;
309 std::string::size_type lastPos = pos;
310 while ((pos = input.find("$<", lastPos)) != std::string::npos) {
311 std::string part = input.substr(lastPos, pos - lastPos);
312 std::string preGenex;
313 if (!part.empty()) {
314 std::string::size_type startPos = input.rfind(';', pos);
315 if (startPos == std::string::npos) {
316 preGenex = part;
317 part.clear();
318 } else if (startPos != pos - 1 && startPos >= lastPos) {
319 part = input.substr(lastPos, startPos - lastPos);
320 preGenex = input.substr(startPos + 1, pos - startPos - 1);
322 if (!part.empty()) {
323 cmExpandList(part, output);
326 pos += 2;
327 int nestingLevel = 1;
328 const char* c = input.c_str() + pos;
329 const char* const cStart = c;
330 for (; *c; ++c) {
331 if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
332 ++nestingLevel;
333 ++c;
334 continue;
336 if (c[0] == '>') {
337 --nestingLevel;
338 if (nestingLevel == 0) {
339 break;
343 for (; *c; ++c) {
344 // Capture the part after the genex and before the next ';'
345 if (c[0] == ';') {
346 --c;
347 break;
350 const std::string::size_type traversed = (c - cStart) + 1;
351 output.push_back(preGenex + "$<" + input.substr(pos, traversed));
352 pos += traversed;
353 lastPos = pos;
355 if (lastPos < input.size()) {
356 cmExpandList(input.substr(lastPos), output);
360 std::string cmGeneratorExpression::Preprocess(const std::string& input,
361 PreprocessContext context,
362 bool resolveRelative)
364 if (context == StripAllGeneratorExpressions) {
365 return stripAllGeneratorExpressions(input);
367 if (context == BuildInterface || context == InstallInterface) {
368 return stripExportInterface(input, context, resolveRelative);
371 assert(false &&
372 "cmGeneratorExpression::Preprocess called with invalid args");
373 return std::string();
376 std::string::size_type cmGeneratorExpression::Find(const std::string& input)
378 const std::string::size_type openpos = input.find("$<");
379 if (openpos != std::string::npos &&
380 input.find('>', openpos) != std::string::npos) {
381 return openpos;
383 return std::string::npos;
386 bool cmGeneratorExpression::IsValidTargetName(const std::string& input)
388 // The ':' is supported to allow use with IMPORTED targets. At least
389 // Qt 4 and 5 IMPORTED targets use ':' as the namespace delimiter.
390 static cmsys::RegularExpression targetNameValidator("^[A-Za-z0-9_.:+-]+$");
392 return targetNameValidator.find(input);
395 void cmGeneratorExpression::ReplaceInstallPrefix(
396 std::string& input, const std::string& replacement)
398 std::string::size_type pos = 0;
399 std::string::size_type lastPos = pos;
401 while ((pos = input.find("$<INSTALL_PREFIX>", lastPos)) !=
402 std::string::npos) {
403 std::string::size_type endPos = pos + cmStrLen("$<INSTALL_PREFIX>");
404 input.replace(pos, endPos - pos, replacement);
405 lastPos = endPos;
409 void cmCompiledGeneratorExpression::GetMaxLanguageStandard(
410 const cmGeneratorTarget* tgt, std::map<std::string, std::string>& mapping)
412 auto it = this->MaxLanguageStandard.find(tgt);
413 if (it != this->MaxLanguageStandard.end()) {
414 mapping = it->second;
418 const std::string& cmGeneratorExpressionInterpreter::Evaluate(
419 std::string expression, const std::string& property)
421 this->CompiledGeneratorExpression =
422 this->GeneratorExpression.Parse(std::move(expression));
424 // Specify COMPILE_OPTIONS to DAGchecker, same semantic as COMPILE_FLAGS
425 cmGeneratorExpressionDAGChecker dagChecker(
426 this->HeadTarget,
427 property == "COMPILE_FLAGS" ? "COMPILE_OPTIONS" : property, nullptr,
428 nullptr, this->LocalGenerator, this->Config);
430 return this->CompiledGeneratorExpression->Evaluate(
431 this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr,
432 this->Language);