/src/CMake/Source/cmLocalNinjaGenerator.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 "cmLocalNinjaGenerator.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <cassert> |
7 | | #include <cstdio> |
8 | | #include <memory> |
9 | | #include <sstream> |
10 | | #include <utility> |
11 | | |
12 | | #include <cm/unordered_set> |
13 | | #include <cmext/string_view> |
14 | | |
15 | | #include "cmsys/FStream.hxx" |
16 | | |
17 | | #include "cm_codecvt_Encoding.hxx" |
18 | | |
19 | | #include "cmCryptoHash.h" |
20 | | #include "cmCustomCommand.h" |
21 | | #include "cmCustomCommandGenerator.h" |
22 | | #include "cmDiagnostics.h" |
23 | | #include "cmGeneratedFileStream.h" |
24 | | #include "cmGeneratorExpression.h" |
25 | | #include "cmGeneratorTarget.h" |
26 | | #include "cmGlobalGenerator.h" |
27 | | #include "cmGlobalNinjaGenerator.h" |
28 | | #include "cmInstrumentation.h" |
29 | | #include "cmInstrumentationQuery.h" |
30 | | #include "cmList.h" |
31 | | #include "cmListFileCache.h" |
32 | | #include "cmLocalGenerator.h" |
33 | | #include "cmMakefile.h" |
34 | | #include "cmNinjaTargetGenerator.h" |
35 | | #include "cmNinjaTypes.h" |
36 | | #include "cmPolicies.h" |
37 | | #include "cmRulePlaceholderExpander.h" |
38 | | #include "cmSourceFile.h" |
39 | | #include "cmState.h" |
40 | | #include "cmStringAlgorithms.h" |
41 | | #include "cmSystemTools.h" |
42 | | #include "cmTarget.h" |
43 | | #include "cmTargetTypes.h" |
44 | | #include "cmValue.h" |
45 | | #include "cmake.h" |
46 | | |
47 | | cmLocalNinjaGenerator::cmLocalNinjaGenerator(cmGlobalGenerator* gg, |
48 | | cmMakefile* mf) |
49 | 0 | : cmLocalCommonGenerator(gg, mf) |
50 | 0 | { |
51 | 0 | } |
52 | | |
53 | | // Virtual public methods. |
54 | | |
55 | | std::unique_ptr<cmRulePlaceholderExpander> |
56 | | cmLocalNinjaGenerator::CreateRulePlaceholderExpander( |
57 | | cmBuildStep buildStep) const |
58 | 0 | { |
59 | 0 | auto ret = this->cmLocalGenerator::CreateRulePlaceholderExpander(buildStep); |
60 | 0 | ret->SetTargetImpLib("$TARGET_IMPLIB"); |
61 | 0 | return std::unique_ptr<cmRulePlaceholderExpander>(std::move(ret)); |
62 | 0 | } |
63 | | |
64 | 0 | cmLocalNinjaGenerator::~cmLocalNinjaGenerator() = default; |
65 | | |
66 | | void cmLocalNinjaGenerator::Generate() |
67 | 0 | { |
68 | | // Compute the path to use when referencing the current output |
69 | | // directory from the top output directory. |
70 | 0 | this->HomeRelativeOutputPath = |
71 | 0 | this->MaybeRelativeToTopBinDir(this->GetObjectOutputRoot()); |
72 | 0 | if (this->HomeRelativeOutputPath == ".") { |
73 | 0 | this->HomeRelativeOutputPath.clear(); |
74 | 0 | } |
75 | |
|
76 | 0 | if (this->GetGlobalGenerator()->IsMultiConfig()) { |
77 | 0 | for (auto const& config : this->GetConfigNames()) { |
78 | 0 | this->WriteProcessedMakefile(this->GetImplFileStream(config)); |
79 | 0 | } |
80 | 0 | } |
81 | 0 | this->WriteProcessedMakefile(this->GetCommonFileStream()); |
82 | | #ifdef NINJA_GEN_VERBOSE_FILES |
83 | | this->WriteProcessedMakefile(this->GetRulesFileStream()); |
84 | | #endif |
85 | | |
86 | | // We do that only once for the top CMakeLists.txt file. |
87 | 0 | if (this->IsRootMakefile()) { |
88 | 0 | this->WriteBuildFileTop(); |
89 | |
|
90 | 0 | this->WritePools(this->GetRulesFileStream()); |
91 | |
|
92 | 0 | std::string const& showIncludesPrefix = |
93 | 0 | this->GetMakefile()->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"); |
94 | 0 | if (!showIncludesPrefix.empty()) { |
95 | 0 | cmGlobalNinjaGenerator::WriteComment(this->GetRulesFileStream(), |
96 | 0 | "localized /showIncludes string"); |
97 | 0 | this->GetRulesFileStream() << "msvc_deps_prefix = "; |
98 | | // 'cl /showIncludes' encodes output in the console output code page. |
99 | | // It may differ from the encoding used for file paths in 'build.ninja'. |
100 | | // Ninja matches the showIncludes prefix using its raw byte sequence. |
101 | 0 | this->GetRulesFileStream().WriteAltEncoding( |
102 | 0 | showIncludesPrefix, cmGeneratedFileStream::Encoding::ConsoleOutput); |
103 | 0 | this->GetRulesFileStream() << "\n\n"; |
104 | 0 | } |
105 | 0 | } |
106 | |
|
107 | 0 | for (auto const& target : this->GetGeneratorTargets()) { |
108 | 0 | if (!target->IsInBuildSystem()) { |
109 | 0 | continue; |
110 | 0 | } |
111 | 0 | auto tg = cmNinjaTargetGenerator::New(target.get()); |
112 | 0 | if (tg) { |
113 | 0 | if (target->Target->IsPerConfig()) { |
114 | 0 | for (auto const& config : this->GetConfigNames()) { |
115 | 0 | tg->Generate(config); |
116 | 0 | if (target->GetType() == cm::TargetType::GLOBAL_TARGET && |
117 | 0 | this->GetGlobalGenerator()->IsMultiConfig()) { |
118 | 0 | cmNinjaBuild phonyAlias("phony"); |
119 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetOutputs( |
120 | 0 | target.get(), phonyAlias.Outputs, "", DependOnTargetArtifact); |
121 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetOutputs( |
122 | 0 | target.get(), phonyAlias.ExplicitDeps, config, |
123 | 0 | DependOnTargetArtifact); |
124 | 0 | this->GetGlobalNinjaGenerator()->WriteBuild( |
125 | 0 | *this->GetGlobalNinjaGenerator()->GetConfigFileStream(config), |
126 | 0 | phonyAlias); |
127 | 0 | } |
128 | 0 | } |
129 | 0 | if (target->GetType() == cm::TargetType::GLOBAL_TARGET && |
130 | 0 | this->GetGlobalGenerator()->IsMultiConfig()) { |
131 | 0 | if (!this->GetGlobalNinjaGenerator()->GetDefaultConfigs().empty()) { |
132 | 0 | cmNinjaBuild phonyAlias("phony"); |
133 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetOutputs( |
134 | 0 | target.get(), phonyAlias.Outputs, "", DependOnTargetArtifact); |
135 | 0 | for (auto const& config : |
136 | 0 | this->GetGlobalNinjaGenerator()->GetDefaultConfigs()) { |
137 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetOutputs( |
138 | 0 | target.get(), phonyAlias.ExplicitDeps, config, |
139 | 0 | DependOnTargetArtifact); |
140 | 0 | } |
141 | 0 | this->GetGlobalNinjaGenerator()->WriteBuild( |
142 | 0 | *this->GetGlobalNinjaGenerator()->GetDefaultFileStream(), |
143 | 0 | phonyAlias); |
144 | 0 | } |
145 | 0 | cmNinjaBuild phonyAlias("phony"); |
146 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetOutputs( |
147 | 0 | target.get(), phonyAlias.Outputs, "all", DependOnTargetArtifact); |
148 | 0 | for (auto const& config : this->GetConfigNames()) { |
149 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetOutputs( |
150 | 0 | target.get(), phonyAlias.ExplicitDeps, config, |
151 | 0 | DependOnTargetArtifact); |
152 | 0 | } |
153 | 0 | this->GetGlobalNinjaGenerator()->WriteBuild( |
154 | 0 | *this->GetGlobalNinjaGenerator()->GetDefaultFileStream(), |
155 | 0 | phonyAlias); |
156 | 0 | } |
157 | 0 | } else { |
158 | 0 | tg->Generate(""); |
159 | 0 | } |
160 | 0 | } |
161 | 0 | } |
162 | |
|
163 | 0 | for (auto const& config : this->GetConfigNames()) { |
164 | 0 | this->WriteCustomCommandBuildStatements(config); |
165 | 0 | this->AdditionalCleanFiles(config); |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | | std::string cmLocalNinjaGenerator::GetObjectOutputRoot( |
170 | | cmStateEnums::IntermediateDirKind kind) const |
171 | 0 | { |
172 | 0 | if (this->UseShortObjectNames(kind)) { |
173 | 0 | return cmStrCat(this->GetBinaryDirectory(), '/', |
174 | 0 | this->GetGlobalGenerator()->GetShortBinaryOutputDir()); |
175 | 0 | } |
176 | 0 | return cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles"); |
177 | 0 | } |
178 | | |
179 | | // Non-virtual public methods. |
180 | | |
181 | | cmGlobalNinjaGenerator const* cmLocalNinjaGenerator::GetGlobalNinjaGenerator() |
182 | | const |
183 | 0 | { |
184 | 0 | return static_cast<cmGlobalNinjaGenerator const*>( |
185 | 0 | this->GetGlobalGenerator()); |
186 | 0 | } |
187 | | |
188 | | cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator() |
189 | 0 | { |
190 | 0 | return static_cast<cmGlobalNinjaGenerator*>(this->GetGlobalGenerator()); |
191 | 0 | } |
192 | | |
193 | | std::string const& cmLocalNinjaGenerator::GetWorkingDirectory() const |
194 | 0 | { |
195 | 0 | return this->GetState()->GetBinaryDirectory(); |
196 | 0 | } |
197 | | |
198 | | std::string cmLocalNinjaGenerator::MaybeRelativeToWorkDir( |
199 | | std::string const& path) const |
200 | 0 | { |
201 | 0 | return this->GetGlobalNinjaGenerator()->NinjaOutputPath( |
202 | 0 | this->MaybeRelativeToTopBinDir(path)); |
203 | 0 | } |
204 | | |
205 | | std::string cmLocalNinjaGenerator::GetLinkDependencyFile( |
206 | | cmGeneratorTarget* target, std::string const& config) const |
207 | 0 | { |
208 | 0 | return cmStrCat(target->GetSupportDirectory(), |
209 | 0 | this->GetGlobalNinjaGenerator()->ConfigDirectory(config), |
210 | 0 | "/link.d"); |
211 | 0 | } |
212 | | |
213 | | // Virtual protected methods. |
214 | | |
215 | | std::string cmLocalNinjaGenerator::ConvertToIncludeReference( |
216 | | std::string const& path, cmOutputConverter::OutputFormat format) |
217 | 0 | { |
218 | 0 | return this->ConvertToOutputFormat(path, format); |
219 | 0 | } |
220 | | |
221 | | // Private methods. |
222 | | |
223 | | cmGeneratedFileStream& cmLocalNinjaGenerator::GetImplFileStream( |
224 | | std::string const& config) const |
225 | 0 | { |
226 | 0 | return *this->GetGlobalNinjaGenerator()->GetImplFileStream(config); |
227 | 0 | } |
228 | | |
229 | | cmGeneratedFileStream& cmLocalNinjaGenerator::GetCommonFileStream() const |
230 | 0 | { |
231 | 0 | return *this->GetGlobalNinjaGenerator()->GetCommonFileStream(); |
232 | 0 | } |
233 | | |
234 | | cmGeneratedFileStream& cmLocalNinjaGenerator::GetRulesFileStream() const |
235 | 0 | { |
236 | 0 | return *this->GetGlobalNinjaGenerator()->GetRulesFileStream(); |
237 | 0 | } |
238 | | |
239 | | cmake const* cmLocalNinjaGenerator::GetCMakeInstance() const |
240 | 0 | { |
241 | 0 | return this->GetGlobalGenerator()->GetCMakeInstance(); |
242 | 0 | } |
243 | | |
244 | | cmake* cmLocalNinjaGenerator::GetCMakeInstance() |
245 | 0 | { |
246 | 0 | return this->GetGlobalGenerator()->GetCMakeInstance(); |
247 | 0 | } |
248 | | |
249 | | void cmLocalNinjaGenerator::WriteBuildFileTop() |
250 | 0 | { |
251 | 0 | this->WriteProjectHeader(this->GetCommonFileStream()); |
252 | |
|
253 | 0 | if (this->GetGlobalGenerator()->IsMultiConfig()) { |
254 | 0 | for (auto const& config : this->GetConfigNames()) { |
255 | 0 | auto& stream = this->GetImplFileStream(config); |
256 | 0 | this->WriteProjectHeader(stream); |
257 | 0 | this->WriteNinjaRequiredVersion(stream); |
258 | 0 | this->WriteNinjaConfigurationVariable(stream, config); |
259 | 0 | this->WriteNinjaFilesInclusionConfig(stream); |
260 | 0 | } |
261 | 0 | } else { |
262 | 0 | this->WriteNinjaRequiredVersion(this->GetCommonFileStream()); |
263 | 0 | this->WriteNinjaConfigurationVariable(this->GetCommonFileStream(), |
264 | 0 | this->GetConfigNames().front()); |
265 | 0 | } |
266 | 0 | this->WriteNinjaFilesInclusionCommon(this->GetCommonFileStream()); |
267 | 0 | this->WriteNinjaWorkDir(this->GetCommonFileStream()); |
268 | | |
269 | | // For the rule file. |
270 | 0 | this->WriteProjectHeader(this->GetRulesFileStream()); |
271 | 0 | } |
272 | | |
273 | | void cmLocalNinjaGenerator::WriteProjectHeader(std::ostream& os) |
274 | 0 | { |
275 | 0 | cmGlobalNinjaGenerator::WriteDivider(os); |
276 | 0 | os << "# Project: " << this->GetProjectName() |
277 | 0 | << "\n" |
278 | 0 | "# Configurations: " |
279 | 0 | << cmJoin(this->GetConfigNames(), ", ") << '\n'; |
280 | 0 | cmGlobalNinjaGenerator::WriteDivider(os); |
281 | 0 | } |
282 | | |
283 | | void cmLocalNinjaGenerator::WriteNinjaRequiredVersion(std::ostream& os) |
284 | 0 | { |
285 | | // Default required version |
286 | 0 | std::string requiredVersion = cmGlobalNinjaGenerator::RequiredNinjaVersion(); |
287 | | |
288 | | // Ninja generator uses the 'console' pool if available (>= 1.5) |
289 | 0 | if (this->GetGlobalNinjaGenerator()->SupportsDirectConsole()) { |
290 | 0 | requiredVersion = |
291 | 0 | cmGlobalNinjaGenerator::RequiredNinjaVersionForConsolePool(); |
292 | 0 | } |
293 | | |
294 | | // The Ninja generator writes rules which require support for restat |
295 | | // when rebuilding build.ninja manifest (>= 1.8) |
296 | 0 | if (this->GetGlobalNinjaGenerator()->SupportsManifestRestat() && |
297 | 0 | this->GetCMakeInstance()->DoWriteGlobVerifyTarget() && |
298 | 0 | !this->GetGlobalNinjaGenerator()->GlobalSettingIsOn( |
299 | 0 | "CMAKE_SUPPRESS_REGENERATION")) { |
300 | 0 | requiredVersion = |
301 | 0 | cmGlobalNinjaGenerator::RequiredNinjaVersionForManifestRestat(); |
302 | 0 | } |
303 | |
|
304 | 0 | cmGlobalNinjaGenerator::WriteComment( |
305 | 0 | os, "Minimal version of Ninja required by this file"); |
306 | 0 | os << "ninja_required_version = " << requiredVersion << "\n\n"; |
307 | 0 | } |
308 | | |
309 | | void cmLocalNinjaGenerator::WriteNinjaConfigurationVariable( |
310 | | std::ostream& os, std::string const& config) |
311 | 0 | { |
312 | 0 | cmGlobalNinjaGenerator::WriteVariable( |
313 | 0 | os, "CONFIGURATION", config, |
314 | 0 | "Set configuration variable for custom commands."); |
315 | 0 | } |
316 | | |
317 | | void cmLocalNinjaGenerator::WritePools(std::ostream& os) |
318 | 0 | { |
319 | 0 | cmGlobalNinjaGenerator::WriteDivider(os); |
320 | |
|
321 | 0 | cmValue jobpools = |
322 | 0 | this->GetCMakeInstance()->GetState()->GetGlobalProperty("JOB_POOLS"); |
323 | 0 | if (!jobpools) { |
324 | 0 | jobpools = this->GetMakefile()->GetDefinition("CMAKE_JOB_POOLS"); |
325 | 0 | } |
326 | 0 | if (jobpools) { |
327 | 0 | cmGlobalNinjaGenerator::WriteComment( |
328 | 0 | os, "Pools defined by global property JOB_POOLS"); |
329 | 0 | cmList pools{ *jobpools }; |
330 | 0 | for (std::string const& pool : pools) { |
331 | 0 | std::string::size_type const eq = pool.find('='); |
332 | 0 | unsigned int jobs; |
333 | 0 | if (eq != std::string::npos && |
334 | 0 | sscanf(pool.c_str() + eq, "=%u", &jobs) == 1) { |
335 | 0 | os << "pool " << pool.substr(0, eq) << "\n depth = " << jobs |
336 | 0 | << "\n\n"; |
337 | 0 | } else { |
338 | 0 | cmSystemTools::Error("Invalid pool defined by property 'JOB_POOLS': " + |
339 | 0 | pool); |
340 | 0 | } |
341 | 0 | } |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | void cmLocalNinjaGenerator::WriteNinjaFilesInclusionConfig(std::ostream& os) |
346 | 0 | { |
347 | 0 | cmGlobalNinjaGenerator::WriteDivider(os); |
348 | 0 | os << "# Include auxiliary files.\n\n"; |
349 | 0 | cmGlobalNinjaGenerator* ng = this->GetGlobalNinjaGenerator(); |
350 | 0 | std::string const ninjaCommonFile = |
351 | 0 | ng->NinjaOutputPath(cmGlobalNinjaMultiGenerator::NINJA_COMMON_FILE); |
352 | 0 | std::string const commonFilePath = ng->EncodePath(ninjaCommonFile); |
353 | 0 | cmGlobalNinjaGenerator::WriteInclude(os, commonFilePath, |
354 | 0 | "Include common file."); |
355 | 0 | os << '\n'; |
356 | 0 | } |
357 | | |
358 | | void cmLocalNinjaGenerator::WriteNinjaFilesInclusionCommon(std::ostream& os) |
359 | 0 | { |
360 | 0 | cmGlobalNinjaGenerator::WriteDivider(os); |
361 | 0 | os << "# Include auxiliary files.\n\n"; |
362 | 0 | cmGlobalNinjaGenerator* ng = this->GetGlobalNinjaGenerator(); |
363 | 0 | std::string const ninjaRulesFile = |
364 | 0 | ng->NinjaOutputPath(cmGlobalNinjaGenerator::NINJA_RULES_FILE); |
365 | 0 | std::string const rulesFilePath = ng->EncodePath(ninjaRulesFile); |
366 | 0 | cmGlobalNinjaGenerator::WriteInclude(os, rulesFilePath, |
367 | 0 | "Include rules file."); |
368 | 0 | os << '\n'; |
369 | 0 | } |
370 | | |
371 | | void cmLocalNinjaGenerator::WriteNinjaWorkDir(std::ostream& os) |
372 | 0 | { |
373 | 0 | cmGlobalNinjaGenerator::WriteDivider(os); |
374 | 0 | cmGlobalNinjaGenerator::WriteComment( |
375 | 0 | os, "Logical path to working directory; prefix for absolute paths."); |
376 | 0 | cmGlobalNinjaGenerator* ng = this->GetGlobalNinjaGenerator(); |
377 | 0 | std::string ninja_workdir = this->GetBinaryDirectory(); |
378 | 0 | ng->StripNinjaOutputPathPrefixAsSuffix(ninja_workdir); // Also appends '/'. |
379 | 0 | os << "cmake_ninja_workdir = " << ng->EncodePath(ninja_workdir) << "\n"; |
380 | 0 | } |
381 | | |
382 | | void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os) |
383 | 0 | { |
384 | 0 | cmGlobalNinjaGenerator::WriteDivider(os); |
385 | 0 | os << "# Write statements declared in CMakeLists.txt:\n" |
386 | 0 | "# " |
387 | 0 | << this->Makefile->GetSafeDefinition("CMAKE_CURRENT_LIST_FILE") << '\n'; |
388 | 0 | if (this->IsRootMakefile()) { |
389 | 0 | os << "# Which is the root file.\n"; |
390 | 0 | } |
391 | 0 | cmGlobalNinjaGenerator::WriteDivider(os); |
392 | 0 | os << '\n'; |
393 | 0 | } |
394 | | |
395 | | void cmLocalNinjaGenerator::AppendTargetOutputs(cmGeneratorTarget* target, |
396 | | cmNinjaDeps& outputs, |
397 | | std::string const& config) |
398 | 0 | { |
399 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetOutputs(target, outputs, config, |
400 | 0 | DependOnTargetArtifact); |
401 | 0 | } |
402 | | |
403 | | void cmLocalNinjaGenerator::AppendTargetDepends(cmGeneratorTarget* target, |
404 | | cmNinjaDeps& outputs, |
405 | | std::string const& config, |
406 | | std::string const& fileConfig, |
407 | | cmNinjaTargetDepends depends) |
408 | 0 | { |
409 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetDepends(target, outputs, config, |
410 | 0 | fileConfig, depends); |
411 | 0 | } |
412 | | |
413 | | void cmLocalNinjaGenerator::AppendCustomCommandDeps( |
414 | | cmCustomCommandGenerator const& ccg, cmNinjaDeps& ninjaDeps, |
415 | | std::string const& config) |
416 | 0 | { |
417 | 0 | for (std::string const& i : ccg.GetDepends()) { |
418 | 0 | std::string dep; |
419 | 0 | if (this->GetRealDependency(i, config, dep, |
420 | 0 | ccg.GetCC().GetCMP0212Status())) { |
421 | 0 | ninjaDeps.push_back( |
422 | 0 | this->GetGlobalNinjaGenerator()->ConvertToNinjaPath(dep)); |
423 | 0 | } |
424 | 0 | } |
425 | 0 | } |
426 | | |
427 | | std::string cmLocalNinjaGenerator::WriteCommandScript( |
428 | | std::vector<std::string> const& cmdLines, std::string const& outputConfig, |
429 | | std::string const& commandConfig, std::string const& customStep, |
430 | | cmGeneratorTarget const* target) const |
431 | 0 | { |
432 | 0 | std::string scriptPath; |
433 | 0 | if (target) { |
434 | 0 | scriptPath = target->GetSupportDirectory(); |
435 | 0 | } else { |
436 | 0 | scriptPath = cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles"); |
437 | 0 | } |
438 | 0 | scriptPath += this->GetGlobalNinjaGenerator()->ConfigDirectory(outputConfig); |
439 | 0 | cmSystemTools::MakeDirectory(scriptPath); |
440 | 0 | scriptPath += '/'; |
441 | 0 | scriptPath += customStep; |
442 | 0 | if (this->GlobalGenerator->IsMultiConfig()) { |
443 | 0 | scriptPath = cmStrCat(std::move(scriptPath), '-', commandConfig); |
444 | 0 | } |
445 | | #ifdef _WIN32 |
446 | | scriptPath += ".bat"; |
447 | | #else |
448 | 0 | scriptPath += ".sh"; |
449 | 0 | #endif |
450 | |
|
451 | 0 | cmsys::ofstream script(scriptPath.c_str()); |
452 | |
|
453 | | #ifdef _WIN32 |
454 | | script << "@echo off\n"; |
455 | | int line = 1; |
456 | | #else |
457 | 0 | script << "set -e\n\n"; |
458 | 0 | #endif |
459 | |
|
460 | 0 | for (auto const& i : cmdLines) { |
461 | 0 | std::string cmd = i; |
462 | | // The command line was built assuming it would be written to |
463 | | // the build.ninja file, so it uses '$$' for '$'. Remove this |
464 | | // for the raw shell script. |
465 | 0 | cmSystemTools::ReplaceString(cmd, "$$", "$"); |
466 | | #ifdef _WIN32 |
467 | | script << cmd << " || (set FAIL_LINE=" << ++line << "& goto :ABORT)" |
468 | | << '\n'; |
469 | | #else |
470 | 0 | script << cmd << '\n'; |
471 | 0 | #endif |
472 | 0 | } |
473 | |
|
474 | | #ifdef _WIN32 |
475 | | script << "goto :EOF\n\n" |
476 | | ":ABORT\n" |
477 | | "set ERROR_CODE=%ERRORLEVEL%\n" |
478 | | "echo Batch file failed at line %FAIL_LINE% " |
479 | | "with errorcode %ERRORLEVEL%\n" |
480 | | "exit /b %ERROR_CODE%"; |
481 | | #elif !defined(CMAKE_BOOTSTRAP) |
482 | 0 | if (this->GetCMakeInstance()->GetInstrumentation()->HasOption( |
483 | 0 | cmInstrumentationQuery::Option::ProcessMetrics)) { |
484 | | // Prevent shell exec optimization from passing accumulated child resource |
485 | | // usage to the final instrumentation wrapper. |
486 | 0 | script << ":\n"; |
487 | 0 | } |
488 | 0 | #endif |
489 | |
|
490 | 0 | return scriptPath; |
491 | 0 | } |
492 | | |
493 | | #ifdef _WIN32 |
494 | | namespace { |
495 | | bool RuleNeedsCMD(std::string const& cmd) |
496 | | { |
497 | | std::vector<std::string> args; |
498 | | cmSystemTools::ParseWindowsCommandLine(cmd.c_str(), args); |
499 | | auto it = std::find_if(args.cbegin(), args.cend(), |
500 | | [](std::string const& arg) -> bool { |
501 | | // FIXME: Detect more windows shell operators. |
502 | | return cmHasPrefix(arg, '>'); |
503 | | }); |
504 | | return it != args.cend(); |
505 | | } |
506 | | } |
507 | | #endif |
508 | | |
509 | | std::string cmLocalNinjaGenerator::BuildCommandLine( |
510 | | std::vector<std::string> const& cmdLines, std::string const& outputConfig, |
511 | | std::string const& commandConfig, std::string const& customStep, |
512 | | cmGeneratorTarget const* target, CommandLineContext context) const |
513 | 0 | { |
514 | | // If we have no commands but we need to build a command anyway, use noop. |
515 | | // This happens when building a POST_BUILD value for link targets that |
516 | | // don't use POST_BUILD. |
517 | 0 | if (cmdLines.empty()) { |
518 | 0 | return cmGlobalNinjaGenerator::SHELL_NOOP; |
519 | 0 | } |
520 | | |
521 | | // If this is a custom step then we will have no '$VAR' ninja placeholders. |
522 | | // This means we can deal with long command sequences by writing to a script. |
523 | | // Do this if the command lines are on the scale of the OS limit. |
524 | 0 | if (!customStep.empty()) { |
525 | 0 | size_t cmdLinesTotal = 0; |
526 | 0 | for (std::string const& cmd : cmdLines) { |
527 | 0 | cmdLinesTotal += cmd.length() + 6; |
528 | 0 | } |
529 | 0 | if (cmdLinesTotal > cmSystemTools::CalculateCommandLineLengthLimit() / 2) { |
530 | 0 | std::string const scriptPath = this->WriteCommandScript( |
531 | 0 | cmdLines, outputConfig, commandConfig, customStep, target); |
532 | 0 | std::string cmd |
533 | 0 | #ifndef _WIN32 |
534 | 0 | = "/bin/sh " |
535 | 0 | #endif |
536 | 0 | ; |
537 | 0 | cmd += this->ConvertToOutputFormat( |
538 | 0 | this->GetGlobalNinjaGenerator()->ConvertToNinjaPath(scriptPath), |
539 | 0 | cmOutputConverter::SHELL); |
540 | | |
541 | | // Add an unused argument based on script content so that Ninja |
542 | | // knows when the command lines change. |
543 | 0 | cmd += " "; |
544 | 0 | cmCryptoHash hash(cmCryptoHash::AlgoSHA256); |
545 | 0 | cmd += hash.HashFile(scriptPath).substr(0, 16); |
546 | 0 | return cmd; |
547 | 0 | } |
548 | 0 | } |
549 | | |
550 | 0 | std::ostringstream cmd; |
551 | | #ifdef _WIN32 |
552 | | (void)context; |
553 | | cmGlobalNinjaGenerator const* gg = this->GetGlobalNinjaGenerator(); |
554 | | bool const needCMD = |
555 | | cmdLines.size() > 1 || (customStep.empty() && RuleNeedsCMD(cmdLines[0])); |
556 | | for (auto li = cmdLines.begin(); li != cmdLines.end(); ++li) { |
557 | | if (li != cmdLines.begin()) { |
558 | | cmd << " && "; |
559 | | } else if (needCMD) { |
560 | | cmd << gg->GetComspec() << " /C \""; |
561 | | } |
562 | | // Put current cmdLine in brackets if it contains "||" because it has |
563 | | // higher precedence than "&&" in cmd.exe |
564 | | if (li->find("||") != std::string::npos) { |
565 | | cmd << "( " << *li << " )"; |
566 | | } else { |
567 | | cmd << *li; |
568 | | } |
569 | | } |
570 | | if (needCMD) { |
571 | | cmd << "\""; |
572 | | } |
573 | | #else |
574 | 0 | for (auto li = cmdLines.begin(); li != cmdLines.end(); ++li) { |
575 | 0 | if (li != cmdLines.begin()) { |
576 | 0 | cmd << " && "; |
577 | 0 | } |
578 | 0 | cmd << *li; |
579 | 0 | } |
580 | 0 | # ifndef CMAKE_BOOTSTRAP |
581 | 0 | if (context == CommandLineContext::Build && |
582 | 0 | this->GetCMakeInstance()->GetInstrumentation()->HasOption( |
583 | 0 | cmInstrumentationQuery::Option::ProcessMetrics)) { |
584 | | // Prevent shell exec optimization from passing accumulated child resource |
585 | | // usage to the final instrumentation wrapper. Keep this build-only shell |
586 | | // syntax out of compile_commands.json, for compiler tooling support |
587 | 0 | cmd << " && :"; |
588 | 0 | } |
589 | | # else |
590 | | (void)context; |
591 | | # endif |
592 | 0 | #endif |
593 | 0 | return cmd.str(); |
594 | 0 | } |
595 | | |
596 | | void cmLocalNinjaGenerator::AppendCustomCommandLines( |
597 | | cmCustomCommandGenerator const& ccg, std::vector<std::string>& cmdLines) |
598 | 0 | { |
599 | 0 | auto* gg = this->GetGlobalNinjaGenerator(); |
600 | |
|
601 | 0 | if (ccg.GetNumberOfCommands() > 0) { |
602 | 0 | std::string wd = ccg.GetWorkingDirectory(); |
603 | 0 | if (wd.empty()) { |
604 | 0 | wd = this->GetCurrentBinaryDirectory(); |
605 | 0 | } |
606 | |
|
607 | 0 | std::ostringstream cdCmd; |
608 | | #ifdef _WIN32 |
609 | | std::string cdStr = "cd /D "; |
610 | | #else |
611 | 0 | std::string cdStr = "cd "; |
612 | 0 | #endif |
613 | 0 | cdCmd << cdStr |
614 | 0 | << this->ConvertToOutputFormat(wd, cmOutputConverter::SHELL); |
615 | 0 | cmdLines.push_back(cdCmd.str()); |
616 | 0 | } |
617 | |
|
618 | 0 | std::string launcher = this->MakeCustomLauncher(ccg); |
619 | |
|
620 | 0 | for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) { |
621 | 0 | std::string c = ccg.GetCommand(i); |
622 | 0 | if (c.empty()) { |
623 | 0 | continue; |
624 | 0 | } |
625 | 0 | cmdLines.push_back(launcher + |
626 | 0 | this->ConvertToOutputFormat( |
627 | 0 | c, |
628 | 0 | gg->IsMultiConfig() ? cmOutputConverter::NINJAMULTI |
629 | 0 | : cmOutputConverter::SHELL)); |
630 | |
|
631 | 0 | std::string& cmd = cmdLines.back(); |
632 | 0 | ccg.AppendArguments(i, cmd); |
633 | 0 | } |
634 | 0 | } |
635 | | |
636 | | void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( |
637 | | cmCustomCommand const* cc, std::set<cmGeneratorTarget*> const& targets, |
638 | | std::string const& fileConfig) |
639 | 0 | { |
640 | 0 | cmGlobalNinjaGenerator* gg = this->GetGlobalNinjaGenerator(); |
641 | 0 | if (gg->SeenCustomCommand(cc, fileConfig)) { |
642 | 0 | return; |
643 | 0 | } |
644 | | |
645 | 0 | auto ccgs = this->MakeCustomCommandGenerators(*cc, fileConfig); |
646 | 0 | for (cmCustomCommandGenerator const& ccg : ccgs) { |
647 | 0 | if (ccg.GetOutputs().empty() && ccg.GetByproducts().empty()) { |
648 | | // Generator expressions evaluate to no output for this config. |
649 | 0 | continue; |
650 | 0 | } |
651 | | |
652 | 0 | std::unordered_set<std::string> orderOnlyDeps; |
653 | |
|
654 | 0 | if (!cc->GetDependsExplicitOnly()) { |
655 | | // A custom command may appear on multiple targets. However, some build |
656 | | // systems exist where the target dependencies on some of the targets are |
657 | | // overspecified, leading to a dependency cycle. If we assume all target |
658 | | // dependencies are a superset of the true target dependencies for this |
659 | | // custom command, we can take the set intersection of all target |
660 | | // dependencies to obtain a correct dependency list. |
661 | | // |
662 | | // FIXME: This won't work in certain obscure scenarios involving indirect |
663 | | // dependencies. |
664 | 0 | auto j = targets.begin(); |
665 | 0 | assert(j != targets.end()); |
666 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure( |
667 | 0 | *j, orderOnlyDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1); |
668 | 0 | ++j; |
669 | |
|
670 | 0 | for (; j != targets.end(); ++j) { |
671 | 0 | std::unordered_set<std::string> jDeps; |
672 | 0 | this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure( |
673 | 0 | *j, jDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1); |
674 | 0 | cm::erase_if(orderOnlyDeps, [&jDeps](std::string const& dep) { |
675 | 0 | return jDeps.find(dep) == jDeps.end(); |
676 | 0 | }); |
677 | 0 | } |
678 | 0 | } |
679 | |
|
680 | 0 | std::vector<std::string> const& outputs = ccg.GetOutputs(); |
681 | 0 | std::vector<std::string> const& byproducts = ccg.GetByproducts(); |
682 | |
|
683 | 0 | bool symbolic = false; |
684 | 0 | for (std::string const& output : outputs) { |
685 | 0 | if (cmSourceFile* sf = this->Makefile->GetSource(output)) { |
686 | 0 | if (sf->GetPropertyAsBool("SYMBOLIC")) { |
687 | 0 | symbolic = true; |
688 | 0 | break; |
689 | 0 | } |
690 | 0 | } |
691 | 0 | } |
692 | |
|
693 | 0 | cmGlobalNinjaGenerator::CCOutputs ccOutputs(gg); |
694 | 0 | ccOutputs.Add(outputs); |
695 | 0 | ccOutputs.Add(byproducts); |
696 | |
|
697 | 0 | std::string mainOutput = ccOutputs.ExplicitOuts[0]; |
698 | |
|
699 | 0 | cmNinjaDeps ninjaDeps; |
700 | 0 | this->AppendCustomCommandDeps(ccg, ninjaDeps, fileConfig); |
701 | |
|
702 | 0 | std::vector<std::string> cmdLines; |
703 | 0 | this->AppendCustomCommandLines(ccg, cmdLines); |
704 | |
|
705 | 0 | cmNinjaDeps sortedOrderOnlyDeps(orderOnlyDeps.begin(), |
706 | 0 | orderOnlyDeps.end()); |
707 | 0 | std::sort(sortedOrderOnlyDeps.begin(), sortedOrderOnlyDeps.end()); |
708 | |
|
709 | 0 | if (cmdLines.empty()) { |
710 | 0 | cmNinjaBuild build("phony"); |
711 | 0 | build.Comment = cmStrCat("Phony custom command for ", mainOutput); |
712 | 0 | build.Outputs = std::move(ccOutputs.ExplicitOuts); |
713 | 0 | build.WorkDirOuts = std::move(ccOutputs.WorkDirOuts); |
714 | 0 | build.ExplicitDeps = std::move(ninjaDeps); |
715 | 0 | build.OrderOnlyDeps = std::move(sortedOrderOnlyDeps); |
716 | 0 | gg->WriteBuild(this->GetImplFileStream(fileConfig), build); |
717 | 0 | } else { |
718 | 0 | std::string customStep = cmSystemTools::GetFilenameName(mainOutput); |
719 | 0 | if (this->GlobalGenerator->IsMultiConfig()) { |
720 | 0 | customStep += '-'; |
721 | 0 | customStep += fileConfig; |
722 | 0 | customStep += '-'; |
723 | 0 | customStep += ccg.GetOutputConfig(); |
724 | 0 | } |
725 | | // Hash full path to make unique. |
726 | 0 | customStep += '-'; |
727 | 0 | cmCryptoHash hash(cmCryptoHash::AlgoSHA256); |
728 | 0 | customStep += hash.HashString(mainOutput).substr(0, 7); |
729 | |
|
730 | 0 | std::string depfile = ccg.GetDepfile(); |
731 | 0 | if (!depfile.empty()) { |
732 | 0 | switch (cc->GetCMP0116Status()) { |
733 | 0 | case cmPolicies::WARN: |
734 | 0 | if (this->GetCurrentBinaryDirectory() != |
735 | 0 | this->GetBinaryDirectory() || |
736 | 0 | this->Makefile->PolicyOptionalWarningEnabled( |
737 | 0 | "CMAKE_POLICY_WARNING_CMP0116")) { |
738 | 0 | this->GetCMakeInstance()->IssueDiagnostic( |
739 | 0 | cmDiagnostics::CMD_POLICY, |
740 | 0 | cmPolicies::GetPolicyWarning(cmPolicies::CMP0116), |
741 | 0 | cc->GetBacktrace()); |
742 | 0 | } |
743 | 0 | CM_FALLTHROUGH; |
744 | 0 | case cmPolicies::OLD: |
745 | 0 | break; |
746 | 0 | case cmPolicies::NEW: |
747 | 0 | depfile = ccg.GetInternalDepfile(); |
748 | 0 | break; |
749 | 0 | } |
750 | 0 | } |
751 | | |
752 | 0 | std::string comment = cmStrCat("Custom command for ", mainOutput); |
753 | 0 | gg->WriteCustomCommandBuild( |
754 | 0 | this->BuildCommandLine(cmdLines, ccg.GetOutputConfig(), fileConfig, |
755 | 0 | customStep), |
756 | 0 | this->ConstructComment(ccg), comment, depfile, cc->GetJobPool(), |
757 | 0 | cc->GetUsesTerminal(), |
758 | 0 | /*restat*/ !symbolic || !byproducts.empty(), fileConfig, |
759 | 0 | std::move(ccOutputs), std::move(ninjaDeps), |
760 | 0 | std::move(sortedOrderOnlyDeps)); |
761 | 0 | } |
762 | 0 | } |
763 | 0 | } |
764 | | |
765 | | bool cmLocalNinjaGenerator::HasUniqueByproducts( |
766 | | std::vector<std::string> const& byproducts, cmListFileBacktrace const& bt) |
767 | 0 | { |
768 | 0 | cmGeneratorExpression ge(*this->GetCMakeInstance(), bt); |
769 | 0 | for (std::string const& p : byproducts) { |
770 | 0 | if (cmGeneratorExpression::Find(p) == std::string::npos) { |
771 | 0 | return false; |
772 | 0 | } |
773 | 0 | std::set<std::string> seen; |
774 | 0 | std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(p); |
775 | 0 | for (std::string const& config : this->GetConfigNames()) { |
776 | 0 | for (std::string const& b : |
777 | 0 | this->ExpandCustomCommandOutputPaths(*cge, config)) { |
778 | 0 | if (!seen.insert(b).second) { |
779 | 0 | return false; |
780 | 0 | } |
781 | 0 | } |
782 | 0 | } |
783 | 0 | } |
784 | 0 | return true; |
785 | 0 | } |
786 | | |
787 | | namespace { |
788 | | bool HasUniqueOutputs(std::vector<cmCustomCommandGenerator> const& ccgs) |
789 | 0 | { |
790 | 0 | std::set<std::string> allOutputs; |
791 | 0 | std::set<std::string> allByproducts; |
792 | 0 | for (cmCustomCommandGenerator const& ccg : ccgs) { |
793 | 0 | for (std::string const& output : ccg.GetOutputs()) { |
794 | 0 | if (!allOutputs.insert(output).second) { |
795 | 0 | return false; |
796 | 0 | } |
797 | 0 | } |
798 | 0 | for (std::string const& byproduct : ccg.GetByproducts()) { |
799 | 0 | if (!allByproducts.insert(byproduct).second) { |
800 | 0 | return false; |
801 | 0 | } |
802 | 0 | } |
803 | 0 | } |
804 | 0 | return true; |
805 | 0 | } |
806 | | } |
807 | | |
808 | | std::string cmLocalNinjaGenerator::CreateUtilityOutput( |
809 | | std::string const& targetName, std::vector<std::string> const& byproducts, |
810 | | cmListFileBacktrace const& bt) |
811 | 0 | { |
812 | | // In Ninja Multi-Config, we can only produce cross-config utility |
813 | | // commands if all byproducts are per-config. |
814 | 0 | if (!this->GetGlobalGenerator()->IsMultiConfig() || |
815 | 0 | !this->HasUniqueByproducts(byproducts, bt)) { |
816 | 0 | return this->cmLocalGenerator::CreateUtilityOutput(targetName, byproducts, |
817 | 0 | bt); |
818 | 0 | } |
819 | | |
820 | 0 | std::string const base = cmStrCat(this->GetCurrentBinaryDirectory(), |
821 | 0 | "/CMakeFiles/", targetName, '-'); |
822 | | // The output is not actually created so mark it symbolic. |
823 | 0 | for (std::string const& config : this->GetConfigNames()) { |
824 | 0 | std::string const force = cmStrCat(base, config); |
825 | 0 | if (cmSourceFile* sf = this->Makefile->GetOrCreateGeneratedSource(force)) { |
826 | 0 | sf->SetProperty("SYMBOLIC", "1"); |
827 | 0 | } else { |
828 | 0 | cmSystemTools::Error("Could not get source file entry for " + force); |
829 | 0 | } |
830 | 0 | } |
831 | 0 | this->GetGlobalNinjaGenerator()->AddPerConfigUtilityTarget(targetName); |
832 | 0 | return cmStrCat(base, "$<CONFIG>"_s); |
833 | 0 | } |
834 | | |
835 | | std::vector<cmCustomCommandGenerator> |
836 | | cmLocalNinjaGenerator::MakeCustomCommandGenerators( |
837 | | cmCustomCommand const& cc, std::string const& fileConfig) |
838 | 0 | { |
839 | 0 | cmGlobalNinjaGenerator const* gg = this->GetGlobalNinjaGenerator(); |
840 | |
|
841 | 0 | bool transformDepfile = false; |
842 | 0 | switch (cc.GetCMP0116Status()) { |
843 | 0 | case cmPolicies::WARN: |
844 | 0 | CM_FALLTHROUGH; |
845 | 0 | case cmPolicies::OLD: |
846 | 0 | break; |
847 | 0 | case cmPolicies::NEW: |
848 | 0 | transformDepfile = true; |
849 | 0 | break; |
850 | 0 | } |
851 | | |
852 | | // Start with the build graph's configuration. |
853 | 0 | std::vector<cmCustomCommandGenerator> ccgs; |
854 | 0 | ccgs.emplace_back(cc, fileConfig, this, transformDepfile); |
855 | | |
856 | | // Consider adding cross configurations. |
857 | 0 | if (!gg->EnableCrossConfigBuild()) { |
858 | 0 | return ccgs; |
859 | 0 | } |
860 | | |
861 | | // Outputs and byproducts must be expressed using generator expressions. |
862 | 0 | for (std::string const& output : cc.GetOutputs()) { |
863 | 0 | if (cmGeneratorExpression::Find(output) == std::string::npos) { |
864 | 0 | return ccgs; |
865 | 0 | } |
866 | 0 | } |
867 | 0 | for (std::string const& byproduct : cc.GetByproducts()) { |
868 | 0 | if (cmGeneratorExpression::Find(byproduct) == std::string::npos) { |
869 | 0 | return ccgs; |
870 | 0 | } |
871 | 0 | } |
872 | | |
873 | | // Tentatively add the other cross configurations. |
874 | 0 | for (std::string const& config : gg->GetCrossConfigs(fileConfig)) { |
875 | 0 | if (fileConfig != config) { |
876 | 0 | ccgs.emplace_back(cc, fileConfig, this, transformDepfile, config); |
877 | 0 | } |
878 | 0 | } |
879 | | |
880 | | // If outputs and byproducts are not unique to each configuration, |
881 | | // drop the cross configurations. |
882 | 0 | if (!HasUniqueOutputs(ccgs)) { |
883 | 0 | ccgs.erase(ccgs.begin() + 1, ccgs.end()); |
884 | 0 | } |
885 | |
|
886 | 0 | return ccgs; |
887 | 0 | } |
888 | | |
889 | | void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand const* cc, |
890 | | cmGeneratorTarget* target) |
891 | 0 | { |
892 | 0 | CustomCommandTargetMap::value_type v(cc, std::set<cmGeneratorTarget*>()); |
893 | 0 | std::pair<CustomCommandTargetMap::iterator, bool> ins = |
894 | 0 | this->CustomCommandTargets.insert(v); |
895 | 0 | if (ins.second) { |
896 | 0 | this->CustomCommands.push_back(cc); |
897 | 0 | } |
898 | 0 | ins.first->second.insert(target); |
899 | 0 | } |
900 | | |
901 | | void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements( |
902 | | std::string const& fileConfig) |
903 | 0 | { |
904 | 0 | for (cmCustomCommand const* customCommand : this->CustomCommands) { |
905 | 0 | auto i = this->CustomCommandTargets.find(customCommand); |
906 | 0 | assert(i != this->CustomCommandTargets.end()); |
907 | |
|
908 | 0 | this->WriteCustomCommandBuildStatement(i->first, i->second, fileConfig); |
909 | 0 | } |
910 | 0 | } |
911 | | |
912 | | std::string cmLocalNinjaGenerator::MakeCustomLauncher( |
913 | | cmCustomCommandGenerator const& ccg) |
914 | 0 | { |
915 | 0 | cmValue property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM"); |
916 | |
|
917 | 0 | if (!cmNonempty(property_value)) { |
918 | 0 | return std::string(); |
919 | 0 | } |
920 | | |
921 | | // Expand rule variables referenced in the given launcher command. |
922 | 0 | cmRulePlaceholderExpander::RuleVariables vars; |
923 | |
|
924 | 0 | std::string output; |
925 | 0 | std::vector<std::string> const& outputs = ccg.GetOutputs(); |
926 | 0 | for (std::string const& ccgOut : outputs) { |
927 | 0 | output = cmStrCat( |
928 | 0 | std::move(output), |
929 | 0 | this->ConvertToOutputFormat(ccg.GetWorkingDirectory().empty() |
930 | 0 | ? this->MaybeRelativeToCurBinDir(ccgOut) |
931 | 0 | : ccgOut, |
932 | 0 | cmOutputConverter::SHELL), |
933 | 0 | ','); |
934 | 0 | } |
935 | 0 | if (!output.empty()) { |
936 | 0 | output.pop_back(); |
937 | 0 | } |
938 | 0 | vars.Output = output.c_str(); |
939 | |
|
940 | 0 | std::string filePathWithOutput = ccg.StoreContentToFile(output); |
941 | 0 | vars.FilePathWithOutput = filePathWithOutput.c_str(); |
942 | |
|
943 | 0 | vars.Role = ccg.GetCC().GetRole().c_str(); |
944 | 0 | vars.CMTargetName = ccg.GetCC().GetTarget().c_str(); |
945 | 0 | vars.Config = ccg.GetOutputConfig().c_str(); |
946 | |
|
947 | 0 | auto rulePlaceholderExpander = this->CreateRulePlaceholderExpander(); |
948 | |
|
949 | 0 | std::string launcher = *property_value; |
950 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this, launcher, vars); |
951 | 0 | if (!launcher.empty()) { |
952 | 0 | launcher += " "; |
953 | 0 | } |
954 | |
|
955 | 0 | return launcher; |
956 | 0 | } |
957 | | |
958 | | void cmLocalNinjaGenerator::AdditionalCleanFiles(std::string const& config) |
959 | 0 | { |
960 | 0 | if (cmValue prop_value = |
961 | 0 | this->Makefile->GetProperty("ADDITIONAL_CLEAN_FILES")) { |
962 | 0 | cmList cleanFiles{ cmGeneratorExpression::Evaluate(*prop_value, this, |
963 | 0 | config) }; |
964 | 0 | std::string const& binaryDir = this->GetCurrentBinaryDirectory(); |
965 | 0 | cmGlobalNinjaGenerator* gg = this->GetGlobalNinjaGenerator(); |
966 | 0 | for (auto const& cleanFile : cleanFiles) { |
967 | | // Support relative paths |
968 | 0 | gg->AddAdditionalCleanFile( |
969 | 0 | cmSystemTools::CollapseFullPath(cleanFile, binaryDir), config); |
970 | 0 | } |
971 | 0 | } |
972 | 0 | } |