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 "cmsys/RegularExpression.hxx"
12 #include "cmGeneratorExpressionContext.h"
13 #include "cmGeneratorExpressionDAGChecker.h"
14 #include "cmGeneratorExpressionEvaluator.h"
15 #include "cmGeneratorExpressionLexer.h"
16 #include "cmGeneratorExpressionParser.h"
18 #include "cmLocalGenerator.h"
19 #include "cmStringAlgorithms.h"
20 #include "cmSystemTools.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
,
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
);
54 cmCompiledGeneratorExpression
cge(*lg
->GetCMakeInstance(),
55 cmListFileBacktrace(), std::move(input
));
56 return cge
.Evaluate(lg
, config
, headTarget
, dagChecker
, currentTarget
,
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
) {
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
) {
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
;
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
112 cmakeInstance
.CreateProfilingEntry("genex_compile", this->Input
);
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
) {
132 result
.reserve(input
.size());
134 const char* c
= input
.c_str();
135 const char* last
= c
;
136 bool skipSemiColons
= true;
139 if (skipSemiColons
) {
140 result
.append(last
, c
- last
);
143 skipSemiColons
= true;
145 skipSemiColons
= false;
150 if (!result
.empty() && *(result
.end() - 1) == ';') {
151 result
.resize(result
.size() - 1);
157 static std::string
stripAllGeneratorExpressions(const std::string
& input
)
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
);
167 const char* c
= input
.c_str() + pos
;
168 const char* const cStart
= c
;
170 if (cmGeneratorExpression::StartsWithGeneratorExpression(c
)) {
177 if (nestingLevel
== 0) {
182 const std::string::size_type traversed
= (c
- cStart
) + 1;
184 result
+= "$<" + input
.substr(pos
, traversed
);
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
) {
204 if (!cmSystemTools::FileIsFullPath(e
) &&
205 cmGeneratorExpression::Find(e
) != 0) {
212 static std::string
stripExportInterface(
213 const std::string
& input
, cmGeneratorExpression::PreprocessContext context
,
214 bool resolveRelative
)
218 int nestingLevel
= 0;
219 std::string::size_type pos
= 0;
220 std::string::size_type lastPos
= pos
;
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
) {
232 result
+= input
.substr(lastPos
, pos
- lastPos
);
233 enum class FoundGenex
238 } foundGenex
= FoundGenex::BuildInterface
;
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:");
249 assert(false && "Invalid position found");
252 const char* c
= input
.c_str() + pos
;
253 const char* const cStart
= c
;
255 if (cmGeneratorExpression::StartsWithGeneratorExpression(c
)) {
262 if (nestingLevel
!= 0) {
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}/");
280 const std::string::size_type traversed
= (c
- cStart
) + 1;
282 auto remaining
= input
.substr(pos
, traversed
);
283 switch (foundGenex
) {
284 case FoundGenex::BuildInterface
:
285 result
= cmStrCat(result
, "$<BUILD_INTERFACE:", remaining
);
287 case FoundGenex::InstallInterface
:
288 result
= cmStrCat(result
, "$<INSTALL_INTERFACE:", remaining
);
290 case FoundGenex::BuildLocalInterface
:
291 result
= cmStrCat(result
, "$<BUILD_LOCAL_INTERFACE:", remaining
);
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
;
314 std::string::size_type startPos
= input
.rfind(';', pos
);
315 if (startPos
== std::string::npos
) {
318 } else if (startPos
!= pos
- 1 && startPos
>= lastPos
) {
319 part
= input
.substr(lastPos
, startPos
- lastPos
);
320 preGenex
= input
.substr(startPos
+ 1, pos
- startPos
- 1);
323 cmExpandList(part
, output
);
327 int nestingLevel
= 1;
328 const char* c
= input
.c_str() + pos
;
329 const char* const cStart
= c
;
331 if (cmGeneratorExpression::StartsWithGeneratorExpression(c
)) {
338 if (nestingLevel
== 0) {
344 // Capture the part after the genex and before the next ';'
350 const std::string::size_type traversed
= (c
- cStart
) + 1;
351 output
.push_back(preGenex
+ "$<" + input
.substr(pos
, traversed
));
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
);
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
) {
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
)) !=
403 std::string::size_type endPos
= pos
+ cmStrLen("$<INSTALL_PREFIX>");
404 input
.replace(pos
, endPos
- pos
, replacement
);
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(
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,