/src/CMake/Source/cmGeneratorRule.cxx
Line | Count | Source |
1 | | |
2 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
3 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
4 | | #include "cmGeneratorRule.h" |
5 | | |
6 | | #include <algorithm> |
7 | | #include <iterator> |
8 | | #include <utility> |
9 | | #include <vector> |
10 | | |
11 | | #include <cm/memory> |
12 | | #include <cm/optional> |
13 | | #include <cm/string_view> |
14 | | #include <cmext/algorithm> |
15 | | #include <cmext/string_view> |
16 | | |
17 | | #include "cmCMakePath.h" |
18 | | #include "cmCustomCommand.h" |
19 | | #include "cmCustomCommandLines.h" |
20 | | #include "cmDiagnostics.h" |
21 | | #include "cmFileSet.h" |
22 | | #include "cmGeneratorExpression.h" |
23 | | #include "cmList.h" |
24 | | #include "cmListFileCache.h" |
25 | | #include "cmMakefile.h" |
26 | | #include "cmSourceFile.h" |
27 | | #include "cmStringAlgorithms.h" |
28 | | #include "cmSystemTools.h" |
29 | | #include "cmTarget.h" |
30 | | |
31 | | namespace { |
32 | | std::vector<cm::string_view> ReservedPatterns{ "RULE"_s, |
33 | | "TARGET"_s, |
34 | | "FILE_SET"_s, |
35 | | "SOURCE_DIR"_s, |
36 | | "BINARY_DIR"_s, |
37 | | "CURRENT_SOURCE_DIR"_s, |
38 | | "CURRENT_BINARY_DIR"_s, |
39 | | "OUTPUT_CURRENT_SOURCE_DIR"_s, |
40 | | "OUTPUT_CURRENT_BINARY_DIR"_s, |
41 | | "SOURCE"_s, |
42 | | "INPUT_DIR"_s, |
43 | | "FILE_NAME"_s, |
44 | | "BASE_NAME"_s, |
45 | | "INCLUDE_DIRECTORIES"_s, |
46 | | "COMPILE_OPTIONS"_s, |
47 | | "COMPILE_DEFINITIONS"_s }; |
48 | | } |
49 | | |
50 | | std::string cmGeneratorRule::RulePlaceholderExpander::ExpandVariable( |
51 | | std::string const& variable) |
52 | 0 | { |
53 | 0 | if (cm::contains(this->Values, variable)) { |
54 | 0 | return this->Values[variable]; |
55 | 0 | } |
56 | | // If there is no variable defined, mark unresolved variable by '{' and '}' |
57 | 0 | return cmStrCat('{', variable, '}'); |
58 | 0 | } |
59 | | |
60 | | cmGeneratorRule::RulePlaceholderExpander::VariableMap const |
61 | | cmGeneratorRule::DefaultProperties{ |
62 | | { "INCLUDE_DIRECTORIES", |
63 | | "$<LIST:FILTER,$<FILE_SET_PROPERTY:<FILE_SET>,TARGET:<TARGET>,INCLUDE_" |
64 | | "DIRECTORIES>;$<SOURCE_PROPERTY:<SOURCE>,TARGET_DIRECTORY:<TARGET>," |
65 | | "INCLUDE_DIRECTORIES>;$<RULE_PROPERTY:<RULE>,INCLUDE_DIRECTORIES>," |
66 | | "EXCLUDE,^$>" }, |
67 | | { "COMPILE_OPTIONS", |
68 | | "$<LIST:FILTER,$<RULE_PROPERTY:<RULE>,COMPILE_OPTIONS>;$<SOURCE_" |
69 | | "PROPERTY:<SOURCE>," |
70 | | "TARGET_DIRECTORY:<TARGET>,COMPILE_OPTIONS>;$<FILE_SET_PROPERTY:<FILE_" |
71 | | "SET>,TARGET:<TARGET>,COMPILE_OPTIONS>,EXCLUDE,^$>" }, |
72 | | { "COMPILE_DEFINITIONS", |
73 | | "$<LIST:FILTER,$<RULE_PROPERTY:<RULE>,COMPILE_DEFINITIONS>;$<SOURCE_" |
74 | | "PROPERTY:<" |
75 | | "SOURCE>," |
76 | | "TARGET_DIRECTORY:<TARGET>,COMPILE_DEFINITIONS>;$<FILE_SET_PROPERTY:<" |
77 | | "FILE_SET>,TARGET:<TARGET>,COMPILE_DEFINITIONS>,EXCLUDE,^$>" } |
78 | | }; |
79 | | |
80 | | cmGeneratorRule::cmGeneratorRule(cmRule const* rule, cmTarget const* target, |
81 | | cmFileSet const* fileSet, |
82 | | cmFileSet const* outputFileSet, |
83 | | cmSourceFile const* source, |
84 | | cmRule::PatternSet const& patterns) |
85 | 0 | : Rule(rule) |
86 | 0 | , Target(target) |
87 | 0 | , FileSet(fileSet) |
88 | 0 | , OutputFileSet(outputFileSet) |
89 | 0 | , Source(source) |
90 | 0 | , Makefile(*fileSet->GetMakefile()) |
91 | 0 | { |
92 | 0 | this->Name = cmStrCat(rule->GetName(), '_', |
93 | 0 | std::hash<std::string>{}(cmStrCat( |
94 | 0 | rule->GetName(), '-', target->GetName(), '-', |
95 | 0 | fileSet->GetName(), '-', source->GetFullPath()))); |
96 | |
|
97 | 0 | auto& values = this->RuleExpander.Values; |
98 | |
|
99 | 0 | cmCMakePath file = cmCMakePath{ this->Source->GetFullPath() }.Normal(); |
100 | 0 | cmCMakePath sourceDir = file.IsAbsolute() |
101 | 0 | ? file.GetParentPath() |
102 | 0 | : this->GetMakefile().GetCurrentSourceDirectory(); |
103 | | |
104 | | /* clang-format off */ |
105 | 0 | values.insert({ "RULE", this->GetName() }); |
106 | 0 | values.insert({ "TARGET", this->Target->GetName() }); |
107 | 0 | values.insert({ "FILE_SET", this->FileSet->GetName() }); |
108 | 0 | values.insert({ "SOURCE_DIR", this->GetMakefile().GetHomeDirectory() }); |
109 | 0 | values.insert({ "BINARY_DIR", this->GetMakefile().GetHomeOutputDirectory() }); |
110 | 0 | values.insert({ "CURRENT_SOURCE_DIR", this->GetMakefile().GetCurrentSourceDirectory() }); |
111 | 0 | values.insert({ "CURRENT_BINARY_DIR", this->GetMakefile().GetCurrentBinaryDirectory() }); |
112 | 0 | values.insert({ "OUTPUT_CURRENT_SOURCE_DIR", outputFileSet->GetMakefile()->GetCurrentSourceDirectory() }); |
113 | 0 | values.insert({ "OUTPUT_CURRENT_BINARY_DIR", outputFileSet->GetMakefile()->GetCurrentBinaryDirectory() }); |
114 | 0 | values.insert({ "SOURCE", file.GenericString() }); |
115 | 0 | values.insert({ "INPUT_DIR", file.GetParentPath().GenericString() }); |
116 | 0 | values.insert({ "FILE_NAME", file.GetFileName().GenericString() }); |
117 | 0 | values.insert({ "BASE_NAME", file.GetFileName().RemoveWideExtension().GenericString() }); |
118 | | /* clang-format on */ |
119 | | |
120 | | // instantiate default properties pattern |
121 | 0 | for (auto const& item : DefaultProperties) { |
122 | 0 | values.insert( |
123 | 0 | { item.first, this->RuleExpander.ExpandVariables(item.second) }); |
124 | 0 | } |
125 | |
|
126 | 0 | this->UpdateRuleExpander(patterns); |
127 | 0 | } |
128 | | |
129 | | void cmGeneratorRule::UpdateRuleExpander(cmRule::PatternSet const& patterns) |
130 | 0 | { |
131 | 0 | if (patterns.empty()) { |
132 | 0 | return; |
133 | 0 | } |
134 | | |
135 | 0 | auto& values = this->RuleExpander.Values; |
136 | |
|
137 | 0 | for (cmRule::Pattern const& pattern : patterns) { |
138 | 0 | if (cm::contains(ReservedPatterns, pattern.Name)) { |
139 | | // overwriting a reserved pattern is not allowed |
140 | 0 | continue; |
141 | 0 | } |
142 | | |
143 | 0 | std::string data{ pattern.Value }; |
144 | 0 | values[pattern.Name] = this->RuleExpander.ExpandVariables(data); |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | | cmValue cmGeneratorRule::GetProperty(std::string const& property) const |
149 | 0 | { |
150 | 0 | cmValue value = this->Properties.GetPropertyValue(property); |
151 | 0 | if (value) { |
152 | 0 | return value; |
153 | 0 | } |
154 | | |
155 | 0 | if (property == "OUTPUT_FILE_SET"_s) { |
156 | 0 | this->Properties.SetProperty( |
157 | 0 | property, |
158 | 0 | cmList{ this->OutputFileSet->GetName(), this->OutputFileSet->GetType() } |
159 | 0 | .to_string()); |
160 | 0 | return this->Properties.GetPropertyValue(property); |
161 | 0 | } |
162 | | |
163 | 0 | if (property == "WORKING_DIRECTORY") { |
164 | | // property was not set by the user, so use the default |
165 | 0 | this->Properties.SetProperty( |
166 | 0 | property, this->GetMakefile().GetCurrentBinaryDirectory()); |
167 | 0 | return this->Properties.GetPropertyValue(property); |
168 | 0 | } |
169 | | |
170 | | // property not yet instantiated, retrieve it from cmRule |
171 | 0 | value = this->Rule->GetProperty(property); |
172 | 0 | if (value) { |
173 | 0 | std::string expandedValue{ *value }; |
174 | 0 | this->Properties.SetProperty( |
175 | 0 | property, this->RuleExpander.ExpandVariables(expandedValue)); |
176 | 0 | return this->Properties.GetPropertyValue(property); |
177 | 0 | } |
178 | | |
179 | 0 | return value; |
180 | 0 | } |
181 | | |
182 | | std::unique_ptr<cmCustomCommand> cmGeneratorRule::CreateCustomCommand() const |
183 | 0 | { |
184 | 0 | auto expandVariables = [this](std::string const& item) -> std::string { |
185 | 0 | return this->RuleExpander.ExpandVariables(item); |
186 | 0 | }; |
187 | |
|
188 | 0 | auto expandVector = |
189 | 0 | [&expandVariables]( |
190 | 0 | std::vector<std::string> const& data) -> std::vector<std::string> { |
191 | 0 | std::vector<std::string> result; |
192 | 0 | result.reserve(data.size()); |
193 | 0 | std::transform(data.begin(), data.end(), std::back_inserter(result), |
194 | 0 | expandVariables); |
195 | 0 | return result; |
196 | 0 | }; |
197 | |
|
198 | 0 | auto expandPath = |
199 | 0 | [&expandVariables](std::string const& binaryDirectory, |
200 | 0 | std::string const& data) -> std::string { |
201 | 0 | std::string path = expandVariables(data); |
202 | 0 | if (cmGeneratorExpression::StartsWithGeneratorExpression(path)) { |
203 | 0 | cmSystemTools::ConvertToUnixSlashes(path); |
204 | 0 | } else { |
205 | 0 | if (cmSystemTools::FileIsFullPath(path)) { |
206 | 0 | path = cmSystemTools::CollapseFullPath(path); |
207 | 0 | } else { |
208 | 0 | path = cmSystemTools::CollapseFullPath(path, binaryDirectory); |
209 | 0 | } |
210 | 0 | } |
211 | 0 | return path; |
212 | 0 | }; |
213 | |
|
214 | 0 | auto expandPaths = |
215 | 0 | [&expandPath]( |
216 | 0 | std::string const& binaryDirectory, |
217 | 0 | std::vector<std::string> const& data) -> std::vector<std::string> { |
218 | 0 | std::vector<std::string> result; |
219 | 0 | result.reserve(data.size()); |
220 | 0 | std::transform( |
221 | 0 | data.begin(), data.end(), std::back_inserter(result), |
222 | 0 | [&expandPath, &binaryDirectory](std::string const& item) -> std::string { |
223 | 0 | return expandPath(binaryDirectory, item); |
224 | 0 | }); |
225 | 0 | return result; |
226 | 0 | }; |
227 | |
|
228 | 0 | auto cc = cm::make_unique<cmCustomCommand>(); |
229 | |
|
230 | 0 | cmCustomCommandLines commandLines; |
231 | 0 | for (cmRule::CustomCommand const& line : this->Rule->GetCommands()) { |
232 | 0 | cmCustomCommandLine command; |
233 | 0 | command.reserve(line.size()); |
234 | 0 | std::transform(line.begin(), line.end(), std::back_inserter(command), |
235 | 0 | expandVariables); |
236 | 0 | commandLines.push_back(command); |
237 | 0 | } |
238 | 0 | cc->SetCommandLines(std::move(commandLines)); |
239 | |
|
240 | 0 | cc->SetOutputs(expandPaths(this->GetMakefile().GetCurrentBinaryDirectory(), |
241 | 0 | this->Rule->GetOutputs())); |
242 | |
|
243 | 0 | if (!this->Rule->GetByproducts().empty()) { |
244 | 0 | cc->SetByproducts(expandVector(this->Rule->GetByproducts())); |
245 | 0 | } |
246 | |
|
247 | 0 | if (!this->Rule->GetDepfile().empty()) { |
248 | | // store normalized path as property |
249 | 0 | this->Properties.SetProperty( |
250 | 0 | "DEPFILE", |
251 | 0 | expandPath(this->GetMakefile().GetCurrentBinaryDirectory(), |
252 | 0 | this->Rule->GetDepfile())); |
253 | 0 | cc->SetDepfile(expandVariables(this->Rule->GetDepfile())); |
254 | 0 | } |
255 | 0 | std::vector<std::string> depends{ 1, this->Source->GetFullPath() }; |
256 | 0 | if (!this->Rule->GetDepends().empty()) { |
257 | 0 | depends.insert(depends.end(), this->Rule->GetDepends().begin(), |
258 | 0 | this->Rule->GetDepends().end()); |
259 | 0 | } |
260 | 0 | cc->SetDepends(expandVector(depends)); |
261 | 0 | if (cmValue deps_explicit = |
262 | 0 | this->Rule->GetProperty("DEPENDS_EXPLICIT_ONLY")) { |
263 | 0 | cc->SetDependsExplicitOnly(deps_explicit.IsOn()); |
264 | 0 | } else { |
265 | 0 | cc->SetDependsExplicitOnly(this->GetMakefile().IsOn( |
266 | 0 | "CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY")); |
267 | 0 | } |
268 | |
|
269 | 0 | if (cmValue wd = this->Rule->GetProperty("WORKING_DIRECTORY")) { |
270 | | // store normalized path as property |
271 | 0 | this->Properties.SetProperty( |
272 | 0 | "WORKING_DIRECTORY", |
273 | 0 | expandPath(this->GetMakefile().GetCurrentBinaryDirectory(), *wd)); |
274 | 0 | cc->SetWorkingDirectory(expandVariables(*wd)); |
275 | 0 | } |
276 | |
|
277 | 0 | if (this->Rule->GetProperty("USES_TERMINAL").IsOn() && |
278 | 0 | !this->Rule->GetProperty("JOB_POOL_COMPILE")->empty()) { |
279 | 0 | this->GetMakefile().IssueDiagnostic( |
280 | 0 | cmDiagnostics::CMD_AUTHOR, |
281 | 0 | cmStrCat("RULE \"", this->GetName(), "\": JOB_POOL \"", |
282 | 0 | expandVariables(this->Rule->GetProperty("JOB_POOL_COMPILE")), |
283 | 0 | "\" is shadowed by USES_TERMINAL.")); |
284 | 0 | } |
285 | 0 | if (!this->Rule->GetProperty("JOB_POOL_COMPILE")->empty()) { |
286 | 0 | cc->SetJobPool( |
287 | 0 | expandVariables(*this->Rule->GetProperty("JOB_POOL_COMPILE"))); |
288 | 0 | } else if (this->Rule->GetProperty("USES_TERMINAL").IsOn()) { |
289 | 0 | cc->SetUsesTerminal(true); |
290 | 0 | } |
291 | |
|
292 | 0 | cc->SetJobserverAware(this->Rule->GetProperty("JOB_SERVER_AWARE").IsOn()); |
293 | |
|
294 | 0 | cc->SetEscapeOldStyle(!this->Rule->GetProperty("VERBATIM").IsOn()); |
295 | 0 | cc->SetCommandExpandLists( |
296 | 0 | this->Rule->GetProperty("COMMAND_EXPAND_LISTS").IsOn()); |
297 | |
|
298 | 0 | if (cmValue comment = this->Rule->GetProperty("COMMENT")) { |
299 | 0 | cc->SetComment(expandVariables(*comment)); |
300 | 0 | } |
301 | |
|
302 | 0 | return cc; |
303 | 0 | } |
304 | | |
305 | | std::unique_ptr<cmCustomCommand> cmGeneratorRule::Generate( |
306 | | cmFileSet* outFileSet) const |
307 | 0 | { |
308 | 0 | auto cc = this->CreateCustomCommand(); |
309 | | |
310 | | // populate file set with outputs from custom command |
311 | 0 | for (auto const& output : cc->GetOutputs()) { |
312 | 0 | outFileSet->AddFileEntry(BT<std::string>{ output }); |
313 | 0 | } |
314 | |
|
315 | 0 | return cc; |
316 | 0 | } |