resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmGeneratorExpression.cxx
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"
4
5 #include <cassert>
6 #include <memory>
7 #include <utility>
8
9 #include "cmsys/RegularExpression.hxx"
10
11 #include "cmGeneratorExpressionContext.h"
12 #include "cmGeneratorExpressionDAGChecker.h"
13 #include "cmGeneratorExpressionEvaluator.h"
14 #include "cmGeneratorExpressionLexer.h"
15 #include "cmGeneratorExpressionParser.h"
16 #include "cmStringAlgorithms.h"
17 #include "cmSystemTools.h"
18
19 cmGeneratorExpression::cmGeneratorExpression(cmListFileBacktrace backtrace)
20   : Backtrace(std::move(backtrace))
21 {
22 }
23
24 cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression() = default;
25
26 cmGeneratorExpression::~cmGeneratorExpression() = default;
27
28 std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse(
29   std::string input) const
30 {
31   return std::unique_ptr<cmCompiledGeneratorExpression>(
32     new cmCompiledGeneratorExpression(this->Backtrace, std::move(input)));
33 }
34
35 std::string cmGeneratorExpression::Evaluate(
36   std::string input, cmLocalGenerator* lg, const std::string& config,
37   cmGeneratorTarget const* headTarget,
38   cmGeneratorExpressionDAGChecker* dagChecker,
39   cmGeneratorTarget const* currentTarget, std::string const& language)
40 {
41   if (Find(input) != std::string::npos) {
42     cmCompiledGeneratorExpression cge(cmListFileBacktrace(), std::move(input));
43     return cge.Evaluate(lg, config, headTarget, dagChecker, currentTarget,
44                         language);
45   }
46   return input;
47 }
48
49 const std::string& cmCompiledGeneratorExpression::Evaluate(
50   cmLocalGenerator* lg, const std::string& config,
51   const cmGeneratorTarget* headTarget,
52   cmGeneratorExpressionDAGChecker* dagChecker,
53   const cmGeneratorTarget* currentTarget, std::string const& language) const
54 {
55   cmGeneratorExpressionContext context(
56     lg, config, this->Quiet, headTarget,
57     currentTarget ? currentTarget : headTarget, this->EvaluateForBuildsystem,
58     this->Backtrace, language);
59
60   return this->EvaluateWithContext(context, dagChecker);
61 }
62
63 const std::string& cmCompiledGeneratorExpression::EvaluateWithContext(
64   cmGeneratorExpressionContext& context,
65   cmGeneratorExpressionDAGChecker* dagChecker) const
66 {
67   if (!this->NeedsEvaluation) {
68     return this->Input;
69   }
70
71   this->Output.clear();
72
73   for (const auto& it : this->Evaluators) {
74     this->Output += it->Evaluate(&context, dagChecker);
75
76     this->SeenTargetProperties.insert(context.SeenTargetProperties.cbegin(),
77                                       context.SeenTargetProperties.cend());
78     if (context.HadError) {
79       this->Output.clear();
80       break;
81     }
82   }
83
84   this->MaxLanguageStandard = context.MaxLanguageStandard;
85
86   if (!context.HadError) {
87     this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
88     this->HadHeadSensitiveCondition = context.HadHeadSensitiveCondition;
89     this->HadLinkLanguageSensitiveCondition =
90       context.HadLinkLanguageSensitiveCondition;
91     this->SourceSensitiveTargets = context.SourceSensitiveTargets;
92   }
93
94   this->DependTargets = context.DependTargets;
95   this->AllTargetsSeen = context.AllTargets;
96   return this->Output;
97 }
98
99 cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
100   cmListFileBacktrace backtrace, std::string input)
101   : Backtrace(std::move(backtrace))
102   , Input(std::move(input))
103 {
104   cmGeneratorExpressionLexer l;
105   std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);
106   this->NeedsEvaluation = l.GetSawGeneratorExpression();
107
108   if (this->NeedsEvaluation) {
109     cmGeneratorExpressionParser p(tokens);
110     p.Parse(this->Evaluators);
111   }
112 }
113
114 std::string cmGeneratorExpression::StripEmptyListElements(
115   const std::string& input)
116 {
117   if (input.find(';') == std::string::npos) {
118     return input;
119   }
120   std::string result;
121   result.reserve(input.size());
122
123   const char* c = input.c_str();
124   const char* last = c;
125   bool skipSemiColons = true;
126   for (; *c; ++c) {
127     if (*c == ';') {
128       if (skipSemiColons) {
129         result.append(last, c - last);
130         last = c + 1;
131       }
132       skipSemiColons = true;
133     } else {
134       skipSemiColons = false;
135     }
136   }
137   result.append(last);
138
139   if (!result.empty() && *(result.end() - 1) == ';') {
140     result.resize(result.size() - 1);
141   }
142
143   return result;
144 }
145
146 static std::string stripAllGeneratorExpressions(const std::string& input)
147 {
148   std::string result;
149   std::string::size_type pos = 0;
150   std::string::size_type lastPos = pos;
151   int nestingLevel = 0;
152   while ((pos = input.find("$<", lastPos)) != std::string::npos) {
153     result += input.substr(lastPos, pos - lastPos);
154     pos += 2;
155     nestingLevel = 1;
156     const char* c = input.c_str() + pos;
157     const char* const cStart = c;
158     for (; *c; ++c) {
159       if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
160         ++nestingLevel;
161         ++c;
162         continue;
163       }
164       if (c[0] == '>') {
165         --nestingLevel;
166         if (nestingLevel == 0) {
167           break;
168         }
169       }
170     }
171     const std::string::size_type traversed = (c - cStart) + 1;
172     if (!*c) {
173       result += "$<" + input.substr(pos, traversed);
174     }
175     pos += traversed;
176     lastPos = pos;
177   }
178   if (nestingLevel == 0) {
179     result += input.substr(lastPos);
180   }
181   return cmGeneratorExpression::StripEmptyListElements(result);
182 }
183
184 static void prefixItems(const std::string& content, std::string& result,
185                         const std::string& prefix)
186 {
187   std::vector<std::string> entries;
188   cmGeneratorExpression::Split(content, entries);
189   const char* sep = "";
190   for (std::string const& e : entries) {
191     result += sep;
192     sep = ";";
193     if (!cmSystemTools::FileIsFullPath(e) &&
194         cmGeneratorExpression::Find(e) != 0) {
195       result += prefix;
196     }
197     result += e;
198   }
199 }
200
201 static std::string stripExportInterface(
202   const std::string& input, cmGeneratorExpression::PreprocessContext context,
203   bool resolveRelative)
204 {
205   std::string result;
206
207   int nestingLevel = 0;
208   std::string::size_type pos = 0;
209   std::string::size_type lastPos = pos;
210   while (true) {
211     std::string::size_type bPos = input.find("$<BUILD_INTERFACE:", lastPos);
212     std::string::size_type iPos = input.find("$<INSTALL_INTERFACE:", lastPos);
213
214     if (bPos == std::string::npos && iPos == std::string::npos) {
215       break;
216     }
217
218     if (bPos == std::string::npos) {
219       pos = iPos;
220     } else if (iPos == std::string::npos) {
221       pos = bPos;
222     } else {
223       pos = (bPos < iPos) ? bPos : iPos;
224     }
225
226     result += input.substr(lastPos, pos - lastPos);
227     const bool gotInstallInterface = input[pos + 2] == 'I';
228     pos += gotInstallInterface ? sizeof("$<INSTALL_INTERFACE:") - 1
229                                : sizeof("$<BUILD_INTERFACE:") - 1;
230     nestingLevel = 1;
231     const char* c = input.c_str() + pos;
232     const char* const cStart = c;
233     for (; *c; ++c) {
234       if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
235         ++nestingLevel;
236         ++c;
237         continue;
238       }
239       if (c[0] == '>') {
240         --nestingLevel;
241         if (nestingLevel != 0) {
242           continue;
243         }
244         if (context == cmGeneratorExpression::BuildInterface &&
245             !gotInstallInterface) {
246           result += input.substr(pos, c - cStart);
247         } else if (context == cmGeneratorExpression::InstallInterface &&
248                    gotInstallInterface) {
249           const std::string content = input.substr(pos, c - cStart);
250           if (resolveRelative) {
251             prefixItems(content, result, "${_IMPORT_PREFIX}/");
252           } else {
253             result += content;
254           }
255         }
256         break;
257       }
258     }
259     const std::string::size_type traversed = (c - cStart) + 1;
260     if (!*c) {
261       result += std::string(gotInstallInterface ? "$<INSTALL_INTERFACE:"
262                                                 : "$<BUILD_INTERFACE:") +
263         input.substr(pos, traversed);
264     }
265     pos += traversed;
266     lastPos = pos;
267   }
268   if (nestingLevel == 0) {
269     result += input.substr(lastPos);
270   }
271
272   return cmGeneratorExpression::StripEmptyListElements(result);
273 }
274
275 void cmGeneratorExpression::Split(const std::string& input,
276                                   std::vector<std::string>& output)
277 {
278   std::string::size_type pos = 0;
279   std::string::size_type lastPos = pos;
280   while ((pos = input.find("$<", lastPos)) != std::string::npos) {
281     std::string part = input.substr(lastPos, pos - lastPos);
282     std::string preGenex;
283     if (!part.empty()) {
284       std::string::size_type startPos = input.rfind(';', pos);
285       if (startPos == std::string::npos) {
286         preGenex = part;
287         part.clear();
288       } else if (startPos != pos - 1 && startPos >= lastPos) {
289         part = input.substr(lastPos, startPos - lastPos);
290         preGenex = input.substr(startPos + 1, pos - startPos - 1);
291       }
292       if (!part.empty()) {
293         cmExpandList(part, output);
294       }
295     }
296     pos += 2;
297     int nestingLevel = 1;
298     const char* c = input.c_str() + pos;
299     const char* const cStart = c;
300     for (; *c; ++c) {
301       if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
302         ++nestingLevel;
303         ++c;
304         continue;
305       }
306       if (c[0] == '>') {
307         --nestingLevel;
308         if (nestingLevel == 0) {
309           break;
310         }
311       }
312     }
313     for (; *c; ++c) {
314       // Capture the part after the genex and before the next ';'
315       if (c[0] == ';') {
316         --c;
317         break;
318       }
319     }
320     const std::string::size_type traversed = (c - cStart) + 1;
321     output.push_back(preGenex + "$<" + input.substr(pos, traversed));
322     pos += traversed;
323     lastPos = pos;
324   }
325   if (lastPos < input.size()) {
326     cmExpandList(input.substr(lastPos), output);
327   }
328 }
329
330 std::string cmGeneratorExpression::Preprocess(const std::string& input,
331                                               PreprocessContext context,
332                                               bool resolveRelative)
333 {
334   if (context == StripAllGeneratorExpressions) {
335     return stripAllGeneratorExpressions(input);
336   }
337   if (context == BuildInterface || context == InstallInterface) {
338     return stripExportInterface(input, context, resolveRelative);
339   }
340
341   assert(false &&
342          "cmGeneratorExpression::Preprocess called with invalid args");
343   return std::string();
344 }
345
346 std::string::size_type cmGeneratorExpression::Find(const std::string& input)
347 {
348   const std::string::size_type openpos = input.find("$<");
349   if (openpos != std::string::npos &&
350       input.find('>', openpos) != std::string::npos) {
351     return openpos;
352   }
353   return std::string::npos;
354 }
355
356 bool cmGeneratorExpression::IsValidTargetName(const std::string& input)
357 {
358   // The ':' is supported to allow use with IMPORTED targets. At least
359   // Qt 4 and 5 IMPORTED targets use ':' as the namespace delimiter.
360   static cmsys::RegularExpression targetNameValidator("^[A-Za-z0-9_.:+-]+$");
361
362   return targetNameValidator.find(input);
363 }
364
365 void cmGeneratorExpression::ReplaceInstallPrefix(
366   std::string& input, const std::string& replacement)
367 {
368   std::string::size_type pos = 0;
369   std::string::size_type lastPos = pos;
370
371   while ((pos = input.find("$<INSTALL_PREFIX>", lastPos)) !=
372          std::string::npos) {
373     std::string::size_type endPos = pos + sizeof("$<INSTALL_PREFIX>") - 1;
374     input.replace(pos, endPos - pos, replacement);
375     lastPos = endPos;
376   }
377 }
378
379 void cmCompiledGeneratorExpression::GetMaxLanguageStandard(
380   const cmGeneratorTarget* tgt, std::map<std::string, std::string>& mapping)
381 {
382   auto it = this->MaxLanguageStandard.find(tgt);
383   if (it != this->MaxLanguageStandard.end()) {
384     mapping = it->second;
385   }
386 }
387
388 const std::string& cmGeneratorExpressionInterpreter::Evaluate(
389   std::string expression, const std::string& property)
390 {
391   this->CompiledGeneratorExpression =
392     this->GeneratorExpression.Parse(std::move(expression));
393
394   // Specify COMPILE_OPTIONS to DAGchecker, same semantic as COMPILE_FLAGS
395   cmGeneratorExpressionDAGChecker dagChecker(
396     this->HeadTarget,
397     property == "COMPILE_FLAGS" ? "COMPILE_OPTIONS" : property, nullptr,
398     nullptr);
399
400   return this->CompiledGeneratorExpression->Evaluate(
401     this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr,
402     this->Language);
403 }