/src/CMake/Source/cmRule.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmRule.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <iterator> |
7 | | #include <memory> |
8 | | #include <unordered_map> |
9 | | #include <utility> |
10 | | |
11 | | #include <cm/string_view> |
12 | | #include <cmext/algorithm> |
13 | | #include <cmext/string_view> |
14 | | |
15 | | #include "cmsys/RegularExpression.hxx" |
16 | | |
17 | | #include "cmExecutionStatus.h" |
18 | | #include "cmFileSet.h" |
19 | | #include "cmFileSetMetadata.h" |
20 | | #include "cmGlobalGenerator.h" |
21 | | #include "cmList.h" |
22 | | #include "cmListFileCache.h" |
23 | | #include "cmMakefile.h" |
24 | | #include "cmMessageType.h" |
25 | | #include "cmPlaceholderExpander.h" |
26 | | #include "cmRange.h" |
27 | | #include "cmSourceFile.h" |
28 | | #include "cmStringAlgorithms.h" |
29 | | #include "cmTarget.h" |
30 | | #include "cmValue.h" |
31 | | |
32 | | namespace { |
33 | | cm::string_view const NAME = "NAME"_s; |
34 | | cm::string_view const OUTPUT = "OUTPUT"_s; |
35 | | cm::string_view const COMMAND = "COMMAND"_s; |
36 | | cm::string_view const COMMAND_COUNT = "COMMAND_COUNT"_s; |
37 | | cm::string_view const COMMAND_EXPAND_LISTS = "COMMAND_EXPAND_LISTS"_s; |
38 | | cm::string_view const COMMENT = "COMMENT"_s; |
39 | | cm::string_view const COMPILE_DEFINITIONS = "COMPILE_DEFINITIONS"_s; |
40 | | cm::string_view const COMPILE_OPTIONS = "COMPILE_OPTIONS"_s; |
41 | | cm::string_view const DEPENDS = "DEPENDS"_s; |
42 | | cm::string_view const DEPENDS_EXPLICIT_ONLY = "DEPENDS_EXPLICIT_ONLY"_s; |
43 | | cm::string_view const BYPRODUCTS = "BYPRODUCTS"_s; |
44 | | cm::string_view const DEPFILE = "DEPFILE"_s; |
45 | | cm::string_view const GLOBAL = "GLOBAL"_s; |
46 | | cm::string_view const INCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES"_s; |
47 | | cm::string_view const JOB_POOL_COMPILE = "JOB_POOL_COMPILE"_s; |
48 | | cm::string_view const JOB_SERVER_AWARE = "JOB_SERVER_AWARE"_s; |
49 | | cm::string_view const OUTPUT_FILE_SET = "OUTPUT_FILE_SET"_s; |
50 | | cm::string_view const OUTPUT_FILE_SET_BASE_DIRS = |
51 | | "OUTPUT_FILE_SET_BASE_DIRS"_s; |
52 | | cm::string_view const PARENT_RULE = "PARENT_RULE"_s; |
53 | | cm::string_view const FILE_SET_CONFIGURATORS = "FILE_SET_CONFIGURATORS"_s; |
54 | | cm::string_view const SOURCE_CONFIGURATORS = "SOURCE_CONFIGURATORS"_s; |
55 | | cm::string_view const USES_TERMINAL = "USES_TERMINAL"_s; |
56 | | cm::string_view const VERBATIM = "VERBATIM"_s; |
57 | | cm::string_view const WORKING_DIRECTORY = "WORKING_DIRECTORY"_s; |
58 | | |
59 | | cmsys::RegularExpression commandIndex("^COMMAND_[0-9]+$"); |
60 | | |
61 | | enum class ReadOnlyCondition |
62 | | { |
63 | | All, |
64 | | Configuration, |
65 | | Generation, |
66 | | }; |
67 | | |
68 | | struct ReadOnlyProperty |
69 | | { |
70 | | ReadOnlyProperty(ReadOnlyCondition cond) |
71 | 0 | : Condition{ cond } |
72 | 0 | { |
73 | 0 | } |
74 | | |
75 | | ReadOnlyCondition Condition; |
76 | | |
77 | | std::string Message(cm::string_view prop, cmRule const* rule) const |
78 | 0 | { |
79 | 0 | std::string msg; |
80 | 0 | switch (this->Condition) { |
81 | 0 | case ReadOnlyCondition::All: |
82 | 0 | msg = " property is read-only for rules (\""; |
83 | 0 | break; |
84 | 0 | case ReadOnlyCondition::Configuration: |
85 | 0 | msg = " property can't be set during configuration for rules (\""; |
86 | 0 | break; |
87 | 0 | case ReadOnlyCondition::Generation: |
88 | 0 | msg = " property can't be set during generation for rules (\""; |
89 | 0 | break; |
90 | 0 | } |
91 | 0 | return cmStrCat('"', prop, "\" ", msg, rule->GetName(), "\")\n"); |
92 | 0 | } |
93 | | |
94 | | bool IsReadOnly(cm::string_view prop, cmRule const* rule) const |
95 | 0 | { |
96 | 0 | if ((rule->InGeneration() && |
97 | 0 | this->Condition == ReadOnlyCondition::Configuration) || |
98 | 0 | (!rule->InGeneration() && |
99 | 0 | this->Condition == ReadOnlyCondition::Generation)) { |
100 | 0 | return false; |
101 | 0 | } |
102 | | |
103 | 0 | rule->GetMakefile().IssueMessage(MessageType::FATAL_ERROR, |
104 | 0 | this->Message(prop, rule)); |
105 | |
|
106 | 0 | return true; |
107 | 0 | } |
108 | | }; |
109 | | |
110 | | bool IsSettableProperty(cm::string_view prop, cmRule const* rule) |
111 | 0 | { |
112 | 0 | using ROC = ReadOnlyCondition; |
113 | 0 | static std::unordered_map<cm::string_view, ReadOnlyProperty> const |
114 | 0 | readOnlyProps{ { NAME, { ROC::All } }, |
115 | 0 | { OUTPUT, { ROC::All } }, |
116 | 0 | { COMMAND, { ROC::All } }, |
117 | 0 | { COMMAND_COUNT, { ROC::All } }, |
118 | 0 | { COMMAND_EXPAND_LISTS, { ROC::Generation } }, |
119 | 0 | { COMMENT, { ROC::Generation } }, |
120 | 0 | { COMPILE_DEFINITIONS, { ROC::Generation } }, |
121 | 0 | { COMPILE_OPTIONS, { ROC::Generation } }, |
122 | 0 | { DEPENDS, { ROC::All } }, |
123 | 0 | { DEPENDS_EXPLICIT_ONLY, { ROC::Generation } }, |
124 | 0 | { BYPRODUCTS, { ROC::All } }, |
125 | 0 | { DEPFILE, { ROC::All } }, |
126 | 0 | { GLOBAL, { ROC::Generation } }, |
127 | 0 | { INCLUDE_DIRECTORIES, { ROC::Generation } }, |
128 | 0 | { JOB_POOL_COMPILE, { ROC::Generation } }, |
129 | 0 | { JOB_SERVER_AWARE, { ROC::Generation } }, |
130 | 0 | { OUTPUT_FILE_SET, { ROC::Generation } }, |
131 | 0 | { OUTPUT_FILE_SET_BASE_DIRS, { ROC::Generation } }, |
132 | 0 | { PARENT_RULE, { ROC::All } }, |
133 | 0 | { FILE_SET_CONFIGURATORS, { ROC::All } }, |
134 | 0 | { SOURCE_CONFIGURATORS, { ROC::All } }, |
135 | 0 | { USES_TERMINAL, { ROC::Generation } }, |
136 | 0 | { VERBATIM, { ROC::Generation } }, |
137 | 0 | { WORKING_DIRECTORY, { ROC::Generation } } }; |
138 | |
|
139 | 0 | auto it = |
140 | 0 | readOnlyProps.find(commandIndex.find(prop.data()) ? COMMAND : prop); |
141 | |
|
142 | 0 | if (it != readOnlyProps.end()) { |
143 | 0 | return !(it->second.IsReadOnly(prop, rule)); |
144 | 0 | } |
145 | | |
146 | 0 | if (rule->InGeneration()) { |
147 | 0 | rule->GetMakefile().IssueMessage( |
148 | 0 | MessageType::FATAL_ERROR, |
149 | 0 | cmStrCat("rule properties cannot be changed during generation (\"", |
150 | 0 | rule->GetName(), "\")\n")); |
151 | 0 | return false; |
152 | 0 | } |
153 | | |
154 | 0 | return true; |
155 | 0 | } |
156 | | |
157 | | class FileSetNamePlaceholderExpander : public cmPlaceholderExpander |
158 | | { |
159 | | public: |
160 | | using VariableMap = std::unordered_map<std::string, std::string>; |
161 | | VariableMap Values; |
162 | | |
163 | | bool InError = false; |
164 | | |
165 | | private: |
166 | | std::string ExpandVariable(std::string const& variable) override |
167 | 0 | { |
168 | 0 | if (cm::contains(this->Values, variable)) { |
169 | 0 | return this->Values[variable]; |
170 | 0 | } |
171 | 0 | this->InError = true; |
172 | | // If there is no variable defined, mark unresolved variable by '<' and '>' |
173 | 0 | return cmStrCat('<', variable, '>'); |
174 | 0 | } |
175 | | }; |
176 | | } |
177 | | |
178 | | cmRule::cmRule(cmMakefile& makefile, std::string name, cm::RuleScope scope) |
179 | 0 | : Makefile(&makefile) |
180 | 0 | , Name(std::move(name)) |
181 | 0 | , Scope(scope) |
182 | 0 | { |
183 | | // set some useful properties |
184 | 0 | this->SetProperty("VERBATIM", cmValue::True); |
185 | 0 | this->SetProperty("COMMAND_EXPAND_LISTS", cmValue::True); |
186 | 0 | this->SetProperty("OUTPUT_FILE_SET", |
187 | 0 | "__cmake_rule_<RULE>_<TARGET>_<FILE_SET>_outputs;SOURCES"); |
188 | 0 | this->SetProperty("OUTPUT_FILE_SET_BASE_DIRS", "<CURRENT_BINARY_DIR>"); |
189 | 0 | } |
190 | | |
191 | | cmRule::cmRule(cmRule const& parent, cmMakefile& makefile, std::string name, |
192 | | cm::RuleScope scope) |
193 | 0 | : Makefile(&makefile) |
194 | 0 | , Name(std::move(name)) |
195 | 0 | , Scope(scope) |
196 | 0 | , ConfiguratorsChain(parent.ConfiguratorsChain) |
197 | 0 | , Properties(parent.Properties) |
198 | 0 | , IncludeDirectories(parent.IncludeDirectories) |
199 | 0 | , CompileOptions(parent.CompileOptions) |
200 | 0 | , CompileDefinitions(parent.CompileDefinitions) |
201 | 0 | , Generation(parent.Generation) |
202 | 0 | { |
203 | 0 | } |
204 | | std::string const& cmRule::GetParentName() const |
205 | 0 | { |
206 | 0 | static std::string empty; |
207 | |
|
208 | 0 | return empty; |
209 | 0 | } |
210 | | |
211 | | void cmRule::SetConfigurator(ConfiguratorType type, std::string configurator, |
212 | | ChainConfigurators chain) |
213 | 0 | { |
214 | 0 | this->Configurators[type] = std::move(configurator); |
215 | 0 | if (chain == ChainConfigurators::Yes) { |
216 | 0 | this->ConfiguratorsChain[type].emplace_back(&this->GetName(), |
217 | 0 | &this->Configurators[type]); |
218 | 0 | } else { |
219 | 0 | this->ConfiguratorsChain[type].assign( |
220 | 0 | 1, |
221 | 0 | ConfiguratorSet::value_type{ &this->GetName(), |
222 | 0 | &this->Configurators[type] }); |
223 | 0 | } |
224 | 0 | } |
225 | | cmRule::ConfiguratorSet const& cmRule::GetConfigurators( |
226 | | ConfiguratorType type) const |
227 | 0 | { |
228 | 0 | static ConfiguratorSet emptySet; |
229 | |
|
230 | 0 | if (this->HasConfigurators(type)) { |
231 | 0 | return this->ConfiguratorsChain.at(type); |
232 | 0 | } |
233 | 0 | return emptySet; |
234 | 0 | } |
235 | | |
236 | | bool cmRule::HasConfigurators(ConfiguratorType type) const |
237 | 0 | { |
238 | 0 | return cm::contains(this->ConfiguratorsChain, type); |
239 | 0 | } |
240 | | |
241 | | cmFileSet* cmRule::GetOutputFileSet(cmTarget* target, |
242 | | cmFileSet const* fileSet) const |
243 | 0 | { |
244 | 0 | cmList outputFileSet{ this->GetProperty(std::string{ OUTPUT_FILE_SET }) }; |
245 | 0 | FileSetNamePlaceholderExpander expander; |
246 | 0 | expander.Values["RULE"] = this->GetName(); |
247 | 0 | expander.Values["TARGET"] = target->GetName(); |
248 | 0 | expander.Values["FILE_SET"] = fileSet->GetName(); |
249 | |
|
250 | 0 | std::string fsName{ outputFileSet[0] }; |
251 | 0 | expander.ExpandVariables(fsName); |
252 | 0 | if (expander.InError) { |
253 | 0 | this->GetMakefile().IssueMessage( |
254 | 0 | MessageType::FATAL_ERROR, |
255 | 0 | cmStrCat("Output File Set name, for rule \"", this->GetName(), |
256 | 0 | "\", did not expand correctly:\n \"", fsName, "\".")); |
257 | 0 | return nullptr; |
258 | 0 | } |
259 | | |
260 | 0 | auto result = target->GetOrCreateFileSet(fsName, outputFileSet[1], |
261 | 0 | fileSet->GetVisibility()); |
262 | 0 | if (!result.second && result.first->GetType() != outputFileSet[1]) { |
263 | 0 | this->GetMakefile().IssueMessage( |
264 | 0 | MessageType::FATAL_ERROR, |
265 | 0 | cmStrCat("The output file set \"", fsName, "\", for the target \"", |
266 | 0 | target->GetName(), "\", has the type \"", |
267 | 0 | result.first->GetType(), "\" instead of \"", outputFileSet[1], |
268 | 0 | "\", as specified by the rule \"", this->GetName(), "\".")); |
269 | 0 | return nullptr; |
270 | 0 | } |
271 | | |
272 | 0 | if (result.first->GetDirectoryEntries().empty()) { |
273 | 0 | if (cmValue baseDirs = |
274 | 0 | this->GetProperty(std::string{ OUTPUT_FILE_SET_BASE_DIRS })) { |
275 | 0 | expander.Values["CURRENT_SOURCE_DIR"] = |
276 | 0 | fileSet->GetMakefile()->GetCurrentSourceDirectory(); |
277 | 0 | expander.Values["CURRENT_BINARY_DIR"] = |
278 | 0 | fileSet->GetMakefile()->GetCurrentBinaryDirectory(); |
279 | 0 | expander.Values["OUTPUT_CURRENT_SOURCE_DIR"] = |
280 | 0 | result.first->GetMakefile()->GetCurrentSourceDirectory(); |
281 | 0 | expander.Values["OUTPUT_CURRENT_BINARY_DIR"] = |
282 | 0 | result.first->GetMakefile()->GetCurrentBinaryDirectory(); |
283 | 0 | std::string dirs{ baseDirs }; |
284 | 0 | expander.ExpandVariables(dirs); |
285 | 0 | result.first->AddDirectoryEntry(BT<std::string>{ dirs }); |
286 | 0 | } else { |
287 | | // Add default base directory if none are specified |
288 | 0 | result.first->AddDirectoryEntry(BT<std::string>{ |
289 | 0 | fileSet->GetMakefile()->GetCurrentBinaryDirectory() }); |
290 | 0 | } |
291 | 0 | } |
292 | |
|
293 | 0 | return result.first; |
294 | 0 | } |
295 | | |
296 | | cmBTStringRange cmRule::GetIncludeDirectories() const |
297 | 0 | { |
298 | 0 | return cmMakeRange(this->IncludeDirectories); |
299 | 0 | } |
300 | | |
301 | | cmBTStringRange cmRule::GetCompileOptions() const |
302 | 0 | { |
303 | 0 | return cmMakeRange(this->CompileOptions); |
304 | 0 | } |
305 | | |
306 | | cmBTStringRange cmRule::GetCompileDefinitions() const |
307 | 0 | { |
308 | 0 | return cmMakeRange(this->CompileDefinitions); |
309 | 0 | } |
310 | | |
311 | | void cmRule::SetProperty(std::string const& prop, cmValue value) |
312 | 0 | { |
313 | 0 | if (!IsSettableProperty(prop, this)) { |
314 | 0 | return; |
315 | 0 | } |
316 | | |
317 | 0 | if (prop == GLOBAL) { |
318 | 0 | if (!value.IsOn()) { |
319 | 0 | this->GetMakefile().IssueMessage( |
320 | 0 | MessageType::FATAL_ERROR, |
321 | 0 | cmStrCat("GLOBAL property can't be set to FALSE on rules (\"", |
322 | 0 | this->GetName(), "\")")); |
323 | 0 | return; |
324 | 0 | } |
325 | | /* no need to change anything if value does not change */ |
326 | 0 | if (!this->IsGloballyVisible()) { |
327 | 0 | this->Scope = cm::RuleScope::Global; |
328 | 0 | this->GetMakefile().GetGlobalGenerator()->IndexRule(this); |
329 | 0 | } |
330 | 0 | } else if (prop == OUTPUT_FILE_SET) { |
331 | 0 | cmList fileSet{ value }; |
332 | 0 | if (fileSet.size() != 2) { |
333 | 0 | this->GetMakefile().IssueMessage( |
334 | 0 | MessageType::FATAL_ERROR, |
335 | 0 | "OUTPUT_FILE_SET property require a list of 2 elements:\n " |
336 | 0 | "\"name;type\""); |
337 | 0 | return; |
338 | 0 | } |
339 | 0 | if (!cm::FileSetMetadata::IsKnownType(fileSet[1])) { |
340 | 0 | this->GetMakefile().IssueMessage( |
341 | 0 | MessageType::FATAL_ERROR, |
342 | 0 | cmStrCat( |
343 | 0 | "specified file set type is erroneous. The supported types are: ", |
344 | 0 | cmJoin(cm::FileSetMetadata::GetKnownTypes(), ", "), '.')); |
345 | 0 | return; |
346 | 0 | } |
347 | 0 | this->Properties.SetProperty(prop, value); |
348 | 0 | } else if (prop == INCLUDE_DIRECTORIES) { |
349 | 0 | this->IncludeDirectories.clear(); |
350 | 0 | if (value) { |
351 | 0 | cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
352 | 0 | this->IncludeDirectories.emplace_back(value, lfbt); |
353 | 0 | } |
354 | 0 | } else if (prop == COMPILE_OPTIONS) { |
355 | 0 | this->CompileOptions.clear(); |
356 | 0 | if (value) { |
357 | 0 | cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
358 | 0 | this->CompileOptions.emplace_back(value, lfbt); |
359 | 0 | } |
360 | 0 | } else if (prop == COMPILE_DEFINITIONS) { |
361 | 0 | this->CompileDefinitions.clear(); |
362 | 0 | if (value) { |
363 | 0 | cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
364 | 0 | this->CompileDefinitions.emplace_back(value, lfbt); |
365 | 0 | } |
366 | 0 | } else { |
367 | 0 | this->Properties.SetProperty(prop, value); |
368 | 0 | } |
369 | 0 | } |
370 | | |
371 | | void cmRule::AppendProperty(std::string const& prop, std::string const& value, |
372 | | bool asString) |
373 | 0 | { |
374 | 0 | if (!IsSettableProperty(prop, this)) { |
375 | 0 | return; |
376 | 0 | } |
377 | | |
378 | 0 | if (prop == GLOBAL) { |
379 | 0 | this->GetMakefile().IssueMessage( |
380 | 0 | MessageType::FATAL_ERROR, |
381 | 0 | cmStrCat("GLOBAL property can't be appended, only set on rules (\"", |
382 | 0 | this->GetName(), "\")\n")); |
383 | 0 | return; |
384 | 0 | } |
385 | | |
386 | 0 | if (prop == INCLUDE_DIRECTORIES) { |
387 | 0 | if (!value.empty()) { |
388 | 0 | cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
389 | 0 | this->IncludeDirectories.emplace_back(value, lfbt); |
390 | 0 | } |
391 | 0 | } else if (prop == COMPILE_OPTIONS) { |
392 | 0 | if (!value.empty()) { |
393 | 0 | cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
394 | 0 | this->CompileOptions.emplace_back(value, lfbt); |
395 | 0 | } |
396 | 0 | } else if (prop == COMPILE_DEFINITIONS) { |
397 | 0 | if (!value.empty()) { |
398 | 0 | cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
399 | 0 | this->CompileDefinitions.emplace_back(value, lfbt); |
400 | 0 | } |
401 | 0 | } else { |
402 | 0 | this->Properties.AppendProperty(prop, value, asString); |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | | cmValue cmRule::GetProperty(std::string const& prop) const |
407 | 0 | { |
408 | 0 | static std::string value; |
409 | |
|
410 | 0 | if (prop == NAME) { |
411 | 0 | return cmValue{ this->GetName() }; |
412 | 0 | } |
413 | 0 | if (prop == OUTPUT) { |
414 | 0 | value = cmList::to_string(this->GetOutputs()); |
415 | 0 | return cmValue{ value }; |
416 | 0 | } |
417 | 0 | if (prop == COMMAND) { |
418 | 0 | value = cmList::to_string(this->GetCommands()[0]); |
419 | 0 | return cmValue{ value }; |
420 | 0 | } |
421 | 0 | if (prop == COMMAND_COUNT) { |
422 | 0 | value = std::to_string(this->GetCommands().size()); |
423 | 0 | return cmValue{ value }; |
424 | 0 | } |
425 | 0 | if (commandIndex.find(prop)) { |
426 | 0 | auto index = std::stoul(prop.substr(8)); |
427 | 0 | value = index >= this->GetCommands().size() |
428 | 0 | ? "NOTFOUND" |
429 | 0 | : cmList::to_string(this->GetCommands()[index]); |
430 | 0 | return cmValue{ value }; |
431 | 0 | } |
432 | 0 | if (prop == DEPENDS) { |
433 | 0 | value = cmList::to_string(this->GetDepends()); |
434 | 0 | return cmValue{ value }; |
435 | 0 | } |
436 | 0 | if (prop == BYPRODUCTS) { |
437 | 0 | value = cmList::to_string(this->GetByproducts()); |
438 | 0 | return cmValue{ value }; |
439 | 0 | } |
440 | 0 | if (prop == DEPFILE) { |
441 | 0 | return cmValue{ this->GetDepfile() }; |
442 | 0 | } |
443 | 0 | if (prop == GLOBAL) { |
444 | 0 | return this->IsGloballyVisible() ? cmValue::True : cmValue::False; |
445 | 0 | } |
446 | 0 | if (prop == PARENT_RULE) { |
447 | 0 | return cmValue{ this->GetParentName() }; |
448 | 0 | } |
449 | 0 | if (prop == FILE_SET_CONFIGURATORS || prop == SOURCE_CONFIGURATORS) { |
450 | 0 | ConfiguratorType type = prop == FILE_SET_CONFIGURATORS |
451 | 0 | ? ConfiguratorType::FileSet |
452 | 0 | : ConfiguratorType::Source; |
453 | |
|
454 | 0 | if (this->HasConfigurators(type)) { |
455 | 0 | auto cfgs_set = this->GetConfigurators(type); |
456 | 0 | std::vector<std::string> cfgs; |
457 | 0 | cfgs.reserve(cfgs_set.size()); |
458 | |
|
459 | 0 | std::transform(cfgs_set.cbegin(), cfgs_set.cend(), |
460 | 0 | std::back_inserter(cfgs), |
461 | 0 | [](ConfiguratorSet::value_type cfg) -> std::string { |
462 | 0 | return *cfg.second; |
463 | 0 | }); |
464 | |
|
465 | 0 | value = cmList::to_string(cfgs); |
466 | 0 | return cmValue{ value }; |
467 | 0 | } |
468 | | |
469 | 0 | return nullptr; |
470 | 0 | } |
471 | | |
472 | | // Check for the properties with backtraces. |
473 | 0 | if (prop == INCLUDE_DIRECTORIES) { |
474 | 0 | if (this->IncludeDirectories.empty()) { |
475 | 0 | return nullptr; |
476 | 0 | } |
477 | | |
478 | 0 | value = cmList::to_string(this->IncludeDirectories); |
479 | 0 | return cmValue{ value }; |
480 | 0 | } |
481 | | |
482 | 0 | if (prop == COMPILE_OPTIONS) { |
483 | 0 | if (this->CompileOptions.empty()) { |
484 | 0 | return nullptr; |
485 | 0 | } |
486 | | |
487 | 0 | value = cmList::to_string(this->CompileOptions); |
488 | 0 | return cmValue{ value }; |
489 | 0 | } |
490 | | |
491 | 0 | if (prop == COMPILE_DEFINITIONS) { |
492 | 0 | if (this->CompileDefinitions.empty()) { |
493 | 0 | return nullptr; |
494 | 0 | } |
495 | | |
496 | 0 | value = cmList::to_string(this->CompileDefinitions); |
497 | 0 | return cmValue{ value }; |
498 | 0 | } |
499 | | |
500 | 0 | return this->Properties.GetPropertyValue(prop); |
501 | 0 | } |
502 | | |
503 | | void cmRule::CheckProperty(std::string const& prop, cmMakefile& context) const |
504 | 0 | { |
505 | | // Certain properties need checking. |
506 | 0 | if (prop == GLOBAL) { |
507 | 0 | auto const& rules = context.GetOwnedRules(); |
508 | 0 | auto it = std::find_if(rules.begin(), rules.end(), |
509 | 0 | [&](std::unique_ptr<cmRule> const& rule) -> bool { |
510 | 0 | return this == rule.get(); |
511 | 0 | }); |
512 | 0 | if (it == rules.end()) { |
513 | 0 | context.IssueMessage( |
514 | 0 | MessageType::FATAL_ERROR, |
515 | 0 | cmStrCat("Attempt to promote rule \"", this->GetName(), |
516 | 0 | "\" to global scope (by setting GLOBAL) " |
517 | 0 | "which is not created in this directory.")); |
518 | 0 | } |
519 | 0 | } |
520 | 0 | } |
521 | | |
522 | | namespace { |
523 | | cmsys::RegularExpression PatternRegex{ "(^[A-Z][A-Z0-9_]+)=(.*)$" }; |
524 | | |
525 | | void UpdatePatterns(cmRule::PatternSet& patterns, cmList const& newPatterns) |
526 | 0 | { |
527 | 0 | for (auto const& pattern : newPatterns) { |
528 | 0 | if (PatternRegex.find(pattern)) { |
529 | 0 | auto it = std::find_if(patterns.begin(), patterns.end(), |
530 | 0 | [](cmRule::Pattern const& item) { |
531 | 0 | return item.Name == PatternRegex.match(1); |
532 | 0 | }); |
533 | 0 | if (it == patterns.end()) { |
534 | 0 | patterns.emplace_back(PatternRegex.match(1), PatternRegex.match(2)); |
535 | 0 | } else { |
536 | 0 | it->Value = PatternRegex.match(2); |
537 | 0 | } |
538 | 0 | } |
539 | 0 | } |
540 | 0 | } |
541 | | |
542 | | bool ConfigureRule(cmRule::ConfiguratorSet const& configurators, |
543 | | cmMakefile* mf, cmRule::PatternSet& patterns, |
544 | | std::string const& rule, std::string const& target, |
545 | | std::string const& fileSet, |
546 | | std::string const& outputFileSet, |
547 | | std::string const& source = {}) |
548 | 0 | { |
549 | 0 | cm::string_view patternsVariable{ "_CMAKE_RULE_PATTERNS"_s }; |
550 | |
|
551 | 0 | for (auto const& item : configurators) { |
552 | | // The validator command will be executed in an isolated scope. |
553 | 0 | cmMakefile::ScopePushPop varScope(mf); |
554 | 0 | cmMakefile::PolicyPushPop polScope(mf); |
555 | 0 | static_cast<void>(varScope); |
556 | 0 | static_cast<void>(polScope); |
557 | |
|
558 | 0 | std::vector<cmListFileArgument> args{ |
559 | 0 | cmListFileArgument{ rule, cmListFileArgument::Unquoted, 0 }, |
560 | 0 | cmListFileArgument{ target, cmListFileArgument::Unquoted, 0 }, |
561 | 0 | cmListFileArgument{ fileSet, cmListFileArgument::Unquoted, 0 }, |
562 | 0 | cmListFileArgument{ outputFileSet, cmListFileArgument::Unquoted, 0 } |
563 | 0 | }; |
564 | 0 | if (!source.empty()) { |
565 | 0 | args.emplace_back(source, cmListFileArgument::Quoted, 0); |
566 | 0 | } |
567 | 0 | args.emplace_back(patternsVariable, cmListFileArgument::Unquoted, 0); |
568 | |
|
569 | 0 | cmListFileFunction command(*item.second, 0, 0, args); |
570 | 0 | cmExecutionStatus status(*mf); |
571 | 0 | if (!mf->ExecuteCommand(command, status)) { |
572 | 0 | mf->IssueMessage(MessageType::FATAL_ERROR, |
573 | 0 | cmStrCat("Erroneous execution of the CONFIGURATOR \"", |
574 | 0 | *item.second, "\", from the RULE \"", |
575 | 0 | *item.first, "\", for the FILE_SET \"", |
576 | 0 | fileSet, "\" of TARGET \"", target, "\".")); |
577 | 0 | return false; |
578 | 0 | } |
579 | 0 | UpdatePatterns( |
580 | 0 | patterns, cmList{ mf->GetDefinition(std::string{ patternsVariable }) }); |
581 | 0 | } |
582 | | |
583 | 0 | return true; |
584 | 0 | } |
585 | | } |
586 | | |
587 | | bool cmRule::Instantiate(cmTarget const* target, cmFileSet const* fileSet, |
588 | | cmFileSet const* outputFileSet, |
589 | | PatternSet& patterns) const |
590 | 0 | { |
591 | 0 | this->SwitchMode(); |
592 | | |
593 | | // First, take patterns from RULE_PATTERNS file set property, if any |
594 | 0 | if (cmValue fsPatterns = fileSet->GetProperty("RULE_PATTERNS")) { |
595 | 0 | UpdatePatterns(patterns, cmList{ fsPatterns, cmList::EmptyElements::No }); |
596 | 0 | } |
597 | |
|
598 | 0 | if (this->HasConfigurators(ConfiguratorType::FileSet)) { |
599 | | // Finalize file set level configuration by calling user's commands |
600 | 0 | return ConfigureRule(this->GetConfigurators(ConfiguratorType::FileSet), |
601 | 0 | fileSet->GetMakefile(), patterns, this->GetName(), |
602 | 0 | target->GetName(), fileSet->GetName(), |
603 | 0 | outputFileSet->GetName()); |
604 | 0 | } |
605 | | |
606 | 0 | return true; |
607 | 0 | } |
608 | | |
609 | | bool cmRule::Instantiate(cmTarget const* target, cmFileSet const* fileSet, |
610 | | cmFileSet const* outputFileSet, cmSourceFile* source, |
611 | | PatternSet& patterns) const |
612 | 0 | { |
613 | | // First, take patterns from <RULE>_PATTERNS source file property, if any |
614 | 0 | if (cmValue sfPatterns = |
615 | 0 | source->GetProperty(cmStrCat(this->GetName(), "_PATTERNS"))) { |
616 | 0 | UpdatePatterns(patterns, cmList{ sfPatterns, cmList::EmptyElements::No }); |
617 | 0 | } |
618 | |
|
619 | 0 | if (this->HasConfigurators(ConfiguratorType::Source)) { |
620 | | // Before custom commands generation, finalize source level |
621 | | // configuration by calling user's commands |
622 | 0 | return ConfigureRule(this->GetConfigurators(ConfiguratorType::Source), |
623 | 0 | fileSet->GetMakefile(), patterns, this->GetName(), |
624 | 0 | target->GetName(), fileSet->GetName(), |
625 | 0 | outputFileSet->GetName(), source->GetFullPath()); |
626 | 0 | } |
627 | | |
628 | 0 | return true; |
629 | 0 | } |