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"
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"
20 #include "cmLocalGenerator.h"
21 #include "cmStringAlgorithms.h"
22 #include "cmSystemTools.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
,
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
);
56 cmCompiledGeneratorExpression
cge(*lg
->GetCMakeInstance(),
57 cmListFileBacktrace(), std::move(input
));
58 return cge
.Evaluate(lg
, config
, headTarget
, dagChecker
, currentTarget
,
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
) {
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
) {
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
;
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
114 cmakeInstance
.CreateProfilingEntry("genex_compile", this->Input
);
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
) {
134 result
.reserve(input
.size());
136 const char* c
= input
.c_str();
137 const char* last
= c
;
138 bool skipSemiColons
= true;
141 if (skipSemiColons
) {
142 result
.append(last
, c
- last
);
145 skipSemiColons
= true;
147 skipSemiColons
= false;
152 if (!result
.empty() && *(result
.end() - 1) == ';') {
153 result
.resize(result
.size() - 1);
159 static std::string
stripAllGeneratorExpressions(const std::string
& input
)
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
);
169 const char* c
= input
.c_str() + pos
;
170 const char* const cStart
= c
;
172 if (cmGeneratorExpression::StartsWithGeneratorExpression(c
)) {
179 if (nestingLevel
== 0) {
184 const std::string::size_type traversed
= (c
- cStart
) + 1;
186 result
+= "$<" + input
.substr(pos
, traversed
);
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
) {
206 if (!cmSystemTools::FileIsFullPath(e
) &&
207 cmGeneratorExpression::Find(e
) != 0) {
214 static std::string
stripExportInterface(
215 const std::string
& input
, cmGeneratorExpression::PreprocessContext context
,
216 cm::string_view importPrefix
)
220 int nestingLevel
= 0;
221 std::string::size_type pos
= 0;
222 std::string::size_type lastPos
= pos
;
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
) {
234 result
+= input
.substr(lastPos
, pos
- lastPos
);
235 enum class FoundGenex
240 } foundGenex
= FoundGenex::BuildInterface
;
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:");
251 assert(false && "Invalid position found");
254 const char* c
= input
.c_str() + pos
;
255 const char* const cStart
= c
;
257 if (cmGeneratorExpression::StartsWithGeneratorExpression(c
)) {
264 if (nestingLevel
!= 0) {
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
);
282 const std::string::size_type traversed
= (c
- cStart
) + 1;
284 auto remaining
= input
.substr(pos
, traversed
);
285 switch (foundGenex
) {
286 case FoundGenex::BuildInterface
:
287 result
= cmStrCat(result
, "$<BUILD_INTERFACE:", remaining
);
289 case FoundGenex::InstallInterface
:
290 result
= cmStrCat(result
, "$<INSTALL_INTERFACE:", remaining
);
292 case FoundGenex::BuildLocalInterface
:
293 result
= cmStrCat(result
, "$<BUILD_LOCAL_INTERFACE:", remaining
);
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
;
316 std::string::size_type startPos
= input
.rfind(';', pos
);
317 if (startPos
== std::string::npos
) {
320 } else if (startPos
!= pos
- 1 && startPos
>= lastPos
) {
321 part
= input
.substr(lastPos
, startPos
- lastPos
);
322 preGenex
= input
.substr(startPos
+ 1, pos
- startPos
- 1);
325 cmExpandList(part
, output
);
329 int nestingLevel
= 1;
330 const char* c
= input
.c_str() + pos
;
331 const char* const cStart
= c
;
333 if (cmGeneratorExpression::StartsWithGeneratorExpression(c
)) {
340 if (nestingLevel
== 0) {
346 // Capture the part after the genex and before the next ';'
352 const std::string::size_type traversed
= (c
- cStart
) + 1;
353 output
.push_back(preGenex
+ "$<" + input
.substr(pos
, traversed
));
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
);
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
) {
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
)) !=
406 std::string::size_type endPos
= pos
+ cmStrLen("$<INSTALL_PREFIX>");
407 input
.replace(pos
, endPos
- pos
, replacement
);
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(
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,