/src/CMake/Source/cmNinjaTargetGenerator.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 "cmNinjaTargetGenerator.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <array> |
7 | | #include <cassert> |
8 | | #include <functional> |
9 | | #include <iterator> |
10 | | #include <map> |
11 | | #include <ostream> |
12 | | #include <unordered_map> |
13 | | #include <unordered_set> |
14 | | #include <utility> |
15 | | |
16 | | #include <cm/memory> |
17 | | #include <cm/optional> |
18 | | #include <cm/string_view> |
19 | | #include <cmext/algorithm> |
20 | | #include <cmext/string_view> |
21 | | |
22 | | #include <cm3p/json/value.h> |
23 | | #include <cm3p/json/writer.h> |
24 | | |
25 | | #include "cmBuildDatabase.h" |
26 | | #include "cmComputeLinkInformation.h" |
27 | | #include "cmCustomCommand.h" |
28 | | #include "cmCustomCommandGenerator.h" |
29 | | #include "cmDiagnostics.h" |
30 | | #include "cmDyndepCollation.h" |
31 | | #include "cmFileSetMetadata.h" |
32 | | #include "cmGeneratedFileStream.h" |
33 | | #include "cmGeneratorExpression.h" |
34 | | #include "cmGeneratorFileSet.h" |
35 | | #include "cmGeneratorFileSets.h" |
36 | | #include "cmGeneratorOptions.h" |
37 | | #include "cmGeneratorTarget.h" |
38 | | #include "cmGlobalCommonGenerator.h" |
39 | | #include "cmGlobalNinjaGenerator.h" |
40 | | #include "cmList.h" |
41 | | #include "cmListFileCache.h" |
42 | | #include "cmLocalGenerator.h" |
43 | | #include "cmLocalNinjaGenerator.h" |
44 | | #include "cmMakefile.h" |
45 | | #include "cmMessageType.h" |
46 | | #include "cmNinjaNormalTargetGenerator.h" |
47 | | #include "cmNinjaUtilityTargetGenerator.h" |
48 | | #include "cmOutputConverter.h" |
49 | | #include "cmPolicies.h" |
50 | | #include "cmRange.h" |
51 | | #include "cmRulePlaceholderExpander.h" |
52 | | #include "cmSourceFile.h" |
53 | | #include "cmState.h" |
54 | | #include "cmStateTypes.h" |
55 | | #include "cmStringAlgorithms.h" |
56 | | #include "cmSystemTools.h" |
57 | | #include "cmTarget.h" |
58 | | #include "cmTargetDepend.h" |
59 | | #include "cmValue.h" |
60 | | #include "cmake.h" |
61 | | |
62 | | std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New( |
63 | | cmGeneratorTarget* target) |
64 | 0 | { |
65 | 0 | switch (target->GetType()) { |
66 | 0 | case cmStateEnums::EXECUTABLE: |
67 | 0 | case cmStateEnums::SHARED_LIBRARY: |
68 | 0 | case cmStateEnums::STATIC_LIBRARY: |
69 | 0 | case cmStateEnums::MODULE_LIBRARY: |
70 | 0 | case cmStateEnums::OBJECT_LIBRARY: |
71 | 0 | return cm::make_unique<cmNinjaNormalTargetGenerator>(target); |
72 | | |
73 | 0 | case cmStateEnums::INTERFACE_LIBRARY: |
74 | 0 | if (target->HaveCxx20ModuleSources()) { |
75 | 0 | return cm::make_unique<cmNinjaNormalTargetGenerator>(target); |
76 | 0 | } |
77 | 0 | CM_FALLTHROUGH; |
78 | |
|
79 | 0 | case cmStateEnums::UTILITY: |
80 | 0 | case cmStateEnums::GLOBAL_TARGET: |
81 | 0 | return cm::make_unique<cmNinjaUtilityTargetGenerator>(target); |
82 | | |
83 | 0 | default: |
84 | 0 | return std::unique_ptr<cmNinjaTargetGenerator>(); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmGeneratorTarget* target) |
89 | 0 | : cmCommonTargetGenerator(target) |
90 | 0 | , OSXBundleGenerator(nullptr) |
91 | | , LocalGenerator( |
92 | 0 | static_cast<cmLocalNinjaGenerator*>(target->GetLocalGenerator())) |
93 | 0 | { |
94 | 0 | for (auto const& fileConfig : this->LocalGenerator->GetConfigNames()) { |
95 | 0 | this->Configs[fileConfig].MacOSXContentGenerator = |
96 | 0 | cm::make_unique<MacOSXContentGeneratorType>(this, fileConfig); |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | cmNinjaTargetGenerator::~cmNinjaTargetGenerator() = default; |
101 | | |
102 | | cmGeneratedFileStream& cmNinjaTargetGenerator::GetImplFileStream( |
103 | | std::string const& config) const |
104 | 0 | { |
105 | 0 | return *this->GetGlobalGenerator()->GetImplFileStream(config); |
106 | 0 | } |
107 | | |
108 | | cmGeneratedFileStream& cmNinjaTargetGenerator::GetCommonFileStream() const |
109 | 0 | { |
110 | 0 | return *this->GetGlobalGenerator()->GetCommonFileStream(); |
111 | 0 | } |
112 | | |
113 | | cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const |
114 | 0 | { |
115 | 0 | return *this->GetGlobalGenerator()->GetRulesFileStream(); |
116 | 0 | } |
117 | | |
118 | | cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const |
119 | 0 | { |
120 | 0 | return this->LocalGenerator->GetGlobalNinjaGenerator(); |
121 | 0 | } |
122 | | |
123 | | std::string cmNinjaTargetGenerator::LanguageCompilerRule( |
124 | | std::string const& lang, std::string const& config, |
125 | | WithScanning withScanning) const |
126 | 0 | { |
127 | 0 | return cmStrCat( |
128 | 0 | lang, "_COMPILER__", |
129 | 0 | cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), |
130 | 0 | withScanning == WithScanning::Yes ? "_scanned_" : "_unscanned_", config); |
131 | 0 | } |
132 | | |
133 | | std::string cmNinjaTargetGenerator::LanguageEmitModuleRule( |
134 | | std::string const& lang, std::string const& config) const |
135 | 0 | { |
136 | 0 | return cmStrCat( |
137 | 0 | lang, "_EMIT_MODULE__", |
138 | 0 | cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), |
139 | 0 | '_', config); |
140 | 0 | } |
141 | | |
142 | | std::string cmNinjaTargetGenerator::LanguagePreprocessAndScanRule( |
143 | | std::string const& lang, std::string const& config) const |
144 | 0 | { |
145 | 0 | return cmStrCat( |
146 | 0 | lang, "_PREPROCESS_SCAN__", |
147 | 0 | cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), |
148 | 0 | '_', config); |
149 | 0 | } |
150 | | |
151 | | std::string cmNinjaTargetGenerator::LanguageScanRule( |
152 | | std::string const& lang, std::string const& config) const |
153 | 0 | { |
154 | 0 | return cmStrCat( |
155 | 0 | lang, "_SCAN__", |
156 | 0 | cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), |
157 | 0 | '_', config); |
158 | 0 | } |
159 | | |
160 | | bool cmNinjaTargetGenerator::NeedExplicitPreprocessing( |
161 | | std::string const& lang) const |
162 | 0 | { |
163 | 0 | return lang == "Fortran" || lang == "Swift"; |
164 | 0 | } |
165 | | |
166 | | bool cmNinjaTargetGenerator::CompileWithDefines(std::string const& lang) const |
167 | 0 | { |
168 | 0 | return this->Makefile->IsOn( |
169 | 0 | cmStrCat("CMAKE_", lang, "_COMPILE_WITH_DEFINES")); |
170 | 0 | } |
171 | | |
172 | | std::string cmNinjaTargetGenerator::LanguageDyndepRule( |
173 | | std::string const& lang, std::string const& config) const |
174 | 0 | { |
175 | 0 | return cmStrCat( |
176 | 0 | lang, "_DYNDEP__", |
177 | 0 | cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), |
178 | 0 | '_', config); |
179 | 0 | } |
180 | | |
181 | | std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget( |
182 | | std::string const& config) |
183 | 0 | { |
184 | 0 | return this->GetGlobalGenerator()->OrderDependsTargetForTarget( |
185 | 0 | this->GeneratorTarget, config); |
186 | 0 | } |
187 | | |
188 | | std::string cmNinjaTargetGenerator::OrderDependsTargetForTargetPrivate( |
189 | | std::string const& config) |
190 | 0 | { |
191 | 0 | return this->GetGlobalGenerator()->OrderDependsTargetForTargetPrivate( |
192 | 0 | this->GeneratorTarget, config); |
193 | 0 | } |
194 | | |
195 | | // TODO: Most of the code is picked up from |
196 | | // void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink), |
197 | | // void cmMakefileTargetGenerator::WriteTargetLanguageFlags() |
198 | | // Refactor it. |
199 | | std::string cmNinjaTargetGenerator::ComputeFlagsForObject( |
200 | | cmSourceFile const* source, std::string const& language, |
201 | | std::string const& config, std::string const& objectFileName) |
202 | 0 | { |
203 | 0 | std::unordered_map<std::string, std::string> pchSources; |
204 | 0 | std::vector<std::string> pchArchs = |
205 | 0 | this->GeneratorTarget->GetPchArchs(config, language); |
206 | |
|
207 | 0 | std::string filterArch; |
208 | 0 | for (std::string const& arch : pchArchs) { |
209 | 0 | std::string const pchSource = |
210 | 0 | this->GeneratorTarget->GetPchSource(config, language, arch); |
211 | 0 | if (pchSource == source->GetFullPath()) { |
212 | 0 | filterArch = arch; |
213 | 0 | } |
214 | 0 | if (!pchSource.empty()) { |
215 | 0 | pchSources.insert(std::make_pair(pchSource, arch)); |
216 | 0 | } |
217 | 0 | } |
218 | |
|
219 | 0 | std::string flags; |
220 | | // Explicitly add the explicit language flag before any other flag |
221 | | // so user flags can override it. |
222 | 0 | this->GeneratorTarget->AddExplicitLanguageFlags(flags, *source); |
223 | |
|
224 | 0 | if (!flags.empty()) { |
225 | 0 | flags += " "; |
226 | 0 | } |
227 | 0 | flags += this->GetFlags(language, config, filterArch); |
228 | | |
229 | | // Add Fortran format flags. |
230 | 0 | if (language == "Fortran") { |
231 | 0 | this->AppendFortranFormatFlags(flags, *source); |
232 | 0 | this->AppendFortranPreprocessFlags(flags, *source, |
233 | 0 | PreprocessFlagsRequired::NO); |
234 | 0 | } |
235 | | |
236 | | // Add source file specific flags. |
237 | 0 | cmGeneratorExpressionInterpreter genexInterpreter( |
238 | 0 | this->LocalGenerator, config, this->GeneratorTarget, language); |
239 | |
|
240 | 0 | std::string const COMPILE_FLAGS("COMPILE_FLAGS"); |
241 | 0 | if (cmValue cflags = source->GetProperty(COMPILE_FLAGS)) { |
242 | 0 | this->LocalGenerator->AppendFlags( |
243 | 0 | flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); |
244 | 0 | } |
245 | |
|
246 | 0 | std::string const COMPILE_OPTIONS("COMPILE_OPTIONS"); |
247 | 0 | if (cmValue coptions = source->GetProperty(COMPILE_OPTIONS)) { |
248 | 0 | this->LocalGenerator->AppendCompileOptions( |
249 | 0 | flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); |
250 | 0 | } |
251 | |
|
252 | 0 | auto const* const fileSet = |
253 | 0 | this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(config, |
254 | 0 | source); |
255 | |
|
256 | 0 | if (fileSet) { |
257 | 0 | auto options = fileSet->BelongsTo(this->GeneratorTarget) |
258 | 0 | ? fileSet->GetCompileOptions(config, language) |
259 | 0 | : fileSet->GetInterfaceCompileOptions(config, language); |
260 | 0 | if (!options.empty()) { |
261 | 0 | this->LocalGenerator->AppendCompileOptions(flags, |
262 | 0 | cm::remove_BT(options)); |
263 | 0 | } |
264 | 0 | } |
265 | | |
266 | | // Add precompile headers compile options. |
267 | 0 | if (!pchSources.empty() && |
268 | 0 | !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || |
269 | 0 | source->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { |
270 | 0 | std::string pchOptions; |
271 | 0 | auto pchIt = pchSources.find(source->GetFullPath()); |
272 | 0 | if (pchIt != pchSources.end()) { |
273 | 0 | pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions( |
274 | 0 | config, language, pchIt->second); |
275 | 0 | } else { |
276 | 0 | pchOptions = |
277 | 0 | this->GeneratorTarget->GetPchUseCompileOptions(config, language); |
278 | 0 | } |
279 | |
|
280 | 0 | this->LocalGenerator->AppendCompileOptions( |
281 | 0 | flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS)); |
282 | 0 | } |
283 | |
|
284 | 0 | if (fileSet && fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) { |
285 | 0 | if (source->GetLanguage() != "CXX"_s) { |
286 | 0 | this->GetMakefile()->IssueMessage( |
287 | 0 | MessageType::FATAL_ERROR, |
288 | 0 | cmStrCat("Target \"", this->GeneratorTarget->Target->GetName(), |
289 | 0 | "\" contains the source\n ", source->GetFullPath(), |
290 | 0 | "\nin a file set of type \"", fileSet->GetType(), |
291 | 0 | R"(" but the source is not classified as a "CXX" source.)")); |
292 | 0 | } |
293 | |
|
294 | 0 | if (!this->GeneratorTarget->Target->IsNormal()) { |
295 | 0 | if (this->GetMakefile() |
296 | 0 | ->GetDefinition("CMAKE_CXX_COMPILE_BMI") |
297 | 0 | .IsEmpty()) { |
298 | 0 | auto flag = this->GetMakefile()->GetSafeDefinition( |
299 | 0 | "CMAKE_CXX_MODULE_BMI_ONLY_FLAG"); |
300 | 0 | cmRulePlaceholderExpander::RuleVariables compileObjectVars; |
301 | 0 | compileObjectVars.Object = objectFileName.c_str(); |
302 | 0 | auto rulePlaceholderExpander = |
303 | 0 | this->GetLocalGenerator()->CreateRulePlaceholderExpander(); |
304 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), |
305 | 0 | flag, compileObjectVars); |
306 | 0 | this->LocalGenerator->AppendCompileOptions(flags, flag); |
307 | 0 | } |
308 | 0 | } |
309 | 0 | } |
310 | |
|
311 | 0 | return flags; |
312 | 0 | } |
313 | | |
314 | | void cmNinjaTargetGenerator::AddIncludeFlags(std::string& languageFlags, |
315 | | std::string const& language, |
316 | | std::string const& config) |
317 | 0 | { |
318 | 0 | std::vector<std::string> includes; |
319 | 0 | this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, |
320 | 0 | language, config); |
321 | | // Add include directory flags. |
322 | 0 | std::string includeFlags = this->LocalGenerator->GetIncludeFlags( |
323 | 0 | includes, this->GeneratorTarget, language, config, false); |
324 | 0 | if (this->GetGlobalGenerator()->IsGCCOnWindows()) { |
325 | 0 | std::replace(includeFlags.begin(), includeFlags.end(), '\\', '/'); |
326 | 0 | } |
327 | |
|
328 | 0 | this->LocalGenerator->AppendFlags(languageFlags, includeFlags); |
329 | 0 | } |
330 | | |
331 | | // TODO: Refactor with |
332 | | // void cmMakefileTargetGenerator::WriteTargetLanguageFlags(). |
333 | | std::string cmNinjaTargetGenerator::ComputeDefines(cmSourceFile const* source, |
334 | | std::string const& language, |
335 | | std::string const& config) |
336 | 0 | { |
337 | 0 | std::set<std::string> defines; |
338 | 0 | cmGeneratorExpressionInterpreter genexInterpreter( |
339 | 0 | this->LocalGenerator, config, this->GeneratorTarget, language); |
340 | | |
341 | | // Seriously?? |
342 | 0 | if (this->GetGlobalGenerator()->IsMultiConfig()) { |
343 | 0 | defines.insert(cmStrCat("CMAKE_INTDIR=\"", config, '"')); |
344 | 0 | } |
345 | |
|
346 | 0 | std::string const COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); |
347 | 0 | if (cmValue compile_defs = source->GetProperty(COMPILE_DEFINITIONS)) { |
348 | 0 | this->LocalGenerator->AppendDefines( |
349 | 0 | defines, genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS)); |
350 | 0 | } |
351 | |
|
352 | 0 | std::string defPropName = |
353 | 0 | cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config)); |
354 | 0 | if (cmValue config_compile_defs = source->GetProperty(defPropName)) { |
355 | 0 | this->LocalGenerator->AppendDefines( |
356 | 0 | defines, |
357 | 0 | genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS)); |
358 | 0 | } |
359 | |
|
360 | 0 | if (auto const* fileSet = |
361 | 0 | this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( |
362 | 0 | config, source)) { |
363 | 0 | auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget) |
364 | 0 | ? fileSet->GetCompileDefinitions(config, language) |
365 | 0 | : fileSet->GetInterfaceCompileDefinitions(config, language); |
366 | 0 | if (!fsDefines.empty()) { |
367 | 0 | this->LocalGenerator->AppendDefines(defines, fsDefines); |
368 | 0 | } |
369 | 0 | } |
370 | |
|
371 | 0 | std::string definesString = this->GetDefines(language, config); |
372 | 0 | this->LocalGenerator->JoinDefines(defines, definesString, language); |
373 | |
|
374 | 0 | return definesString; |
375 | 0 | } |
376 | | |
377 | | std::string cmNinjaTargetGenerator::ComputeIncludes( |
378 | | cmSourceFile const* source, std::string const& language, |
379 | | std::string const& config) |
380 | 0 | { |
381 | 0 | std::vector<std::string> includes; |
382 | |
|
383 | 0 | if (auto const* fileSet = |
384 | 0 | this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( |
385 | 0 | config, source)) { |
386 | 0 | auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget) |
387 | 0 | ? fileSet->GetIncludeDirectories(config, language) |
388 | 0 | : fileSet->GetInterfaceIncludeDirectories(config, language); |
389 | 0 | if (!fsIncludes.empty()) { |
390 | 0 | this->LocalGenerator->AppendIncludeDirectories( |
391 | 0 | includes, cm::remove_BT(fsIncludes), *source); |
392 | 0 | } |
393 | 0 | } |
394 | |
|
395 | 0 | cmGeneratorExpressionInterpreter genexInterpreter( |
396 | 0 | this->LocalGenerator, config, this->GeneratorTarget, language); |
397 | |
|
398 | 0 | std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); |
399 | 0 | if (cmValue cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) { |
400 | 0 | this->LocalGenerator->AppendIncludeDirectories( |
401 | 0 | includes, genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES), |
402 | 0 | *source); |
403 | 0 | } |
404 | |
|
405 | 0 | std::string includesString = this->LocalGenerator->GetIncludeFlags( |
406 | 0 | includes, this->GeneratorTarget, language, config, false); |
407 | 0 | this->LocalGenerator->AppendFlags(includesString, |
408 | 0 | this->GetIncludes(language, config)); |
409 | |
|
410 | 0 | return includesString; |
411 | 0 | } |
412 | | |
413 | | cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps( |
414 | | std::string const& linkLanguage, std::string const& config, |
415 | | bool ignoreType) const |
416 | 0 | { |
417 | | // Static libraries never depend on other targets for linking. |
418 | 0 | if (!ignoreType && |
419 | 0 | (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY || |
420 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::OBJECT_LIBRARY)) { |
421 | 0 | return cmNinjaDeps(); |
422 | 0 | } |
423 | | |
424 | 0 | cmComputeLinkInformation* cli = |
425 | 0 | this->GeneratorTarget->GetLinkInformation(config); |
426 | 0 | if (!cli) { |
427 | 0 | return cmNinjaDeps(); |
428 | 0 | } |
429 | | |
430 | 0 | std::vector<std::string> const& deps = cli->GetDepends(); |
431 | 0 | cmNinjaDeps result(deps.size()); |
432 | 0 | std::transform(deps.begin(), deps.end(), result.begin(), |
433 | 0 | this->MapToNinjaPath()); |
434 | | |
435 | | // Add a dependency on the link definitions file, if any. |
436 | 0 | if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi = |
437 | 0 | this->GeneratorTarget->GetModuleDefinitionInfo(config)) { |
438 | 0 | for (cmSourceFile const* src : mdi->Sources) { |
439 | 0 | result.push_back(this->ConvertToNinjaPath(src->GetFullPath())); |
440 | 0 | } |
441 | 0 | } |
442 | | |
443 | | // Add a dependency on user-specified manifest files, if any. |
444 | 0 | std::vector<cmSourceFile const*> manifest_srcs; |
445 | 0 | this->GeneratorTarget->GetManifests(manifest_srcs, config); |
446 | 0 | for (cmSourceFile const* manifest_src : manifest_srcs) { |
447 | 0 | result.push_back(this->ConvertToNinjaPath(manifest_src->GetFullPath())); |
448 | 0 | } |
449 | | |
450 | | // Add user-specified dependencies. |
451 | 0 | std::vector<std::string> linkDeps; |
452 | 0 | this->GeneratorTarget->GetLinkDepends(linkDeps, config, linkLanguage); |
453 | 0 | std::transform(linkDeps.begin(), linkDeps.end(), std::back_inserter(result), |
454 | 0 | this->MapToNinjaPath()); |
455 | |
|
456 | 0 | return result; |
457 | 0 | } |
458 | | |
459 | | std::string cmNinjaTargetGenerator::GetCompiledSourceNinjaPath( |
460 | | cmSourceFile const* source) const |
461 | 0 | { |
462 | | // Pass source files to the compiler by absolute path. |
463 | 0 | return this->ConvertToNinjaAbsPath(source->GetFullPath()); |
464 | 0 | } |
465 | | |
466 | | std::string cmNinjaTargetGenerator::GetObjectFileDir( |
467 | | std::string const& config) const |
468 | 0 | { |
469 | 0 | return this->LocalGenerator->MaybeRelativeToTopBinDir( |
470 | 0 | cmStrCat(this->GeneratorTarget->GetSupportDirectory(), |
471 | 0 | this->GetGlobalGenerator()->GetConfigDirectory(config))); |
472 | 0 | } |
473 | | |
474 | | std::string cmNinjaTargetGenerator::GetObjectFilePath( |
475 | | cmSourceFile const* source, std::string const& config) const |
476 | 0 | { |
477 | 0 | std::string const& objectName = this->GeneratorTarget->GetObjectName(source); |
478 | 0 | return cmStrCat(this->GetObjectFileDir(config), '/', objectName); |
479 | 0 | } |
480 | | |
481 | | std::string cmNinjaTargetGenerator::GetBmiFilePath( |
482 | | cmSourceFile const* source, std::string const& config) const |
483 | 0 | { |
484 | 0 | auto& importedConfigInfo = this->Configs.at(config).ImportedCxxModules; |
485 | 0 | if (!importedConfigInfo.Initialized()) { |
486 | 0 | std::string configUpper = cmSystemTools::UpperCase(config); |
487 | 0 | std::string propName = cmStrCat("IMPORTED_CXX_MODULES_", configUpper); |
488 | 0 | auto value = this->GeneratorTarget->GetSafeProperty(propName); |
489 | 0 | importedConfigInfo.Initialize(value); |
490 | 0 | } |
491 | |
|
492 | 0 | std::string bmiName = |
493 | 0 | importedConfigInfo.BmiNameForSource(source->GetFullPath()); |
494 | |
|
495 | 0 | return cmStrCat(this->GetObjectFileDir(config), '/', bmiName); |
496 | 0 | } |
497 | | |
498 | | std::string cmNinjaTargetGenerator::GetClangTidyReplacementsFilePath( |
499 | | std::string const& directory, cmSourceFile const& source, |
500 | | std::string const& config) const |
501 | 0 | { |
502 | 0 | auto const& objectName = this->GeneratorTarget->GetObjectName(&source); |
503 | 0 | return cmStrCat(directory, '/', |
504 | 0 | this->LocalGenerator->CreateSafeObjectFileName( |
505 | 0 | this->GetObjectFileDir(config)), |
506 | 0 | '/', objectName, ".yaml"); |
507 | 0 | } |
508 | | |
509 | | std::string cmNinjaTargetGenerator::GetPreprocessedFilePath( |
510 | | cmSourceFile const* source, std::string const& config) const |
511 | 0 | { |
512 | | // Choose an extension to compile already-preprocessed source. |
513 | 0 | std::string ppExt = source->GetExtension(); |
514 | 0 | if (cmHasPrefix(ppExt, 'F')) { |
515 | | // Some Fortran compilers automatically enable preprocessing for |
516 | | // upper-case extensions. Since the source is already preprocessed, |
517 | | // use a lower-case extension. |
518 | 0 | ppExt = cmSystemTools::LowerCase(ppExt); |
519 | 0 | } |
520 | 0 | if (ppExt == "fpp") { |
521 | | // Some Fortran compilers automatically enable preprocessing for |
522 | | // the ".fpp" extension. Since the source is already preprocessed, |
523 | | // use the ".f" extension. |
524 | 0 | ppExt = "f"; |
525 | 0 | } |
526 | | |
527 | | // Take the object file name and replace the extension. |
528 | 0 | std::string const& objName = this->GeneratorTarget->GetObjectName(source); |
529 | 0 | std::string const& objExt = |
530 | 0 | this->GetGlobalGenerator()->GetLanguageOutputExtension(*source); |
531 | 0 | assert(objName.size() >= objExt.size()); |
532 | 0 | std::string const ppName = |
533 | 0 | cmStrCat(objName.substr(0, objName.size() - objExt.size()), "-pp.", ppExt); |
534 | |
|
535 | 0 | return cmStrCat(this->GetObjectFileDir(config), '/', ppName); |
536 | 0 | } |
537 | | |
538 | | std::string cmNinjaTargetGenerator::GetDyndepFilePath( |
539 | | std::string const& lang, std::string const& config) const |
540 | 0 | { |
541 | 0 | return cmStrCat(this->GetObjectFileDir(config), '/', lang, ".dd"); |
542 | 0 | } |
543 | | |
544 | | std::string cmNinjaTargetGenerator::GetTargetDependInfoPath( |
545 | | std::string const& lang, std::string const& config) const |
546 | 0 | { |
547 | 0 | return cmStrCat(this->GeneratorTarget->GetSupportDirectory(), |
548 | 0 | this->GetGlobalGenerator()->ConfigDirectory(config), '/', |
549 | 0 | lang, "DependInfo.json"); |
550 | 0 | } |
551 | | |
552 | | std::string cmNinjaTargetGenerator::GetTargetOutputDir( |
553 | | std::string const& config) const |
554 | 0 | { |
555 | 0 | std::string dir = this->GeneratorTarget->GetDirectory(config); |
556 | 0 | return this->ConvertToNinjaPath(dir); |
557 | 0 | } |
558 | | |
559 | | std::string cmNinjaTargetGenerator::GetTargetFilePath( |
560 | | std::string const& name, std::string const& config) const |
561 | 0 | { |
562 | 0 | std::string path = this->GetTargetOutputDir(config); |
563 | 0 | if (path.empty() || path == ".") { |
564 | 0 | return name; |
565 | 0 | } |
566 | 0 | path += cmStrCat('/', name); |
567 | 0 | return path; |
568 | 0 | } |
569 | | |
570 | | std::string cmNinjaTargetGenerator::GetTargetName() const |
571 | 0 | { |
572 | 0 | return this->GeneratorTarget->GetName(); |
573 | 0 | } |
574 | | |
575 | | std::string cmNinjaTargetGenerator::ConvertToOutputFormatForShell( |
576 | | cm::string_view path) const |
577 | 0 | { |
578 | 0 | return this->LocalGenerator->ConvertToOutputFormat(path, |
579 | 0 | cmOutputConverter::SHELL); |
580 | 0 | } |
581 | | |
582 | | bool cmNinjaTargetGenerator::SetMsvcTargetPdbVariable( |
583 | | cmNinjaVars& vars, std::string const& config) const |
584 | 0 | { |
585 | 0 | cmMakefile* mf = this->GetMakefile(); |
586 | 0 | bool supportsPDB = mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") || |
587 | 0 | mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID") || |
588 | 0 | mf->GetDefinition("MSVC_CUDA_ARCHITECTURE_ID"); |
589 | 0 | if (!supportsPDB) { |
590 | 0 | std::string const linkLanguage = |
591 | 0 | this->GeneratorTarget->GetLinkerLanguage(config); |
592 | 0 | supportsPDB = |
593 | 0 | mf->IsOn(cmStrCat("CMAKE_", linkLanguage, "_LINKER_SUPPORTS_PDB")); |
594 | 0 | } |
595 | 0 | if (supportsPDB) { |
596 | 0 | std::string pdbPath; |
597 | 0 | std::string compilePdbPath = this->ComputeTargetCompilePDB(config); |
598 | 0 | if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE || |
599 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY || |
600 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY || |
601 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) { |
602 | 0 | pdbPath = cmStrCat(this->GeneratorTarget->GetPDBDirectory(config), '/', |
603 | 0 | this->GeneratorTarget->GetPDBName(config)); |
604 | 0 | } |
605 | |
|
606 | 0 | vars["TARGET_PDB"] = |
607 | 0 | this->ConvertToOutputFormatForShell(this->ConvertToNinjaPath(pdbPath)); |
608 | 0 | vars["TARGET_COMPILE_PDB"] = this->ConvertToOutputFormatForShell( |
609 | 0 | this->ConvertToNinjaPath(compilePdbPath)); |
610 | |
|
611 | 0 | this->EnsureParentDirectoryExists(pdbPath); |
612 | 0 | this->EnsureParentDirectoryExists(compilePdbPath); |
613 | 0 | return true; |
614 | 0 | } |
615 | 0 | return false; |
616 | 0 | } |
617 | | |
618 | | void cmNinjaTargetGenerator::WriteLanguageRules(std::string const& language, |
619 | | std::string const& config) |
620 | 0 | { |
621 | | #ifdef NINJA_GEN_VERBOSE_FILES |
622 | | this->GetRulesFileStream() << "# Rules for language " << language << "\n\n"; |
623 | | #endif |
624 | 0 | this->WriteCompileRule(language, config); |
625 | 0 | } |
626 | | |
627 | | namespace { |
628 | | // Create the command to run the dependency scanner |
629 | | std::string GetScanCommand( |
630 | | cm::string_view cmakeCmd, cm::string_view tdi, cm::string_view lang, |
631 | | cm::string_view srcFile, cm::string_view ddiFile, |
632 | | cm::optional<cm::string_view> srcOrigFile = cm::nullopt) |
633 | 0 | { |
634 | 0 | return cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi, |
635 | 0 | " --lang=", lang, " --src=", srcFile, |
636 | 0 | " --out=$out" |
637 | 0 | " --dep=$DEP_FILE --obj=$OBJ_FILE --ddi=", |
638 | 0 | ddiFile, |
639 | 0 | srcOrigFile ? cmStrCat(" --src-orig=", *srcOrigFile) : ""); |
640 | 0 | } |
641 | | |
642 | | // Helper function to create dependency scanning rule that may or may |
643 | | // not perform explicit preprocessing too. |
644 | | cmNinjaRule GetScanRule( |
645 | | std::string const& ruleName, std::string const& ppFileName, |
646 | | std::string const& deptype, |
647 | | cmRulePlaceholderExpander::RuleVariables const& vars, |
648 | | std::string const& responseFlag, std::string const& flags, |
649 | | cmRulePlaceholderExpander* const rulePlaceholderExpander, |
650 | | cmLocalNinjaGenerator* generator, std::vector<std::string> scanCmds, |
651 | | std::string const& outputConfig) |
652 | 0 | { |
653 | 0 | cmNinjaRule rule(ruleName); |
654 | | // Scanning always uses a depfile for preprocessor dependencies. |
655 | 0 | if (deptype == "msvc"_s) { |
656 | 0 | rule.DepType = deptype; |
657 | 0 | rule.DepFile.clear(); |
658 | 0 | } else { |
659 | 0 | rule.DepType.clear(); // no deps= for multiple outputs |
660 | 0 | rule.DepFile = "$DEP_FILE"; |
661 | 0 | } |
662 | |
|
663 | 0 | cmRulePlaceholderExpander::RuleVariables scanVars; |
664 | 0 | scanVars.CMTargetName = vars.CMTargetName; |
665 | 0 | scanVars.CMTargetType = vars.CMTargetType; |
666 | 0 | scanVars.Language = vars.Language; |
667 | 0 | scanVars.Object = "$OBJ_FILE"; |
668 | 0 | scanVars.PreprocessedSource = ppFileName.c_str(); |
669 | 0 | scanVars.DynDepFile = "$DYNDEP_INTERMEDIATE_FILE"; |
670 | 0 | scanVars.DependencyFile = rule.DepFile.c_str(); |
671 | 0 | scanVars.DependencyTarget = "$out"; |
672 | 0 | scanVars.Config = vars.Config; |
673 | | |
674 | | // Scanning needs the same preprocessor settings as direct compilation would. |
675 | 0 | scanVars.Source = vars.Source; |
676 | 0 | scanVars.Defines = vars.Defines; |
677 | 0 | scanVars.Includes = vars.Includes; |
678 | | |
679 | | // Scanning needs the compilation flags too. |
680 | 0 | std::string scanFlags = flags; |
681 | | |
682 | | // If using a response file, move defines, includes, and flags into it. |
683 | 0 | if (!responseFlag.empty()) { |
684 | 0 | rule.RspFile = "$RSP_FILE"; |
685 | 0 | rule.RspContent = |
686 | 0 | cmStrCat(' ', scanVars.Defines, ' ', scanVars.Includes, ' ', scanFlags); |
687 | 0 | scanFlags = cmStrCat(responseFlag, rule.RspFile); |
688 | 0 | scanVars.Defines = ""; |
689 | 0 | scanVars.Includes = ""; |
690 | 0 | } |
691 | |
|
692 | 0 | scanVars.Flags = scanFlags.c_str(); |
693 | | |
694 | | // Rule for scanning a source file. |
695 | 0 | for (std::string& scanCmd : scanCmds) { |
696 | 0 | rulePlaceholderExpander->ExpandRuleVariables(generator, scanCmd, scanVars); |
697 | 0 | } |
698 | 0 | rule.Command = |
699 | 0 | generator->BuildCommandLine(scanCmds, outputConfig, outputConfig); |
700 | |
|
701 | 0 | return rule; |
702 | 0 | } |
703 | | |
704 | | void SetupResponseFile(cmNinjaRule& rule, |
705 | | cmRulePlaceholderExpander::RuleVariables& vars, |
706 | | std::string& flags, std::string const& lang, |
707 | | std::string const& responseFlag) |
708 | 0 | { |
709 | 0 | rule.RspFile = "$RSP_FILE"; |
710 | 0 | rule.RspContent = |
711 | 0 | cmStrCat(' ', vars.Defines, ' ', vars.Includes, ' ', flags); |
712 | 0 | flags = cmStrCat(responseFlag, rule.RspFile); |
713 | 0 | vars.Defines = ""; |
714 | 0 | vars.Includes = ""; |
715 | | // Swift consumes all source files in a module at once, which reaches |
716 | | // command line length limits pretty quickly. Inject source files into the |
717 | | // response file in this case as well. |
718 | 0 | if (lang == "Swift") { |
719 | 0 | rule.RspContent = cmStrCat(rule.RspContent, ' ', vars.Source); |
720 | 0 | vars.Source = ""; |
721 | 0 | } |
722 | 0 | } |
723 | | |
724 | | cmList ExpandRuleCommands(std::string const& command, |
725 | | cmRulePlaceholderExpander::RuleVariables const& vars, |
726 | | cmMakefile const* mf, std::string const& lang, |
727 | | std::string const& launcher, |
728 | | cmLocalGenerator* localGenerator, |
729 | | cmRulePlaceholderExpander* rulePlaceholderExpander) |
730 | 0 | { |
731 | 0 | std::string const extraCommands = |
732 | 0 | mf->GetSafeDefinition(cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS")); |
733 | 0 | cmList commands(command); |
734 | 0 | if (!commands.empty()) { |
735 | 0 | commands.front().insert(0, "${CODE_CHECK}"); |
736 | 0 | commands.front().insert(0, "${LAUNCHER}"); |
737 | 0 | } |
738 | 0 | if (!extraCommands.empty()) { |
739 | 0 | commands.append(extraCommands); |
740 | 0 | } |
741 | 0 | for (std::string& cmd : commands) { |
742 | 0 | cmd = cmStrCat(launcher, cmd); |
743 | 0 | rulePlaceholderExpander->ExpandRuleVariables(localGenerator, cmd, vars); |
744 | 0 | } |
745 | 0 | return commands; |
746 | 0 | } |
747 | | } |
748 | | |
749 | | void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang, |
750 | | std::string const& config) |
751 | 0 | { |
752 | | // For some cases we scan to dynamically discover dependencies. |
753 | 0 | bool const needDyndep = this->GetGeneratorTarget()->NeedDyndep(lang, config); |
754 | |
|
755 | 0 | if (needDyndep) { |
756 | 0 | this->WriteCompileRule(lang, config, WithScanning::Yes); |
757 | 0 | } |
758 | 0 | this->WriteCompileRule(lang, config, WithScanning::No); |
759 | 0 | } |
760 | | |
761 | | std::string cmNinjaTargetGenerator::GetCompileTemplateVar( |
762 | | std::string const& lang) const |
763 | 0 | { |
764 | 0 | std::string cmdVar = cmStrCat("CMAKE_", lang, "_COMPILE_OBJECT"); |
765 | 0 | if (!this->GetGeneratorTarget()->IsNormal()) { |
766 | 0 | std::string bmiCmdVar = cmStrCat("CMAKE_", lang, "_COMPILE_BMI"); |
767 | 0 | if (!this->GetMakefile()->GetDefinition(bmiCmdVar).IsEmpty()) { |
768 | 0 | cmdVar = std::move(bmiCmdVar); |
769 | 0 | } |
770 | 0 | } |
771 | 0 | return cmdVar; |
772 | 0 | } |
773 | | |
774 | | void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang, |
775 | | std::string const& config, |
776 | | WithScanning withScanning) |
777 | 0 | { |
778 | 0 | cmRulePlaceholderExpander::RuleVariables vars; |
779 | 0 | vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); |
780 | 0 | vars.CMTargetType = |
781 | 0 | cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str(); |
782 | 0 | vars.Language = lang.c_str(); |
783 | 0 | vars.Source = "$in"; |
784 | 0 | vars.Object = "$out"; |
785 | 0 | vars.Defines = "$DEFINES"; |
786 | 0 | vars.Includes = "$INCLUDES"; |
787 | 0 | vars.TargetPDB = "$TARGET_PDB"; |
788 | 0 | vars.TargetCompilePDB = "$TARGET_COMPILE_PDB"; |
789 | 0 | vars.ObjectDir = "$OBJECT_DIR"; |
790 | 0 | vars.TargetSupportDir = "$TARGET_SUPPORT_DIR"; |
791 | 0 | vars.ObjectFileDir = "$OBJECT_FILE_DIR"; |
792 | 0 | vars.CudaCompileMode = "$CUDA_COMPILE_MODE"; |
793 | 0 | vars.ISPCHeader = "$ISPC_HEADER_FILE"; |
794 | 0 | vars.Config = "$CONFIG"; |
795 | 0 | vars.RustEmit = "$RUST_EMIT"; |
796 | |
|
797 | 0 | cmMakefile* mf = this->GetMakefile(); |
798 | | |
799 | | // For some cases we scan to dynamically discover dependencies. |
800 | 0 | bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(lang); |
801 | |
|
802 | 0 | std::string flags = "$FLAGS"; |
803 | |
|
804 | 0 | std::string responseFlag; |
805 | 0 | bool const lang_supports_response = lang != "RC"; |
806 | 0 | if (lang_supports_response && this->ForceResponseFile()) { |
807 | 0 | std::string const responseFlagVar = |
808 | 0 | cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_FLAG"); |
809 | 0 | responseFlag = this->Makefile->GetSafeDefinition(responseFlagVar); |
810 | 0 | if (responseFlag.empty() && lang != "CUDA") { |
811 | 0 | responseFlag = "@"; |
812 | 0 | } |
813 | 0 | } |
814 | 0 | std::string const modmapFormatVar = |
815 | 0 | cmStrCat("CMAKE_", lang, "_MODULE_MAP_FORMAT"); |
816 | 0 | std::string const modmapFormat = |
817 | 0 | this->Makefile->GetSafeDefinition(modmapFormatVar); |
818 | |
|
819 | 0 | auto rulePlaceholderExpander = |
820 | 0 | this->GetLocalGenerator()->CreateRulePlaceholderExpander(); |
821 | |
|
822 | 0 | std::string const tdi = this->ConvertToOutputFormatForShell( |
823 | 0 | this->ConvertToNinjaPath(this->GetTargetDependInfoPath(lang, config))); |
824 | |
|
825 | 0 | std::string launcher; |
826 | 0 | std::string val = this->GetLocalGenerator()->GetRuleLauncher( |
827 | 0 | this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", config); |
828 | 0 | if (cmNonempty(val)) { |
829 | 0 | launcher = cmStrCat(val, ' '); |
830 | 0 | } |
831 | |
|
832 | 0 | std::string const cmakeCmd = |
833 | 0 | this->ConvertToOutputFormatForShell(cmSystemTools::GetCMakeCommand()); |
834 | |
|
835 | 0 | if (withScanning == WithScanning::Yes) { |
836 | 0 | auto const& scanDepType = this->GetMakefile()->GetSafeDefinition( |
837 | 0 | cmStrCat("CMAKE_", lang, "_SCANDEP_DEPFILE_FORMAT")); |
838 | | |
839 | | // Rule to scan dependencies of sources that need preprocessing. |
840 | 0 | { |
841 | 0 | cmList scanCommands; |
842 | 0 | std::string scanRuleName; |
843 | 0 | std::string ppFileName; |
844 | 0 | if (compilationPreprocesses) { |
845 | 0 | scanRuleName = this->LanguageScanRule(lang, config); |
846 | 0 | ppFileName = "$PREPROCESSED_OUTPUT_FILE"; |
847 | 0 | std::string const& scanCommand = mf->GetRequiredDefinition( |
848 | 0 | cmStrCat("CMAKE_", lang, "_SCANDEP_SOURCE")); |
849 | 0 | scanCommands.assign(scanCommand); |
850 | 0 | for (auto& i : scanCommands) { |
851 | 0 | i = cmStrCat(launcher, i); |
852 | 0 | } |
853 | 0 | } else { |
854 | 0 | scanRuleName = this->LanguagePreprocessAndScanRule(lang, config); |
855 | 0 | ppFileName = "$out"; |
856 | 0 | std::string const& ppCommand = mf->GetRequiredDefinition( |
857 | 0 | cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE")); |
858 | 0 | scanCommands.assign(ppCommand); |
859 | 0 | for (auto& i : scanCommands) { |
860 | 0 | i = cmStrCat(launcher, i); |
861 | 0 | } |
862 | 0 | scanCommands.emplace_back(GetScanCommand( |
863 | 0 | cmakeCmd, tdi, lang, "$out", "$DYNDEP_INTERMEDIATE_FILE", "$in")); |
864 | 0 | } |
865 | |
|
866 | 0 | auto scanRule = GetScanRule( |
867 | 0 | scanRuleName, ppFileName, scanDepType, vars, responseFlag, flags, |
868 | 0 | rulePlaceholderExpander.get(), this->GetLocalGenerator(), |
869 | 0 | std::move(scanCommands), config); |
870 | |
|
871 | 0 | scanRule.Comment = |
872 | 0 | cmStrCat("Rule for generating ", lang, " dependencies."); |
873 | 0 | if (compilationPreprocesses) { |
874 | 0 | scanRule.Description = |
875 | 0 | cmStrCat("Scanning $in for ", lang, " dependencies"); |
876 | 0 | } else { |
877 | 0 | scanRule.Description = |
878 | 0 | cmStrCat("Building ", lang, " preprocessed $out"); |
879 | 0 | } |
880 | |
|
881 | 0 | this->GetGlobalGenerator()->AddRule(scanRule); |
882 | 0 | } |
883 | |
|
884 | 0 | if (!compilationPreprocesses) { |
885 | | // Compilation will not preprocess, so it does not need the defines |
886 | | // unless the compiler wants them for some other purpose. |
887 | 0 | if (!this->CompileWithDefines(lang)) { |
888 | 0 | vars.Defines = ""; |
889 | 0 | } |
890 | | |
891 | | // Rule to scan dependencies of sources that do not need preprocessing. |
892 | 0 | std::string const& scanRuleName = this->LanguageScanRule(lang, config); |
893 | 0 | std::vector<std::string> scanCommands; |
894 | 0 | scanCommands.emplace_back( |
895 | 0 | GetScanCommand(cmakeCmd, tdi, lang, "$in", "$out")); |
896 | |
|
897 | 0 | auto scanRule = |
898 | 0 | GetScanRule(scanRuleName, "", scanDepType, vars, "", flags, |
899 | 0 | rulePlaceholderExpander.get(), this->GetLocalGenerator(), |
900 | 0 | std::move(scanCommands), config); |
901 | | |
902 | | // Write the rule for generating dependencies for the given language. |
903 | 0 | scanRule.Comment = cmStrCat("Rule for generating ", lang, |
904 | 0 | " dependencies on non-preprocessed files."); |
905 | 0 | scanRule.Description = |
906 | 0 | cmStrCat("Generating ", lang, " dependencies for $in"); |
907 | |
|
908 | 0 | this->GetGlobalGenerator()->AddRule(scanRule); |
909 | 0 | } |
910 | | |
911 | | // Write the rule for ninja dyndep file generation. |
912 | 0 | cmNinjaRule rule(this->LanguageDyndepRule(lang, config)); |
913 | | // Command line length is almost always limited -> use response file for |
914 | | // dyndep rules |
915 | 0 | rule.RspFile = "$out.rsp"; |
916 | 0 | rule.RspContent = "$in"; |
917 | | // Ninja's collator writes all outputs using `cmGeneratedFileStream`, so |
918 | | // they are only updated if contents actually change. Avoid running |
919 | | // dependent jobs if the contents don't change by telling `ninja` to check |
920 | | // the timestamp again. |
921 | 0 | rule.Restat = "1"; |
922 | | |
923 | | // Run CMake dependency scanner on the source file (using the preprocessed |
924 | | // source if that was performed). |
925 | 0 | std::string ddModmapArg; |
926 | 0 | if (!modmapFormat.empty()) { |
927 | 0 | ddModmapArg += cmStrCat(" --modmapfmt=", modmapFormat); |
928 | 0 | } |
929 | 0 | { |
930 | 0 | std::vector<std::string> ddCmds; |
931 | 0 | { |
932 | 0 | std::string ccmd = cmStrCat( |
933 | 0 | cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi, " --lang=", lang, |
934 | 0 | ddModmapArg, " --dd=$out @", rule.RspFile); |
935 | 0 | ddCmds.emplace_back(std::move(ccmd)); |
936 | 0 | } |
937 | 0 | rule.Command = |
938 | 0 | this->GetLocalGenerator()->BuildCommandLine(ddCmds, config, config); |
939 | 0 | } |
940 | 0 | rule.Comment = |
941 | 0 | cmStrCat("Rule to generate ninja dyndep files for ", lang, '.'); |
942 | 0 | rule.Description = cmStrCat("Generating ", lang, " dyndep file $out"); |
943 | 0 | this->GetGlobalGenerator()->AddRule(rule); |
944 | 0 | } |
945 | |
|
946 | 0 | cmNinjaRule rule(this->LanguageCompilerRule(lang, config, withScanning)); |
947 | | // If using a response file, move defines, includes, and flags into it. |
948 | 0 | if (!responseFlag.empty()) { |
949 | 0 | SetupResponseFile(rule, vars, flags, lang, responseFlag); |
950 | 0 | } |
951 | | |
952 | | // Tell ninja dependency format so all deps can be loaded into a database |
953 | 0 | std::string cldeps; |
954 | 0 | if (!compilationPreprocesses) { |
955 | | // The compiler will not do preprocessing, so it has no such dependencies. |
956 | 0 | } else if (mf->IsOn(cmStrCat("CMAKE_NINJA_CMCLDEPS_", lang))) { |
957 | | // For the MS resource compiler we need cmcldeps, but skip dependencies |
958 | | // for source-file try_compile cases because they are always fresh. |
959 | 0 | if (!mf->GetIsSourceFileTryCompile()) { |
960 | 0 | rule.DepType = "gcc"; |
961 | 0 | rule.DepFile = "$DEP_FILE"; |
962 | 0 | cmValue d = mf->GetDefinition("CMAKE_C_COMPILER"); |
963 | 0 | std::string const cl = |
964 | 0 | d ? *d : mf->GetSafeDefinition("CMAKE_CXX_COMPILER"); |
965 | 0 | std::string cmcldepsPath; |
966 | 0 | cmSystemTools::GetShortPath(cmSystemTools::GetCMClDepsCommand(), |
967 | 0 | cmcldepsPath); |
968 | 0 | cldeps = cmStrCat(cmcldepsPath, ' ', lang, ' ', vars.Source, |
969 | 0 | " $DEP_FILE $out \"", |
970 | 0 | mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"), |
971 | 0 | "\" \"", cl, "\" "); |
972 | 0 | } |
973 | 0 | } else { |
974 | 0 | auto const& depType = this->GetMakefile()->GetSafeDefinition( |
975 | 0 | cmStrCat("CMAKE_", lang, "_DEPFILE_FORMAT")); |
976 | 0 | if (depType == "msvc"_s) { |
977 | 0 | rule.DepType = "msvc"; |
978 | 0 | rule.DepFile.clear(); |
979 | 0 | } else { |
980 | 0 | rule.DepType = "gcc"; |
981 | 0 | rule.DepFile = "$DEP_FILE"; |
982 | 0 | } |
983 | 0 | vars.DependencyFile = rule.DepFile.c_str(); |
984 | 0 | vars.DependencyTarget = "$out"; |
985 | |
|
986 | 0 | std::string const flagsName = cmStrCat("CMAKE_DEPFILE_FLAGS_", lang); |
987 | 0 | std::string depfileFlags = mf->GetSafeDefinition(flagsName); |
988 | 0 | if (!depfileFlags.empty()) { |
989 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), |
990 | 0 | depfileFlags, vars); |
991 | 0 | flags += cmStrCat(' ', depfileFlags); |
992 | 0 | } |
993 | 0 | } |
994 | |
|
995 | 0 | if (withScanning == WithScanning::Yes && !modmapFormat.empty()) { |
996 | 0 | std::string modmapFlags = |
997 | 0 | mf->GetRequiredDefinition(cmStrCat("CMAKE_", lang, "_MODULE_MAP_FLAG")); |
998 | 0 | cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>", |
999 | 0 | "$DYNDEP_MODULE_MAP_FILE"); |
1000 | 0 | flags += cmStrCat(' ', modmapFlags); |
1001 | 0 | } |
1002 | |
|
1003 | 0 | vars.Flags = flags.c_str(); |
1004 | 0 | vars.DependencyFile = rule.DepFile.c_str(); |
1005 | |
|
1006 | 0 | std::string cudaCompileMode; |
1007 | 0 | if (lang == "CUDA") { |
1008 | 0 | if (this->GeneratorTarget->GetPropertyAsBool( |
1009 | 0 | "CUDA_SEPARABLE_COMPILATION")) { |
1010 | 0 | std::string const& rdcFlag = |
1011 | 0 | this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG"); |
1012 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, ' '); |
1013 | 0 | } |
1014 | 0 | static std::array<cm::string_view, 4> const compileModes{ |
1015 | 0 | { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s } |
1016 | 0 | }; |
1017 | 0 | bool useNormalCompileMode = true; |
1018 | 0 | for (cm::string_view mode : compileModes) { |
1019 | 0 | auto propName = cmStrCat("CUDA_", mode, "_COMPILATION"); |
1020 | 0 | auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG"); |
1021 | 0 | if (this->GeneratorTarget->GetPropertyAsBool(propName)) { |
1022 | 0 | std::string const& flag = |
1023 | 0 | this->Makefile->GetRequiredDefinition(defName); |
1024 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, flag); |
1025 | 0 | useNormalCompileMode = false; |
1026 | 0 | break; |
1027 | 0 | } |
1028 | 0 | } |
1029 | 0 | if (useNormalCompileMode) { |
1030 | 0 | std::string const& wholeFlag = |
1031 | 0 | this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG"); |
1032 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag); |
1033 | 0 | } |
1034 | 0 | vars.CudaCompileMode = cudaCompileMode.c_str(); |
1035 | 0 | } |
1036 | | |
1037 | | // Rule for compiling object file. |
1038 | 0 | std::string const cmdVar = this->GetCompileTemplateVar(lang); |
1039 | 0 | std::string const& compileCmd = mf->GetRequiredDefinition(cmdVar); |
1040 | 0 | cmList compileCmds = ExpandRuleCommands(compileCmd, vars, mf, lang, launcher, |
1041 | 0 | this->GetLocalGenerator(), |
1042 | 0 | rulePlaceholderExpander.get()); |
1043 | |
|
1044 | 0 | if (!compileCmds.empty()) { |
1045 | 0 | compileCmds.front().insert(0, cldeps); |
1046 | 0 | } |
1047 | |
|
1048 | 0 | rule.Command = |
1049 | 0 | this->GetLocalGenerator()->BuildCommandLine(compileCmds, config, config); |
1050 | | |
1051 | | // Write the rule for compiling file of the given language. |
1052 | 0 | rule.Comment = cmStrCat("Rule for compiling ", lang, " files."); |
1053 | 0 | rule.Description = cmStrCat("Building ", lang, " object $out"); |
1054 | 0 | this->GetGlobalGenerator()->AddRule(rule); |
1055 | | |
1056 | | // Write a separate emit-module rule for Swift (produces .swiftmodule |
1057 | | // without compile outputs, enabling downstream modules to compile in |
1058 | | // parallel with upstream compilation). |
1059 | 0 | if (lang == "Swift" && withScanning == WithScanning::No) { |
1060 | 0 | std::string const emitModCmdVar = "CMAKE_Swift_EMIT_MODULE"; |
1061 | 0 | cmValue emitModCmdVal = mf->GetDefinition(emitModCmdVar); |
1062 | 0 | if (emitModCmdVal) { |
1063 | 0 | cmNinjaRule emitModRule(this->LanguageEmitModuleRule(lang, config)); |
1064 | 0 | cmRulePlaceholderExpander::RuleVariables emVars = vars; |
1065 | 0 | std::string emFlags = "$FLAGS"; |
1066 | 0 | if (!responseFlag.empty()) { |
1067 | | // Reset placeholders after compile response-file setup. |
1068 | 0 | emVars.Source = "$in"; |
1069 | 0 | emVars.Object = "$out"; |
1070 | 0 | emVars.Defines = "$DEFINES"; |
1071 | 0 | emVars.Includes = "$INCLUDES"; |
1072 | 0 | SetupResponseFile(emitModRule, emVars, emFlags, lang, responseFlag); |
1073 | 0 | } |
1074 | |
|
1075 | 0 | emVars.Flags = emFlags.c_str(); |
1076 | 0 | emitModRule.Restat = "1"; |
1077 | |
|
1078 | 0 | cmList emitModCmds = ExpandRuleCommands( |
1079 | 0 | *emitModCmdVal, emVars, mf, lang, launcher, this->GetLocalGenerator(), |
1080 | 0 | rulePlaceholderExpander.get()); |
1081 | 0 | emitModRule.Command = this->GetLocalGenerator()->BuildCommandLine( |
1082 | 0 | emitModCmds, config, config); |
1083 | 0 | emitModRule.Comment = "Rule for emitting Swift .swiftmodule files."; |
1084 | 0 | emitModRule.Description = |
1085 | 0 | cmStrCat("Emitting Swift .swiftmodule ", "$out"); |
1086 | 0 | this->GetGlobalGenerator()->AddRule(emitModRule); |
1087 | 0 | } |
1088 | 0 | } |
1089 | 0 | } |
1090 | | |
1091 | | void cmNinjaTargetGenerator::WriteObjectBuildStatements( |
1092 | | std::string const& config, std::string const& fileConfig, |
1093 | | bool firstForConfig) |
1094 | 0 | { |
1095 | 0 | this->GeneratorTarget->CheckCxxModuleStatus(config); |
1096 | | |
1097 | | // Write comments. |
1098 | 0 | cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig)); |
1099 | 0 | this->GetImplFileStream(fileConfig) |
1100 | 0 | << "# Object build statements for " |
1101 | 0 | << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) |
1102 | 0 | << " target " << this->GetTargetName() << "\n\n"; |
1103 | |
|
1104 | 0 | std::vector<cmCustomCommand const*> customCommands; |
1105 | 0 | { |
1106 | 0 | std::vector<cmSourceFile const*> customCommandSources; |
1107 | 0 | this->GeneratorTarget->GetCustomCommands(customCommandSources, config); |
1108 | 0 | for (cmSourceFile const* sf : customCommandSources) { |
1109 | 0 | cmCustomCommand const* cc = sf->GetCustomCommand(); |
1110 | 0 | this->GetLocalGenerator()->AddCustomCommandTarget( |
1111 | 0 | cc, this->GetGeneratorTarget()); |
1112 | 0 | customCommands.push_back(cc); |
1113 | 0 | } |
1114 | 0 | } |
1115 | 0 | { |
1116 | 0 | std::vector<cmSourceFile const*> headerSources; |
1117 | 0 | this->GeneratorTarget->GetHeaderSources(headerSources, config); |
1118 | 0 | this->OSXBundleGenerator->GenerateMacOSXContentStatements( |
1119 | 0 | headerSources, this->Configs[fileConfig].MacOSXContentGenerator.get(), |
1120 | 0 | config); |
1121 | 0 | } |
1122 | 0 | { |
1123 | 0 | std::vector<cmSourceFile const*> extraSources; |
1124 | 0 | this->GeneratorTarget->GetExtraSources(extraSources, config); |
1125 | 0 | this->OSXBundleGenerator->GenerateMacOSXContentStatements( |
1126 | 0 | extraSources, this->Configs[fileConfig].MacOSXContentGenerator.get(), |
1127 | 0 | config); |
1128 | 0 | } |
1129 | 0 | if (firstForConfig) { |
1130 | 0 | cmValue pchExtension = |
1131 | 0 | this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION"); |
1132 | |
|
1133 | 0 | std::vector<cmSourceFile const*> externalObjects; |
1134 | 0 | this->GeneratorTarget->GetExternalObjects(externalObjects, config); |
1135 | 0 | for (cmSourceFile const* sf : externalObjects) { |
1136 | 0 | auto objectFileName = this->GetGlobalGenerator()->ExpandCFGIntDir( |
1137 | 0 | this->ConvertToNinjaPath(sf->GetFullPath()), config); |
1138 | 0 | if (!cmHasSuffix(objectFileName, pchExtension)) { |
1139 | 0 | this->Configs[config].Objects.push_back(objectFileName); |
1140 | 0 | } |
1141 | 0 | } |
1142 | 0 | } |
1143 | |
|
1144 | 0 | if (!this->GetGlobalGenerator()->SupportsCWDDepend()) { |
1145 | | // Ensure that the object directory exists. If there are no objects in the |
1146 | | // target (e.g., an empty `OBJECT` library), the directory is still listed |
1147 | | // as an order-only depends in the build files. Alternate `ninja` |
1148 | | // implementations may not allow this (such as `samu`). See #25526. |
1149 | 0 | auto const objectDir = this->GetObjectFileDir(config); |
1150 | 0 | this->EnsureDirectoryExists(objectDir); |
1151 | 0 | } |
1152 | |
|
1153 | 0 | { |
1154 | 0 | cmNinjaBuild build("phony"); |
1155 | 0 | build.Comment = |
1156 | 0 | cmStrCat("Order-only phony target for ", this->GetTargetName()); |
1157 | 0 | build.Outputs.push_back(this->OrderDependsTargetForTarget(config)); |
1158 | | |
1159 | | // Gather order-only dependencies on custom command outputs. |
1160 | 0 | std::vector<std::string> ccouts; |
1161 | 0 | std::vector<std::string> ccouts_private; |
1162 | 0 | bool usePrivateGeneratedSources = this->GeneratorTarget->HasFileSets() && |
1163 | 0 | this->GetGeneratorTarget()->GetPolicyStatusCMP0154() == cmPolicies::NEW; |
1164 | |
|
1165 | 0 | for (cmCustomCommand const* cc : customCommands) { |
1166 | 0 | cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator()); |
1167 | 0 | std::vector<std::string> const& ccoutputs = ccg.GetOutputs(); |
1168 | 0 | std::vector<std::string> const& ccbyproducts = ccg.GetByproducts(); |
1169 | 0 | auto const nPreviousOutputs = ccouts.size(); |
1170 | 0 | ccouts.insert(ccouts.end(), ccoutputs.begin(), ccoutputs.end()); |
1171 | 0 | ccouts.insert(ccouts.end(), ccbyproducts.begin(), ccbyproducts.end()); |
1172 | 0 | if (usePrivateGeneratedSources) { |
1173 | 0 | auto it = ccouts.begin(); |
1174 | | // Skip over outputs that were already detected. |
1175 | 0 | std::advance(it, nPreviousOutputs); |
1176 | 0 | while (it != ccouts.end()) { |
1177 | 0 | cmGeneratorFileSet const* fileset = |
1178 | 0 | this->GeneratorTarget->GetFileSetForSource( |
1179 | 0 | config, this->Makefile->GetOrCreateGeneratedSource(*it)); |
1180 | |
|
1181 | 0 | if (!fileset) { |
1182 | | // use private order dependency |
1183 | 0 | ccouts_private.push_back(*it); |
1184 | 0 | it = ccouts.erase(it); |
1185 | 0 | continue; |
1186 | 0 | } |
1187 | | |
1188 | 0 | using DependencyMode = cm::FileSetMetadata::DependencyMode; |
1189 | |
|
1190 | 0 | cmValue independentFiles = fileset->GetProperty("INDEPENDENT_FILES"); |
1191 | | // retrieve default mode |
1192 | 0 | DependencyMode dependencyMode = |
1193 | 0 | cm::FileSetMetadata::GetDependencyMode(fileset->GetType()); |
1194 | | // if property is defined, try to enforce mode requested |
1195 | 0 | if (independentFiles) { |
1196 | 0 | dependencyMode = cm::FileSetMetadata::GetDependencyMode( |
1197 | 0 | fileset->GetType(), |
1198 | 0 | independentFiles.IsOn() ? DependencyMode::IndependentFiles |
1199 | 0 | : DependencyMode::Includables); |
1200 | 0 | } |
1201 | 0 | if (independentFiles.IsOn() && |
1202 | 0 | dependencyMode != DependencyMode::IndependentFiles) { |
1203 | | // requested dependency mode not supported |
1204 | 0 | this->GetMakefile()->IssueDiagnostic( |
1205 | 0 | cmDiagnostics::CMD_AUTHOR, |
1206 | 0 | cmStrCat(R"(the "INDEPENDENT_FILES" property of the file set ")", |
1207 | 0 | fileset->GetName(), R"(" of the target ")", |
1208 | 0 | this->GeneratorTarget->GetName(), |
1209 | 0 | R"(" will be ignored because it is incompatible with )" |
1210 | 0 | R"(the file set type ")", |
1211 | 0 | fileset->GetType(), R"(".)")); |
1212 | 0 | } |
1213 | 0 | if (dependencyMode == DependencyMode::Includables) { |
1214 | 0 | if (fileset->IsForInterface()) { |
1215 | | // use public order dependency |
1216 | 0 | ++it; |
1217 | 0 | } else { |
1218 | | // use private order dependency |
1219 | 0 | ccouts_private.push_back(*it); |
1220 | 0 | it = ccouts.erase(it); |
1221 | 0 | } |
1222 | 0 | continue; |
1223 | 0 | } |
1224 | | // no order dependency is required |
1225 | 0 | it = ccouts.erase(it); |
1226 | 0 | } |
1227 | 0 | } |
1228 | 0 | } |
1229 | |
|
1230 | 0 | cmNinjaDeps& orderOnlyDeps = build.OrderOnlyDeps; |
1231 | 0 | this->GetLocalGenerator()->AppendTargetDepends( |
1232 | 0 | this->GeneratorTarget, orderOnlyDeps, config, fileConfig, |
1233 | 0 | DependOnTargetOrdering); |
1234 | | |
1235 | | // Add order-only dependencies on other files associated with the target. |
1236 | 0 | cm::append(orderOnlyDeps, this->Configs[config].ExtraFiles); |
1237 | | |
1238 | | // Add order-only dependencies on custom command outputs. |
1239 | 0 | std::transform(ccouts.begin(), ccouts.end(), |
1240 | 0 | std::back_inserter(orderOnlyDeps), this->MapToNinjaPath()); |
1241 | |
|
1242 | 0 | std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end()); |
1243 | 0 | orderOnlyDeps.erase( |
1244 | 0 | std::unique(orderOnlyDeps.begin(), orderOnlyDeps.end()), |
1245 | 0 | orderOnlyDeps.end()); |
1246 | | |
1247 | | // The phony target must depend on at least one input or ninja will explain |
1248 | | // that "output ... of phony edge with no inputs doesn't exist" and |
1249 | | // consider the phony output "dirty". |
1250 | 0 | if (orderOnlyDeps.empty()) { |
1251 | 0 | std::string tgtDir; |
1252 | 0 | if (this->GetGlobalGenerator()->SupportsCWDDepend()) { |
1253 | 0 | tgtDir = "."; |
1254 | 0 | } else { |
1255 | | // Any path that always exists will work here. |
1256 | 0 | tgtDir = this->GetObjectFileDir(config); |
1257 | 0 | } |
1258 | 0 | orderOnlyDeps.push_back(this->ConvertToNinjaPath(tgtDir)); |
1259 | 0 | } |
1260 | |
|
1261 | 0 | this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), |
1262 | 0 | build); |
1263 | | |
1264 | | // Add order-only dependencies on custom command outputs that are |
1265 | | // private to this target. |
1266 | 0 | this->HasPrivateGeneratedSources = !ccouts_private.empty(); |
1267 | 0 | if (this->HasPrivateGeneratedSources) { |
1268 | 0 | cmNinjaBuild buildPrivate("phony"); |
1269 | 0 | cmNinjaDeps& orderOnlyDepsPrivate = buildPrivate.OrderOnlyDeps; |
1270 | 0 | orderOnlyDepsPrivate.push_back( |
1271 | 0 | this->OrderDependsTargetForTarget(config)); |
1272 | |
|
1273 | 0 | buildPrivate.Outputs.push_back( |
1274 | 0 | this->OrderDependsTargetForTargetPrivate(config)); |
1275 | |
|
1276 | 0 | std::transform(ccouts_private.begin(), ccouts_private.end(), |
1277 | 0 | std::back_inserter(orderOnlyDepsPrivate), |
1278 | 0 | this->MapToNinjaPath()); |
1279 | |
|
1280 | 0 | std::sort(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end()); |
1281 | 0 | orderOnlyDepsPrivate.erase( |
1282 | 0 | std::unique(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end()), |
1283 | 0 | orderOnlyDepsPrivate.end()); |
1284 | |
|
1285 | 0 | this->GetGlobalGenerator()->WriteBuild( |
1286 | 0 | this->GetImplFileStream(fileConfig), buildPrivate); |
1287 | 0 | } |
1288 | 0 | } |
1289 | 0 | { |
1290 | 0 | std::vector<cmSourceFile const*> objectSources; |
1291 | 0 | this->GeneratorTarget->GetObjectSources(objectSources, config); |
1292 | |
|
1293 | 0 | std::vector<cmSourceFile const*> swiftSources; |
1294 | |
|
1295 | 0 | for (cmSourceFile const* sf : objectSources) { |
1296 | 0 | if (this->GetLocalGenerator()->IsSplitSwiftBuild() && |
1297 | 0 | sf->GetLanguage() == "Swift") { |
1298 | 0 | swiftSources.push_back(sf); |
1299 | 0 | } else { |
1300 | 0 | this->WriteObjectBuildStatement(sf, config, fileConfig, |
1301 | 0 | firstForConfig); |
1302 | 0 | } |
1303 | 0 | } |
1304 | 0 | WriteSwiftObjectBuildStatement(swiftSources, config, fileConfig, |
1305 | 0 | firstForConfig); |
1306 | 0 | } |
1307 | |
|
1308 | 0 | { |
1309 | 0 | std::vector<cmSourceFile const*> bmiOnlySources; |
1310 | 0 | this->GeneratorTarget->GetCxxModuleSources(bmiOnlySources, config); |
1311 | |
|
1312 | 0 | for (cmSourceFile const* sf : bmiOnlySources) { |
1313 | 0 | this->WriteCxxModuleBmiBuildStatement(sf, config, fileConfig, |
1314 | 0 | firstForConfig); |
1315 | 0 | } |
1316 | 0 | } |
1317 | | |
1318 | | // Detect sources in `CXX_MODULES` which are not compiled. |
1319 | 0 | { |
1320 | 0 | std::vector<cmSourceFile*> sources; |
1321 | 0 | this->GeneratorTarget->GetSourceFiles(sources, config); |
1322 | 0 | for (cmSourceFile const* sf : sources) { |
1323 | 0 | cmGeneratorFileSet const* fs = |
1324 | 0 | this->GeneratorTarget->GetFileSetForSource(config, sf); |
1325 | 0 | if (!fs) { |
1326 | 0 | continue; |
1327 | 0 | } |
1328 | 0 | if (fs->GetType() != cm::FileSetMetadata::CXX_MODULES) { |
1329 | 0 | continue; |
1330 | 0 | } |
1331 | 0 | if (sf->GetLanguage().empty()) { |
1332 | 0 | this->GeneratorTarget->Makefile->IssueMessage( |
1333 | 0 | MessageType::FATAL_ERROR, |
1334 | 0 | cmStrCat("Target \"", this->GeneratorTarget->GetName(), |
1335 | 0 | "\" has source file\n ", sf->GetFullPath(), |
1336 | 0 | "\nin a \"FILE_SET TYPE ", cm::FileSetMetadata::CXX_MODULES, |
1337 | 0 | "\" but it is not scheduled for compilation.")); |
1338 | 0 | } |
1339 | 0 | } |
1340 | 0 | } |
1341 | | |
1342 | | // Check if there are Fortran objects which need to participate in forwarding |
1343 | | // module requirements. |
1344 | 0 | if (this->GeneratorTarget->HaveFortranSources(config) && |
1345 | 0 | !this->Configs[config].ScanningInfo.count("Fortran")) { |
1346 | 0 | ScanningFiles files; |
1347 | 0 | this->Configs[config].ScanningInfo["Fortran"].emplace_back(files); |
1348 | 0 | this->WriteCompileRule("Fortran", config, WithScanning::Yes); |
1349 | 0 | } |
1350 | |
|
1351 | 0 | for (auto const& langScanningFiles : this->Configs[config].ScanningInfo) { |
1352 | 0 | std::string const& language = langScanningFiles.first; |
1353 | 0 | std::vector<ScanningFiles> const& scanningFiles = langScanningFiles.second; |
1354 | |
|
1355 | 0 | cmNinjaBuild build(this->LanguageDyndepRule(language, config)); |
1356 | 0 | build.Outputs.push_back(this->GetDyndepFilePath(language, config)); |
1357 | 0 | build.ImplicitOuts.emplace_back( |
1358 | 0 | cmStrCat(this->GetObjectFileDir(config), '/', language, "Modules.json")); |
1359 | 0 | build.ImplicitDeps.emplace_back( |
1360 | 0 | this->GetTargetDependInfoPath(language, config)); |
1361 | 0 | { |
1362 | 0 | auto bdb_path = |
1363 | 0 | this->GeneratorTarget->BuildDatabasePath(language, config); |
1364 | 0 | if (!bdb_path.empty()) { |
1365 | 0 | build.ImplicitOuts.emplace_back(this->ConvertToNinjaPath(bdb_path)); |
1366 | 0 | } |
1367 | 0 | } |
1368 | 0 | auto bdb_path = this->GeneratorTarget->BuildDatabasePath(language, config); |
1369 | 0 | if (!bdb_path.empty()) { |
1370 | 0 | auto db = cmBuildDatabase::ForTarget(this->GeneratorTarget, config); |
1371 | 0 | auto mcdb_template_path = cmStrCat(bdb_path, ".in"); |
1372 | 0 | db.Write(mcdb_template_path); |
1373 | 0 | build.ImplicitDeps.emplace_back(std::move(mcdb_template_path)); |
1374 | 0 | build.ImplicitOuts.emplace_back(std::move(bdb_path)); |
1375 | 0 | } |
1376 | 0 | for (auto const& scanFiles : scanningFiles) { |
1377 | 0 | if (!scanFiles.ScanningOutput.empty()) { |
1378 | 0 | build.ExplicitDeps.push_back(scanFiles.ScanningOutput); |
1379 | 0 | } |
1380 | 0 | if (!scanFiles.ModuleMapFile.empty()) { |
1381 | 0 | build.ImplicitOuts.push_back(scanFiles.ModuleMapFile); |
1382 | 0 | } |
1383 | 0 | } |
1384 | |
|
1385 | 0 | this->WriteTargetDependInfo(language, config); |
1386 | | |
1387 | | // Non-imported synthetic targets read module info from their native target |
1388 | | // Add as implicit dependency. |
1389 | 0 | if (this->GeneratorTarget->IsSynthetic()) { |
1390 | 0 | if (cmGeneratorTarget const* native_gt = |
1391 | 0 | this->LocalGenerator->FindGeneratorTargetToUse( |
1392 | 0 | this->GeneratorTarget->Target->GetTemplateName())) { |
1393 | 0 | if (!native_gt->IsImported()) { |
1394 | 0 | std::string native_dir = native_gt->GetSupportDirectory(); |
1395 | 0 | if (this->GetGlobalGenerator()->IsMultiConfig()) { |
1396 | 0 | native_dir = cmStrCat(native_dir, '/', config); |
1397 | 0 | } |
1398 | 0 | build.ImplicitDeps.emplace_back(this->ConvertToNinjaPath( |
1399 | 0 | cmStrCat(native_dir, '/', language, "Modules.json"))); |
1400 | 0 | } |
1401 | 0 | } |
1402 | 0 | } |
1403 | |
|
1404 | 0 | auto const linked_directories = |
1405 | 0 | this->GetLinkedTargetDirectories(language, config); |
1406 | 0 | for (std::string const& l : linked_directories.Direct) { |
1407 | 0 | build.ImplicitDeps.emplace_back( |
1408 | 0 | this->ConvertToNinjaPath(cmStrCat(l, '/', language, "Modules.json"))); |
1409 | 0 | } |
1410 | 0 | for (std::string const& l : linked_directories.Forward) { |
1411 | 0 | build.ImplicitDeps.emplace_back( |
1412 | 0 | this->ConvertToNinjaPath(cmStrCat(l, '/', language, "Modules.json"))); |
1413 | 0 | } |
1414 | |
|
1415 | 0 | this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), |
1416 | 0 | build); |
1417 | 0 | } |
1418 | |
|
1419 | 0 | this->GetImplFileStream(fileConfig) << '\n'; |
1420 | 0 | } |
1421 | | |
1422 | | void cmNinjaTargetGenerator::GenerateSwiftOutputFileMap( |
1423 | | std::string const& config, std::string& flags) |
1424 | 0 | { |
1425 | 0 | if (this->Configs[config].SwiftOutputMap.empty()) { |
1426 | 0 | return; |
1427 | 0 | } |
1428 | | |
1429 | 0 | std::string const targetSwiftDepsPath = [this, config]() -> std::string { |
1430 | 0 | cmGeneratorTarget const* target = this->GeneratorTarget; |
1431 | 0 | if (cmValue name = target->GetProperty("Swift_DEPENDENCIES_FILE")) { |
1432 | 0 | return *name; |
1433 | 0 | } |
1434 | 0 | return this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(), |
1435 | 0 | '/', config, '/', |
1436 | 0 | target->GetName(), ".swiftdeps")); |
1437 | 0 | }(); |
1438 | |
|
1439 | 0 | std::string mapFilePath = |
1440 | 0 | cmStrCat(this->GeneratorTarget->GetSupportDirectory(), '/', config, |
1441 | 0 | "/" |
1442 | 0 | "output-file-map.json"); |
1443 | | |
1444 | | // build the global target dependencies |
1445 | | // https://github.com/apple/swift/blob/master/docs/Driver.md#output-file-maps |
1446 | 0 | Json::Value deps(Json::objectValue); |
1447 | 0 | deps["swift-dependencies"] = targetSwiftDepsPath; |
1448 | 0 | this->Configs[config].SwiftOutputMap[""] = deps; |
1449 | |
|
1450 | 0 | cmGeneratedFileStream output(mapFilePath); |
1451 | 0 | output.SetCopyIfDifferent(true); |
1452 | 0 | output << this->Configs[config].SwiftOutputMap; |
1453 | | |
1454 | | // Add flag |
1455 | 0 | this->LocalGenerator->AppendFlags(flags, "-output-file-map"); |
1456 | 0 | this->LocalGenerator->AppendFlags( |
1457 | 0 | flags, |
1458 | 0 | this->ConvertToOutputFormatForShell(ConvertToNinjaPath(mapFilePath))); |
1459 | 0 | } |
1460 | | |
1461 | | namespace { |
1462 | | cmNinjaBuild GetScanBuildStatement(std::string const& ruleName, |
1463 | | std::string const& ppFileName, |
1464 | | bool compilePP, bool compilePPWithDefines, |
1465 | | bool compilationPreprocesses, |
1466 | | cmNinjaBuild& objBuild, cmNinjaVars& vars, |
1467 | | std::string const& objectFileName, |
1468 | | cmNinjaTargetGenerator* tg) |
1469 | 0 | { |
1470 | 0 | cmNinjaBuild scanBuild(ruleName); |
1471 | |
|
1472 | 0 | if (compilePP) { |
1473 | | // Move compilation dependencies to the scan/preprocessing build statement. |
1474 | 0 | std::swap(scanBuild.ExplicitDeps, objBuild.ExplicitDeps); |
1475 | 0 | std::swap(scanBuild.ImplicitDeps, objBuild.ImplicitDeps); |
1476 | 0 | std::swap(scanBuild.OrderOnlyDeps, objBuild.OrderOnlyDeps); |
1477 | 0 | std::swap(scanBuild.Variables["IN_ABS"], vars["IN_ABS"]); |
1478 | | |
1479 | | // The actual compilation will now use the preprocessed source. |
1480 | 0 | objBuild.ExplicitDeps.push_back(ppFileName); |
1481 | 0 | } else { |
1482 | | // Copy compilation dependencies to the scan/preprocessing build statement. |
1483 | 0 | scanBuild.ExplicitDeps = objBuild.ExplicitDeps; |
1484 | 0 | scanBuild.ImplicitDeps = objBuild.ImplicitDeps; |
1485 | 0 | scanBuild.OrderOnlyDeps = objBuild.OrderOnlyDeps; |
1486 | 0 | scanBuild.Variables["IN_ABS"] = vars["IN_ABS"]; |
1487 | 0 | } |
1488 | | |
1489 | | // Scanning and compilation generally use the same flags. |
1490 | 0 | scanBuild.Variables["FLAGS"] = vars["FLAGS"]; |
1491 | |
|
1492 | 0 | if (compilePP && !compilePPWithDefines) { |
1493 | | // Move preprocessor definitions to the scan/preprocessor build statement. |
1494 | 0 | std::swap(scanBuild.Variables["DEFINES"], vars["DEFINES"]); |
1495 | 0 | } else { |
1496 | | // Copy preprocessor definitions to the scan/preprocessor build statement. |
1497 | 0 | scanBuild.Variables["DEFINES"] = vars["DEFINES"]; |
1498 | 0 | } |
1499 | | |
1500 | | // Copy include directories to the preprocessor build statement. The |
1501 | | // Fortran compilation build statement still needs them for the INCLUDE |
1502 | | // directive. |
1503 | 0 | scanBuild.Variables["INCLUDES"] = vars["INCLUDES"]; |
1504 | | |
1505 | | // Tell dependency scanner the object file that will result from |
1506 | | // compiling the source. |
1507 | 0 | scanBuild.Variables["OBJ_FILE"] = |
1508 | 0 | tg->ConvertToOutputFormatForShell(objectFileName); |
1509 | | |
1510 | | // Tell dependency scanner where to store dyndep intermediate results. |
1511 | 0 | std::string ddiFileName = cmStrCat(objectFileName, ".ddi"); |
1512 | 0 | scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = |
1513 | 0 | tg->ConvertToOutputFormatForShell(ddiFileName); |
1514 | 0 | scanBuild.RspFile = cmStrCat(ddiFileName, ".rsp"); |
1515 | | |
1516 | | // Outputs of the scan/preprocessor build statement. |
1517 | 0 | if (compilePP) { |
1518 | 0 | scanBuild.Outputs.push_back(ppFileName); |
1519 | 0 | scanBuild.ImplicitOuts.push_back(ddiFileName); |
1520 | 0 | } else { |
1521 | 0 | scanBuild.Outputs.push_back(ddiFileName); |
1522 | 0 | scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] = |
1523 | 0 | tg->ConvertToOutputFormatForShell(ppFileName); |
1524 | 0 | if (!compilationPreprocesses) { |
1525 | | // Compilation does not preprocess and we are not compiling an |
1526 | | // already-preprocessed source. Make compilation depend on the scan |
1527 | | // results to honor implicit dependencies discovered during scanning |
1528 | | // (such as Fortran INCLUDE directives). |
1529 | 0 | objBuild.ImplicitDeps.emplace_back(ddiFileName); |
1530 | 0 | } |
1531 | 0 | } |
1532 | | |
1533 | | // Scanning always provides a depfile for preprocessor dependencies. This |
1534 | | // variable is unused in `msvc`-deptype scanners. |
1535 | 0 | tg->AddDepfileBinding(scanBuild.Variables, |
1536 | 0 | cmStrCat(scanBuild.Outputs.front(), ".d")); |
1537 | 0 | if (compilePP) { |
1538 | | // The actual compilation does not need a depfile because it |
1539 | | // depends on the already-preprocessed source. |
1540 | 0 | tg->RemoveDepfileBinding(vars); |
1541 | 0 | } |
1542 | |
|
1543 | 0 | return scanBuild; |
1544 | 0 | } |
1545 | | } |
1546 | | |
1547 | | void cmNinjaTargetGenerator::WriteObjectBuildStatement( |
1548 | | cmSourceFile const* source, std::string const& config, |
1549 | | std::string const& fileConfig, bool firstForConfig) |
1550 | 0 | { |
1551 | 0 | std::string const language = source->GetLanguage(); |
1552 | 0 | std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source); |
1553 | 0 | std::string const targetSupportDir = |
1554 | 0 | this->ConvertToNinjaPath(this->GeneratorTarget->GetCMFSupportDirectory()); |
1555 | 0 | std::string const objectDir = |
1556 | 0 | this->ConvertToNinjaPath(this->GetObjectFileDir(config)); |
1557 | 0 | std::string const objectFileName = |
1558 | 0 | this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)); |
1559 | 0 | std::string const objectFileDir = |
1560 | 0 | cmSystemTools::GetFilenamePath(objectFileName); |
1561 | |
|
1562 | 0 | std::string cmakeVarLang = cmStrCat("CMAKE_", language); |
1563 | | |
1564 | | // build response file name |
1565 | 0 | std::string cmakeLinkVar = cmStrCat(cmakeVarLang, "_RESPONSE_FILE_FLAG"); |
1566 | |
|
1567 | 0 | cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); |
1568 | |
|
1569 | 0 | bool const lang_supports_response = |
1570 | 0 | !(language == "RC" || (language == "CUDA" && !flag)); |
1571 | 0 | int const commandLineLengthLimit = |
1572 | 0 | ((lang_supports_response && this->ForceResponseFile())) ? -1 : 0; |
1573 | 0 | cmValue pchExtension = |
1574 | 0 | this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION"); |
1575 | 0 | bool const isPch = cmHasSuffix(objectFileName, pchExtension); |
1576 | 0 | bool const needDyndep = !isPch && |
1577 | 0 | this->GeneratorTarget->NeedDyndepForSource(language, config, source); |
1578 | |
|
1579 | 0 | WithScanning withScanning = |
1580 | 0 | needDyndep ? WithScanning::Yes : WithScanning::No; |
1581 | 0 | cmNinjaBuild objBuild( |
1582 | 0 | this->LanguageCompilerRule(language, config, withScanning)); |
1583 | 0 | cmNinjaVars& vars = objBuild.Variables; |
1584 | 0 | vars["FLAGS"] = |
1585 | 0 | this->ComputeFlagsForObject(source, language, config, objectFileName); |
1586 | 0 | vars["DEFINES"] = this->ComputeDefines(source, language, config); |
1587 | 0 | vars["INCLUDES"] = this->ComputeIncludes(source, language, config); |
1588 | 0 | vars["CONFIG"] = config; |
1589 | 0 | if (this->GetGeneratorTarget()->GetUseShortObjectNames()) { |
1590 | 0 | vars.emplace( |
1591 | 0 | "description", |
1592 | 0 | cmStrCat("Compiling object ", objectFileName, " from source ", |
1593 | 0 | this->GetLocalGenerator()->GetRelativeSourceFileName(*source))); |
1594 | 0 | } |
1595 | |
|
1596 | 0 | auto compilerLauncher = this->GetCompilerLauncher(language, config); |
1597 | |
|
1598 | 0 | cmGeneratorFileSet const* fileSet = |
1599 | 0 | this->GeneratorTarget->GetFileSetForSource(config, source); |
1600 | |
|
1601 | 0 | cmValue const fsSkipCodeCheckVal = |
1602 | 0 | fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr; |
1603 | 0 | cmValue const srcSkipCodeCheckVal = source->GetProperty("SKIP_LINTING"); |
1604 | 0 | bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet() |
1605 | 0 | ? fsSkipCodeCheckVal.IsOn() |
1606 | 0 | : (srcSkipCodeCheckVal.IsSet() |
1607 | 0 | ? srcSkipCodeCheckVal.IsOn() |
1608 | 0 | : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING")); |
1609 | |
|
1610 | 0 | if (!skipCodeCheck) { |
1611 | 0 | auto const cmakeCmd = |
1612 | 0 | this->ConvertToOutputFormatForShell(cmSystemTools::GetCMakeCommand()); |
1613 | 0 | vars["CODE_CHECK"] = |
1614 | 0 | this->GenerateCodeCheckRules(*source, compilerLauncher, cmakeCmd, config, |
1615 | 0 | [this](std::string const& path) { |
1616 | 0 | return this->ConvertToNinjaPath(path); |
1617 | 0 | }); |
1618 | 0 | } |
1619 | | |
1620 | | // If compiler launcher was specified and not consumed above, it |
1621 | | // goes to the beginning of the command line. |
1622 | 0 | if (!compilerLauncher.empty()) { |
1623 | 0 | cmList args{ compilerLauncher, cmList::EmptyElements::Yes }; |
1624 | 0 | if (!args.empty()) { |
1625 | 0 | args[0] = this->LocalGenerator->ConvertToOutputFormat( |
1626 | 0 | args[0], cmOutputConverter::SHELL); |
1627 | 0 | for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) { |
1628 | 0 | i = this->LocalGenerator->EscapeForShell(i); |
1629 | 0 | } |
1630 | 0 | vars["LAUNCHER"] = args.join(" ") + " "; |
1631 | 0 | } |
1632 | 0 | } |
1633 | |
|
1634 | 0 | if (this->GetMakefile()->GetSafeDefinition( |
1635 | 0 | cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) { |
1636 | 0 | bool replaceExt = false; |
1637 | 0 | if (!language.empty()) { |
1638 | 0 | std::string repVar = |
1639 | 0 | cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE"); |
1640 | 0 | replaceExt = this->Makefile->IsOn(repVar); |
1641 | 0 | } |
1642 | 0 | this->AddDepfileBinding( |
1643 | 0 | vars, |
1644 | 0 | replaceExt ? cmStrCat(objectFileDir, '/', |
1645 | 0 | cmSystemTools::GetFilenameWithoutLastExtension( |
1646 | 0 | objectFileName), |
1647 | 0 | ".d") |
1648 | 0 | : cmStrCat(objectFileName, ".d")); |
1649 | 0 | } |
1650 | |
|
1651 | 0 | this->SetMsvcTargetPdbVariable(vars, config); |
1652 | |
|
1653 | 0 | if (firstForConfig) { |
1654 | 0 | this->ExportObjectCompileCommand( |
1655 | 0 | language, sourceFilePath, objectDir, targetSupportDir, objectFileName, |
1656 | 0 | objectFileDir, vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], |
1657 | 0 | vars["TARGET_COMPILE_PDB"], vars["TARGET_PDB"], config, withScanning); |
1658 | 0 | } |
1659 | |
|
1660 | 0 | objBuild.Outputs.push_back(objectFileName); |
1661 | 0 | if (firstForConfig && !isPch) { |
1662 | | // Add this object to the list of object files. |
1663 | 0 | this->Configs[config].Objects.push_back(objectFileName); |
1664 | 0 | } |
1665 | |
|
1666 | 0 | objBuild.ExplicitDeps.push_back(sourceFilePath); |
1667 | | |
1668 | | // Add precompile headers dependencies |
1669 | 0 | std::vector<std::string> depList; |
1670 | |
|
1671 | 0 | std::vector<std::string> pchArchs = |
1672 | 0 | this->GeneratorTarget->GetPchArchs(config, language); |
1673 | |
|
1674 | 0 | std::unordered_set<std::string> pchSources; |
1675 | 0 | for (std::string const& arch : pchArchs) { |
1676 | 0 | std::string const pchSource = |
1677 | 0 | this->GeneratorTarget->GetPchSource(config, language, arch); |
1678 | |
|
1679 | 0 | if (!pchSource.empty()) { |
1680 | 0 | pchSources.insert(pchSource); |
1681 | 0 | } |
1682 | 0 | } |
1683 | |
|
1684 | 0 | if (!pchSources.empty() && |
1685 | 0 | !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || |
1686 | 0 | source->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { |
1687 | 0 | for (std::string const& arch : pchArchs) { |
1688 | 0 | depList.push_back( |
1689 | 0 | this->GeneratorTarget->GetPchHeader(config, language, arch)); |
1690 | 0 | if (pchSources.find(source->GetFullPath()) == pchSources.end()) { |
1691 | 0 | depList.push_back( |
1692 | 0 | this->GeneratorTarget->GetPchFile(config, language, arch)); |
1693 | 0 | } |
1694 | 0 | } |
1695 | 0 | } |
1696 | |
|
1697 | 0 | if (cmValue objectDeps = source->GetProperty("OBJECT_DEPENDS")) { |
1698 | 0 | cmList objDepList{ *objectDeps }; |
1699 | 0 | std::copy(objDepList.begin(), objDepList.end(), |
1700 | 0 | std::back_inserter(depList)); |
1701 | 0 | } |
1702 | |
|
1703 | 0 | if (!depList.empty()) { |
1704 | 0 | for (std::string& odi : depList) { |
1705 | 0 | if (cmSystemTools::FileIsFullPath(odi)) { |
1706 | 0 | odi = cmSystemTools::CollapseFullPath(odi); |
1707 | 0 | } |
1708 | 0 | } |
1709 | 0 | std::transform(depList.begin(), depList.end(), |
1710 | 0 | std::back_inserter(objBuild.ImplicitDeps), |
1711 | 0 | this->MapToNinjaPath()); |
1712 | 0 | } |
1713 | |
|
1714 | 0 | if (this->HasPrivateGeneratedSources) { |
1715 | 0 | objBuild.OrderOnlyDeps.push_back( |
1716 | 0 | this->OrderDependsTargetForTargetPrivate(config)); |
1717 | 0 | } else { |
1718 | 0 | objBuild.OrderOnlyDeps.push_back( |
1719 | 0 | this->OrderDependsTargetForTarget(config)); |
1720 | 0 | } |
1721 | | |
1722 | | // If the source file is GENERATED and does not have a custom command |
1723 | | // (either attached to this source file or another one), assume that one of |
1724 | | // the target dependencies, OBJECT_DEPENDS or header file custom commands |
1725 | | // will rebuild the file. |
1726 | 0 | if (source->GetIsGenerated() && |
1727 | 0 | !source->GetPropertyAsBool("__CMAKE_GENERATED_BY_CMAKE") && |
1728 | 0 | !source->GetCustomCommand() && |
1729 | 0 | !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFilePath)) { |
1730 | 0 | this->GetGlobalGenerator()->AddAssumedSourceDependencies( |
1731 | 0 | sourceFilePath, objBuild.OrderOnlyDeps); |
1732 | 0 | } |
1733 | | |
1734 | | // For some cases we scan to dynamically discover dependencies. |
1735 | 0 | bool const compilationPreprocesses = |
1736 | 0 | !this->NeedExplicitPreprocessing(language); |
1737 | |
|
1738 | 0 | std::string modmapFormat; |
1739 | 0 | if (needDyndep) { |
1740 | 0 | std::string const modmapFormatVar = |
1741 | 0 | cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT"); |
1742 | 0 | modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar); |
1743 | 0 | } |
1744 | |
|
1745 | 0 | if (needDyndep) { |
1746 | | // If source/target has preprocessing turned off, we still need to |
1747 | | // generate an explicit dependency step |
1748 | 0 | auto const srcpp = source->GetSafeProperty("Fortran_PREPROCESS"); |
1749 | 0 | cmOutputConverter::FortranPreprocess preprocess = |
1750 | 0 | cmOutputConverter::GetFortranPreprocess(srcpp); |
1751 | 0 | if (preprocess == cmOutputConverter::FortranPreprocess::Unset) { |
1752 | 0 | auto const& tgtpp = |
1753 | 0 | this->GeneratorTarget->GetSafeProperty("Fortran_PREPROCESS"); |
1754 | 0 | preprocess = cmOutputConverter::GetFortranPreprocess(tgtpp); |
1755 | 0 | } |
1756 | |
|
1757 | 0 | bool const compilePP = !compilationPreprocesses && |
1758 | 0 | (preprocess != cmOutputConverter::FortranPreprocess::NotNeeded); |
1759 | 0 | bool const compilePPWithDefines = |
1760 | 0 | compilePP && this->CompileWithDefines(language); |
1761 | |
|
1762 | 0 | std::string scanRuleName; |
1763 | 0 | std::string ppFileName; |
1764 | 0 | if (compilePP) { |
1765 | 0 | scanRuleName = this->LanguagePreprocessAndScanRule(language, config); |
1766 | 0 | ppFileName = this->ConvertToNinjaPath( |
1767 | 0 | this->GetPreprocessedFilePath(source, config)); |
1768 | 0 | } else { |
1769 | 0 | scanRuleName = this->LanguageScanRule(language, config); |
1770 | 0 | ppFileName = cmStrCat(objectFileName, ".ddi.i"); |
1771 | 0 | } |
1772 | |
|
1773 | 0 | cmNinjaBuild ppBuild = GetScanBuildStatement( |
1774 | 0 | scanRuleName, ppFileName, compilePP, compilePPWithDefines, |
1775 | 0 | compilationPreprocesses, objBuild, vars, objectFileName, this); |
1776 | |
|
1777 | 0 | if (compilePP) { |
1778 | | // In case compilation requires flags that are incompatible with |
1779 | | // preprocessing, include them here. |
1780 | 0 | std::string const& postFlag = this->Makefile->GetSafeDefinition( |
1781 | 0 | cmStrCat("CMAKE_", language, "_POSTPROCESS_FLAG")); |
1782 | 0 | this->LocalGenerator->AppendFlags(vars["FLAGS"], postFlag); |
1783 | | |
1784 | | // Prepend source file's original directory as an include directory |
1785 | | // so e.g. Fortran INCLUDE statements can look for files in it. |
1786 | 0 | std::vector<std::string> sourceDirectory; |
1787 | 0 | sourceDirectory.push_back( |
1788 | 0 | cmSystemTools::GetParentDirectory(source->GetFullPath())); |
1789 | |
|
1790 | 0 | std::string sourceDirectoryFlag = this->LocalGenerator->GetIncludeFlags( |
1791 | 0 | sourceDirectory, this->GeneratorTarget, language, config, false); |
1792 | |
|
1793 | 0 | vars["INCLUDES"] = cmStrCat(sourceDirectoryFlag, ' ', vars["INCLUDES"]); |
1794 | 0 | } |
1795 | |
|
1796 | 0 | ScanningFiles scanningFiles; |
1797 | |
|
1798 | 0 | if (firstForConfig) { |
1799 | 0 | scanningFiles.ScanningOutput = cmStrCat(objectFileName, ".ddi"); |
1800 | 0 | } |
1801 | |
|
1802 | 0 | this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, |
1803 | 0 | this->GetGeneratorTarget(), source, |
1804 | 0 | ppBuild.Variables); |
1805 | |
|
1806 | 0 | this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), |
1807 | 0 | ppBuild, commandLineLengthLimit); |
1808 | |
|
1809 | 0 | std::string const dyndep = this->GetDyndepFilePath(language, config); |
1810 | 0 | objBuild.OrderOnlyDeps.push_back(dyndep); |
1811 | 0 | vars["dyndep"] = dyndep; |
1812 | |
|
1813 | 0 | if (!modmapFormat.empty()) { |
1814 | | // XXX(modmap): If changing this path construction, change |
1815 | | // `cmGlobalNinjaGenerator::WriteDyndep` and |
1816 | | // `cmNinjaTargetGenerator::ExportObjectCompileCommand` to expect the |
1817 | | // corresponding file path. |
1818 | 0 | std::string ddModmapFile = cmStrCat(objectFileName, ".modmap"); |
1819 | 0 | vars["DYNDEP_MODULE_MAP_FILE"] = |
1820 | 0 | this->ConvertToOutputFormatForShell(ddModmapFile); |
1821 | 0 | objBuild.ImplicitDeps.push_back(ddModmapFile); |
1822 | 0 | scanningFiles.ModuleMapFile = std::move(ddModmapFile); |
1823 | 0 | } |
1824 | |
|
1825 | 0 | if (!scanningFiles.IsEmpty()) { |
1826 | 0 | this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles); |
1827 | 0 | } |
1828 | 0 | } |
1829 | |
|
1830 | 0 | this->EnsureParentDirectoryExists(objectFileName); |
1831 | |
|
1832 | 0 | vars["OBJECT_DIR"] = this->ConvertToOutputFormatForShell(objectDir); |
1833 | 0 | vars["TARGET_SUPPORT_DIR"] = |
1834 | 0 | this->ConvertToOutputFormatForShell(targetSupportDir); |
1835 | 0 | vars["OBJECT_FILE_DIR"] = this->ConvertToOutputFormatForShell(objectFileDir); |
1836 | |
|
1837 | 0 | this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, |
1838 | 0 | this->GetGeneratorTarget(), source, vars); |
1839 | |
|
1840 | 0 | if (!pchSources.empty() && |
1841 | 0 | !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || |
1842 | 0 | source->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { |
1843 | 0 | auto pchIt = pchSources.find(source->GetFullPath()); |
1844 | 0 | if (pchIt != pchSources.end()) { |
1845 | 0 | this->addPoolNinjaVariable("JOB_POOL_PRECOMPILE_HEADER", config, |
1846 | 0 | this->GetGeneratorTarget(), nullptr, vars); |
1847 | 0 | } |
1848 | 0 | } |
1849 | |
|
1850 | 0 | objBuild.RspFile = cmStrCat(objectFileName, ".rsp"); |
1851 | |
|
1852 | 0 | if (language == "ISPC") { |
1853 | 0 | std::string const& objectName = |
1854 | 0 | this->GeneratorTarget->GetObjectName(source); |
1855 | 0 | std::string ispcSource = |
1856 | 0 | cmSystemTools::GetFilenameWithoutLastExtension(objectName); |
1857 | 0 | ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource); |
1858 | |
|
1859 | 0 | cmValue ispcSuffixProp = |
1860 | 0 | this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX"); |
1861 | 0 | assert(ispcSuffixProp); |
1862 | |
|
1863 | 0 | std::string ispcHeaderDirectory = |
1864 | 0 | this->GeneratorTarget->GetObjectDirectory(config); |
1865 | 0 | if (cmValue prop = |
1866 | 0 | this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) { |
1867 | 0 | ispcHeaderDirectory = |
1868 | 0 | cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop); |
1869 | 0 | } |
1870 | |
|
1871 | 0 | std::string ispcHeader = |
1872 | 0 | cmStrCat(ispcHeaderDirectory, '/', ispcSource, *ispcSuffixProp); |
1873 | 0 | ispcHeader = this->ConvertToNinjaPath(ispcHeader); |
1874 | | |
1875 | | // Make sure ninja knows what command generates the header |
1876 | 0 | objBuild.ImplicitOuts.push_back(ispcHeader); |
1877 | | |
1878 | | // Make sure ninja knows how to clean the generated header |
1879 | 0 | this->GetGlobalGenerator()->AddAdditionalCleanFile(ispcHeader, config); |
1880 | |
|
1881 | 0 | auto ispcSuffixes = |
1882 | 0 | detail::ComputeISPCObjectSuffixes(this->GeneratorTarget); |
1883 | 0 | if (ispcSuffixes.size() > 1) { |
1884 | 0 | std::string rootObjectDir = |
1885 | 0 | this->GeneratorTarget->GetObjectDirectory(config); |
1886 | 0 | auto ispcSideEffectObjects = detail::ComputeISPCExtraObjects( |
1887 | 0 | objectName, rootObjectDir, ispcSuffixes); |
1888 | |
|
1889 | 0 | for (auto sideEffect : ispcSideEffectObjects) { |
1890 | 0 | sideEffect = this->ConvertToNinjaPath(sideEffect); |
1891 | 0 | objBuild.ImplicitOuts.emplace_back(sideEffect); |
1892 | 0 | this->GetGlobalGenerator()->AddAdditionalCleanFile(sideEffect, config); |
1893 | 0 | } |
1894 | 0 | } |
1895 | |
|
1896 | 0 | vars["ISPC_HEADER_FILE"] = this->ConvertToOutputFormatForShell(ispcHeader); |
1897 | 0 | } else { |
1898 | 0 | auto headers = this->GeneratorTarget->GetGeneratedISPCHeaders(config); |
1899 | 0 | if (!headers.empty()) { |
1900 | 0 | std::transform(headers.begin(), headers.end(), headers.begin(), |
1901 | 0 | this->MapToNinjaPath()); |
1902 | 0 | objBuild.OrderOnlyDeps.insert(objBuild.OrderOnlyDeps.end(), |
1903 | 0 | headers.begin(), headers.end()); |
1904 | 0 | } |
1905 | 0 | } |
1906 | |
|
1907 | 0 | if (language == "Rust") { |
1908 | 0 | cmValue const rustEmit = source->GetRustEmitProperty(); |
1909 | 0 | vars["RUST_EMIT"] = rustEmit; |
1910 | 0 | } |
1911 | |
|
1912 | 0 | if (language == "Swift") { |
1913 | 0 | this->EmitSwiftDependencyInfo(source, config); |
1914 | 0 | } else { |
1915 | 0 | this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), |
1916 | 0 | objBuild, commandLineLengthLimit); |
1917 | 0 | } |
1918 | |
|
1919 | 0 | if (cmValue objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) { |
1920 | 0 | std::string evaluatedObjectOutputs = cmGeneratorExpression::Evaluate( |
1921 | 0 | *objectOutputs, this->LocalGenerator, config); |
1922 | |
|
1923 | 0 | if (!evaluatedObjectOutputs.empty()) { |
1924 | 0 | cmNinjaBuild build("phony"); |
1925 | 0 | build.Comment = "Additional output files."; |
1926 | 0 | build.Outputs = cmList{ evaluatedObjectOutputs }.data(); |
1927 | 0 | std::transform(build.Outputs.begin(), build.Outputs.end(), |
1928 | 0 | build.Outputs.begin(), this->MapToNinjaPath()); |
1929 | 0 | build.ExplicitDeps = objBuild.Outputs; |
1930 | 0 | this->GetGlobalGenerator()->WriteBuild( |
1931 | 0 | this->GetImplFileStream(fileConfig), build); |
1932 | 0 | } |
1933 | 0 | } |
1934 | 0 | } |
1935 | | |
1936 | | void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement( |
1937 | | cmSourceFile const* source, std::string const& config, |
1938 | | std::string const& fileConfig, bool firstForConfig) |
1939 | 0 | { |
1940 | 0 | std::string const language = source->GetLanguage(); |
1941 | 0 | if (language != "CXX"_s) { |
1942 | 0 | this->GetMakefile()->IssueMessage( |
1943 | 0 | MessageType::FATAL_ERROR, |
1944 | 0 | cmStrCat("Source file '", source->GetFullPath(), "' of target '", |
1945 | 0 | this->GetTargetName(), "' is a '", language, |
1946 | 0 | "' source but must be 'CXX' in order to have a BMI build " |
1947 | 0 | "statement generated.")); |
1948 | 0 | return; |
1949 | 0 | } |
1950 | | |
1951 | 0 | std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source); |
1952 | 0 | std::string const targetSupportDir = |
1953 | 0 | this->ConvertToNinjaPath(this->GeneratorTarget->GetCMFSupportDirectory()); |
1954 | 0 | std::string const bmiDir = this->ConvertToNinjaPath( |
1955 | 0 | cmStrCat(this->GeneratorTarget->GetSupportDirectory(), |
1956 | 0 | this->GetGlobalGenerator()->ConfigDirectory(config))); |
1957 | 0 | std::string const bmiFileName = |
1958 | 0 | this->ConvertToNinjaPath(this->GetBmiFilePath(source, config)); |
1959 | 0 | std::string const bmiFileDir = cmSystemTools::GetFilenamePath(bmiFileName); |
1960 | |
|
1961 | 0 | int const commandLineLengthLimit = this->ForceResponseFile() ? -1 : 0; |
1962 | |
|
1963 | 0 | cmNinjaBuild bmiBuild( |
1964 | 0 | this->LanguageCompilerRule(language, config, WithScanning::Yes)); |
1965 | 0 | cmNinjaVars& vars = bmiBuild.Variables; |
1966 | 0 | vars["FLAGS"] = |
1967 | 0 | this->ComputeFlagsForObject(source, language, config, bmiFileName); |
1968 | 0 | vars["DEFINES"] = this->ComputeDefines(source, language, config); |
1969 | 0 | vars["INCLUDES"] = this->ComputeIncludes(source, language, config); |
1970 | 0 | vars["CONFIG"] = config; |
1971 | |
|
1972 | 0 | if (this->GetMakefile()->GetSafeDefinition( |
1973 | 0 | cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) { |
1974 | 0 | bool replaceExt = false; |
1975 | 0 | if (!language.empty()) { |
1976 | 0 | std::string repVar = |
1977 | 0 | cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE"); |
1978 | 0 | replaceExt = this->Makefile->IsOn(repVar); |
1979 | 0 | } |
1980 | 0 | this->AddDepfileBinding( |
1981 | 0 | vars, |
1982 | 0 | replaceExt |
1983 | 0 | ? cmStrCat(bmiFileDir, '/', |
1984 | 0 | cmSystemTools::GetFilenameWithoutLastExtension(bmiFileName), |
1985 | 0 | ".d") |
1986 | 0 | : cmStrCat(bmiFileName, ".d")); |
1987 | 0 | } |
1988 | |
|
1989 | 0 | std::string d = |
1990 | 0 | this->GeneratorTarget->GetClangTidyExportFixesDirectory(language); |
1991 | 0 | if (!d.empty()) { |
1992 | 0 | this->GlobalCommonGenerator->AddClangTidyExportFixesDir(d); |
1993 | 0 | std::string fixesFile = |
1994 | 0 | this->GetClangTidyReplacementsFilePath(d, *source, config); |
1995 | 0 | this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile); |
1996 | 0 | cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(fixesFile)); |
1997 | 0 | fixesFile = this->ConvertToNinjaPath(fixesFile); |
1998 | 0 | vars["CLANG_TIDY_EXPORT_FIXES"] = fixesFile; |
1999 | 0 | } |
2000 | |
|
2001 | 0 | this->SetMsvcTargetPdbVariable(vars, config); |
2002 | |
|
2003 | 0 | if (firstForConfig) { |
2004 | 0 | this->ExportObjectCompileCommand( |
2005 | 0 | language, sourceFilePath, bmiDir, targetSupportDir, bmiFileName, |
2006 | 0 | bmiFileDir, vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], |
2007 | 0 | vars["TARGET_COMPILE_PDB"], vars["TARGET_PDB"], config, |
2008 | 0 | WithScanning::Yes); |
2009 | 0 | } |
2010 | |
|
2011 | 0 | bmiBuild.Outputs.push_back(bmiFileName); |
2012 | 0 | bmiBuild.ExplicitDeps.push_back(sourceFilePath); |
2013 | |
|
2014 | 0 | std::vector<std::string> depList; |
2015 | |
|
2016 | 0 | bmiBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); |
2017 | | |
2018 | | // For some cases we scan to dynamically discover dependencies. |
2019 | 0 | std::string modmapFormat; |
2020 | 0 | if (true) { |
2021 | 0 | std::string const modmapFormatVar = |
2022 | 0 | cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT"); |
2023 | 0 | modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar); |
2024 | 0 | } |
2025 | |
|
2026 | 0 | { |
2027 | 0 | bool const compilePPWithDefines = this->CompileWithDefines(language); |
2028 | |
|
2029 | 0 | std::string scanRuleName = this->LanguageScanRule(language, config); |
2030 | 0 | std::string ppFileName = cmStrCat(bmiFileName, ".ddi.i"); |
2031 | |
|
2032 | 0 | cmNinjaBuild ppBuild = GetScanBuildStatement( |
2033 | 0 | scanRuleName, ppFileName, false, compilePPWithDefines, true, bmiBuild, |
2034 | 0 | vars, bmiFileName, this); |
2035 | |
|
2036 | 0 | ScanningFiles scanningFiles; |
2037 | |
|
2038 | 0 | if (firstForConfig) { |
2039 | 0 | scanningFiles.ScanningOutput = cmStrCat(bmiFileName, ".ddi"); |
2040 | 0 | } |
2041 | |
|
2042 | 0 | this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, |
2043 | 0 | this->GetGeneratorTarget(), source, |
2044 | 0 | ppBuild.Variables); |
2045 | |
|
2046 | 0 | this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), |
2047 | 0 | ppBuild, commandLineLengthLimit); |
2048 | |
|
2049 | 0 | std::string const dyndep = this->GetDyndepFilePath(language, config); |
2050 | 0 | bmiBuild.OrderOnlyDeps.push_back(dyndep); |
2051 | 0 | vars["dyndep"] = dyndep; |
2052 | |
|
2053 | 0 | if (!modmapFormat.empty()) { |
2054 | 0 | std::string ddModmapFile = cmStrCat(bmiFileName, ".modmap"); |
2055 | 0 | vars["DYNDEP_MODULE_MAP_FILE"] = |
2056 | 0 | this->ConvertToOutputFormatForShell(ddModmapFile); |
2057 | 0 | scanningFiles.ModuleMapFile = std::move(ddModmapFile); |
2058 | 0 | } |
2059 | |
|
2060 | 0 | if (!scanningFiles.IsEmpty()) { |
2061 | 0 | this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles); |
2062 | 0 | } |
2063 | 0 | } |
2064 | |
|
2065 | 0 | this->EnsureParentDirectoryExists(bmiFileName); |
2066 | |
|
2067 | 0 | vars["OBJECT_DIR"] = this->ConvertToOutputFormatForShell(bmiDir); |
2068 | 0 | vars["TARGET_SUPPORT_DIR"] = |
2069 | 0 | this->ConvertToOutputFormatForShell(targetSupportDir); |
2070 | 0 | vars["OBJECT_FILE_DIR"] = this->ConvertToOutputFormatForShell(bmiFileDir); |
2071 | |
|
2072 | 0 | this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, |
2073 | 0 | this->GetGeneratorTarget(), source, vars); |
2074 | |
|
2075 | 0 | bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp"); |
2076 | |
|
2077 | 0 | this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), |
2078 | 0 | bmiBuild, commandLineLengthLimit); |
2079 | 0 | } |
2080 | | |
2081 | | void cmNinjaTargetGenerator::WriteSwiftObjectBuildStatement( |
2082 | | std::vector<cmSourceFile const*> const& sources, std::string const& config, |
2083 | | std::string const& fileConfig, bool firstForConfig) |
2084 | 0 | { |
2085 | | // Swift sources are compiled as a module, not individually like with C/C++. |
2086 | | // Flags, header search paths, and definitions are passed to the entire |
2087 | | // module build, but we still need to emit compile-commands for each source |
2088 | | // file in order to support CMAKE_EXPORT_COMPILE_COMMANDS. |
2089 | | // In whole-module mode, with a single thread, the Swift compiler will |
2090 | | // only emit a single object file, but if more than one thread is specified, |
2091 | | // or building in other modes, the compiler will emit multiple object files. |
2092 | | // When building a single-output, we do not provide an output-file-map (OFM), |
2093 | | // and instead pass `-o` to tell the compiler where to write the object. |
2094 | | // When building multiple outputs, we provide an OFM to tell the compiler |
2095 | | // where to put each object. |
2096 | | // |
2097 | | // |
2098 | | // Per-Target (module): |
2099 | | // - Flags |
2100 | | // - Definitions |
2101 | | // - Include paths |
2102 | | // - (single-output) output object filename |
2103 | | // - Swiftmodule (for importable targets), produced either by the compile |
2104 | | // edge or by a separate emit-module edge |
2105 | | // |
2106 | | // Per-File: |
2107 | | // - compile-command |
2108 | | // - (multi-output) OFM data |
2109 | | // - (multi-output) output object filename |
2110 | | // |
2111 | | // Note: Due to the differences in the build models, we are only able to |
2112 | | // build the object build-graph if we know what mode the target is built in. |
2113 | | // For that, we need the "NEW" behavior for CMP0157. Otherwise, we have to |
2114 | | // fall back on the old "linker" build. Otherwise, this should be |
2115 | | // indistinguishable from the old behavior. |
2116 | |
|
2117 | 0 | if (sources.empty()) { |
2118 | 0 | return; |
2119 | 0 | } |
2120 | | |
2121 | 0 | cmSwiftCompileMode compileMode; |
2122 | 0 | if (cm::optional<cmSwiftCompileMode> optionalCompileMode = |
2123 | 0 | this->LocalGenerator->GetSwiftCompileMode(this->GeneratorTarget, |
2124 | 0 | config)) { |
2125 | 0 | compileMode = *optionalCompileMode; |
2126 | 0 | } else { |
2127 | | // CMP0157 is not NEW, bailing early! |
2128 | 0 | return; |
2129 | 0 | } |
2130 | | |
2131 | 0 | std::string const language = "Swift"; |
2132 | 0 | std::string const objectDir = this->ConvertToNinjaPath( |
2133 | 0 | cmStrCat(this->GeneratorTarget->GetSupportDirectory(), |
2134 | 0 | this->GetGlobalGenerator()->ConfigDirectory(config))); |
2135 | |
|
2136 | 0 | cmGeneratorTarget const& target = *this->GeneratorTarget; |
2137 | 0 | cmNinjaBuild objBuild( |
2138 | 0 | this->LanguageCompilerRule(language, config, WithScanning::No)); |
2139 | 0 | cmNinjaVars& vars = objBuild.Variables; |
2140 | | |
2141 | | // The swift toolchain leaves outputs untouched if there are no meaningful |
2142 | | // changes to input files (e.g. addition of a comment). |
2143 | 0 | vars.emplace("restat", "1"); |
2144 | |
|
2145 | 0 | std::string const moduleName = target.GetSwiftModuleName(); |
2146 | 0 | std::string const moduleFilepath = |
2147 | 0 | this->ConvertToNinjaPath(target.GetSwiftModulePath(config)); |
2148 | |
|
2149 | 0 | vars.emplace("description", |
2150 | 0 | cmStrCat("Building Swift Module '", moduleName, "' with ", |
2151 | 0 | sources.size(), |
2152 | 0 | sources.size() == 1 ? " source" : " sources")); |
2153 | |
|
2154 | 0 | bool const isSingleOutput = [this, compileMode]() -> bool { |
2155 | 0 | bool isMultiThread = false; |
2156 | 0 | if (cmValue numThreadStr = |
2157 | 0 | this->GetMakefile()->GetDefinition("CMAKE_Swift_NUM_THREADS")) { |
2158 | 0 | unsigned long numThreads; |
2159 | 0 | cmStrToULong(*numThreadStr, &numThreads); |
2160 | | // numThreads == 1 is multi-threaded according to swiftc |
2161 | 0 | isMultiThread = numThreads > 0; |
2162 | 0 | } |
2163 | 0 | return !isMultiThread && compileMode == cmSwiftCompileMode::Wholemodule; |
2164 | 0 | }(); |
2165 | | |
2166 | | // Does this swift target emit a module file for importing into other |
2167 | | // targets? |
2168 | 0 | auto isImportableTarget = [](cmGeneratorTarget const& tgt) -> bool { |
2169 | | // Everything except for executables that don't export anything should emit |
2170 | | // some way to import them. |
2171 | 0 | if (tgt.GetType() == cmStateEnums::EXECUTABLE) { |
2172 | 0 | return tgt.IsExecutableWithExports(); |
2173 | 0 | } |
2174 | 0 | return true; |
2175 | 0 | }; |
2176 | 0 | bool const targetIsImportable = isImportableTarget(target); |
2177 | | |
2178 | | // Check if we can emit the module separately (produces .swiftmodule before |
2179 | | // compilation finishes, enabling downstream modules to compile in parallel). |
2180 | 0 | bool const emitModuleSeparately = [&]() -> bool { |
2181 | 0 | if (!targetIsImportable || |
2182 | 0 | !this->GetMakefile()->GetDefinition("CMAKE_Swift_EMIT_MODULE")) { |
2183 | 0 | return false; |
2184 | 0 | } |
2185 | 0 | cmValue prop = |
2186 | 0 | this->GeneratorTarget->GetProperty("Swift_SEPARATE_MODULE_EMISSION"); |
2187 | 0 | if (prop) { |
2188 | 0 | return prop.IsOn(); |
2189 | 0 | } |
2190 | 0 | return this->GeneratorTarget->GetPolicyStatusCMP0215() == cmPolicies::NEW; |
2191 | 0 | }(); |
2192 | | |
2193 | | // Build flags common to both compile and emit-module edges. |
2194 | 0 | if (target.GetType() != cmStateEnums::EXECUTABLE) { |
2195 | | // Without `-emit-library` or `-emit-executable`, targets with a single |
2196 | | // source file parse as a Swift script instead of like normal source. For |
2197 | | // non-executable targets, append this to ensure that they are parsed like |
2198 | | // a normal source. |
2199 | 0 | this->LocalGenerator->AppendFlags(vars["FLAGS"], "-parse-as-library"); |
2200 | 0 | } |
2201 | 0 | if (target.GetType() == cmStateEnums::STATIC_LIBRARY) { |
2202 | 0 | this->LocalGenerator->AppendFlags(vars["FLAGS"], "-static"); |
2203 | 0 | } |
2204 | 0 | this->LocalGenerator->AppendFlags(vars["FLAGS"], |
2205 | 0 | cmStrCat("-module-name ", moduleName)); |
2206 | |
|
2207 | 0 | if (target.GetType() != cmStateEnums::EXECUTABLE) { |
2208 | 0 | std::string const libraryLinkNameFlag = "-module-link-name"; |
2209 | 0 | std::string const libraryLinkName = |
2210 | 0 | this->GetGeneratorTarget()->GetLibraryNames(config).Base; |
2211 | 0 | this->LocalGenerator->AppendFlags( |
2212 | 0 | vars["FLAGS"], cmStrCat(libraryLinkNameFlag, ' ', libraryLinkName)); |
2213 | 0 | } |
2214 | 0 | this->LocalGenerator->AppendFlags(vars["FLAGS"], |
2215 | 0 | this->GetFlags(language, config)); |
2216 | 0 | vars["DEFINES"] = this->GetDefines(language, config); |
2217 | 0 | vars["INCLUDES"] = this->GetIncludes(language, config); |
2218 | 0 | vars["CONFIG"] = config; |
2219 | | |
2220 | | // target-level object filename |
2221 | 0 | std::string const targetObjectFilename = this->ConvertToNinjaPath(cmStrCat( |
2222 | 0 | objectDir, '/', moduleName, |
2223 | 0 | this->GetGlobalGenerator()->GetLanguageOutputExtension(language))); |
2224 | 0 | objBuild.RspFile = cmStrCat(targetObjectFilename, ".swift.rsp"); |
2225 | | |
2226 | | // Importable targets keep -emit-module on compile so swiftc still emits |
2227 | | // .swiftdoc. When splitting module emission, both the .swiftmodule output |
2228 | | // and -emit-module flags move entirely to the separate emit-module edge. |
2229 | 0 | if (targetIsImportable) { |
2230 | 0 | this->Configs[config].SwiftModuleOutput = moduleFilepath; |
2231 | 0 | } |
2232 | 0 | if (targetIsImportable && !emitModuleSeparately) { |
2233 | 0 | objBuild.Outputs.push_back(moduleFilepath); |
2234 | 0 | } |
2235 | |
|
2236 | 0 | if (isSingleOutput) { |
2237 | 0 | objBuild.Outputs.push_back(targetObjectFilename); |
2238 | 0 | this->Configs[config].Objects.push_back(targetObjectFilename); |
2239 | 0 | } |
2240 | |
|
2241 | 0 | for (cmSourceFile const* sf : sources) { |
2242 | | // Add dependency to object build on each source file |
2243 | 0 | std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(sf); |
2244 | 0 | objBuild.ExplicitDeps.push_back(sourceFilePath); |
2245 | |
|
2246 | 0 | if (!isSingleOutput) { |
2247 | | // Object outputs |
2248 | 0 | std::string const objectFilepath = |
2249 | 0 | this->ConvertToNinjaPath(this->GetObjectFilePath(sf, config)); |
2250 | 0 | this->EnsureParentDirectoryExists(objectFilepath); |
2251 | 0 | objBuild.Outputs.push_back(objectFilepath); |
2252 | 0 | this->Configs[config].Objects.push_back(objectFilepath); |
2253 | | |
2254 | | // Add OFM data |
2255 | 0 | this->EmitSwiftDependencyInfo(sf, config); |
2256 | 0 | } |
2257 | 0 | } |
2258 | |
|
2259 | 0 | if (!isSingleOutput) { |
2260 | 0 | this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]); |
2261 | 0 | } |
2262 | | |
2263 | | // Save common flags for the emit-module edge before adding |
2264 | | // compile-specific flags (-emit-module, -emit-module-path, -o). |
2265 | 0 | std::string const commonFlags = vars["FLAGS"]; |
2266 | |
|
2267 | 0 | std::string const moduleOutputPath = |
2268 | 0 | this->LocalGenerator->ConvertToOutputFormat(moduleFilepath, |
2269 | 0 | cmOutputConverter::SHELL); |
2270 | 0 | if (targetIsImportable && !emitModuleSeparately && |
2271 | 0 | commonFlags.find("-emit-module-path") == std::string::npos) { |
2272 | 0 | std::string const emitModuleFlag = "-emit-module"; |
2273 | 0 | std::string const modulePathFlag = "-emit-module-path"; |
2274 | 0 | this->LocalGenerator->AppendFlags( |
2275 | 0 | vars["FLAGS"], { emitModuleFlag, modulePathFlag, moduleOutputPath }); |
2276 | 0 | } |
2277 | |
|
2278 | 0 | if (isSingleOutput) { |
2279 | 0 | this->LocalGenerator->AppendFlags(vars["FLAGS"], |
2280 | 0 | cmStrCat("-o ", targetObjectFilename)); |
2281 | 0 | } |
2282 | |
|
2283 | 0 | if (firstForConfig) { |
2284 | 0 | this->ExportSwiftObjectCompileCommand( |
2285 | 0 | sources, targetObjectFilename, vars["FLAGS"], vars["DEFINES"], |
2286 | 0 | vars["INCLUDES"], config, isSingleOutput); |
2287 | 0 | } |
2288 | |
|
2289 | 0 | for (cmTargetDepend const& dep : |
2290 | 0 | this->GetGlobalGenerator()->GetTargetDirectDepends(&target)) { |
2291 | 0 | if (!dep->IsLanguageUsed("Swift", config)) { |
2292 | 0 | continue; |
2293 | 0 | } |
2294 | | |
2295 | | // If the dependency emits a swiftmodule, add a dependency edge on that |
2296 | | // swiftmodule to the ninja build graph. |
2297 | 0 | if (isImportableTarget(*dep)) { |
2298 | 0 | std::string const depModuleFilepath = |
2299 | 0 | this->ConvertToNinjaPath(dep->GetSwiftModulePath(config)); |
2300 | 0 | objBuild.ImplicitDeps.push_back(depModuleFilepath); |
2301 | 0 | } |
2302 | 0 | } |
2303 | |
|
2304 | 0 | objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); |
2305 | | |
2306 | | // Write a separate emit-module build edge that produces .swiftmodule |
2307 | | // without compile outputs. This allows downstream Swift targets to start |
2308 | | // compiling as soon as the module interface is ready, overlapping with |
2309 | | // upstream compilation and linking. |
2310 | 0 | if (emitModuleSeparately) { |
2311 | 0 | cmNinjaBuild modBuild = objBuild; |
2312 | 0 | modBuild.Rule = this->LanguageEmitModuleRule(language, config); |
2313 | | |
2314 | | // Start from common flags (shared with compile edge) and add |
2315 | | // emit-module-specific flags. The emit-module rule template already |
2316 | | // contains -emit-module, so we only need -emit-module-path here. |
2317 | | // Skip if the flags already contain one (e.g. a directory-style path |
2318 | | // set by the target's compile options). |
2319 | 0 | modBuild.Variables["FLAGS"] = commonFlags; |
2320 | 0 | if (commonFlags.find("-emit-module-path") == std::string::npos) { |
2321 | 0 | this->LocalGenerator->AppendFlags( |
2322 | 0 | modBuild.Variables["FLAGS"], |
2323 | 0 | cmStrCat("-emit-module-path ", moduleOutputPath)); |
2324 | 0 | } |
2325 | |
|
2326 | 0 | modBuild.RspFile = cmStrCat(moduleFilepath, ".rsp"); |
2327 | | |
2328 | | // Output is just the .swiftmodule |
2329 | 0 | this->EnsureParentDirectoryExists(moduleFilepath); |
2330 | 0 | modBuild.Outputs.clear(); |
2331 | 0 | modBuild.Outputs.push_back(moduleFilepath); |
2332 | |
|
2333 | 0 | this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), |
2334 | 0 | modBuild, |
2335 | 0 | this->ForceResponseFile() ? -1 : 0); |
2336 | | |
2337 | | // Both edges share the same -output-file-map; serialize the compile |
2338 | | // edge after emit-module so they do not race on the module .swiftdeps. |
2339 | 0 | objBuild.OrderOnlyDeps.push_back(moduleFilepath); |
2340 | 0 | } |
2341 | | |
2342 | | // Write object build |
2343 | 0 | this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), |
2344 | 0 | objBuild, |
2345 | 0 | this->ForceResponseFile() ? -1 : 0); |
2346 | 0 | } |
2347 | | |
2348 | | void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, |
2349 | | std::string const& config) |
2350 | 0 | { |
2351 | 0 | Json::Value tdi(Json::objectValue); |
2352 | 0 | tdi["language"] = lang; |
2353 | 0 | tdi["compiler-id"] = this->Makefile->GetSafeDefinition( |
2354 | 0 | cmStrCat("CMAKE_", lang, "_COMPILER_ID")); |
2355 | 0 | tdi["compiler-simulate-id"] = this->Makefile->GetSafeDefinition( |
2356 | 0 | cmStrCat("CMAKE_", lang, "_SIMULATE_ID")); |
2357 | 0 | tdi["compiler-frontend-variant"] = this->Makefile->GetSafeDefinition( |
2358 | 0 | cmStrCat("CMAKE_", lang, "_COMPILER_FRONTEND_VARIANT")); |
2359 | |
|
2360 | 0 | std::string mod_dir; |
2361 | 0 | if (lang == "Fortran") { |
2362 | 0 | mod_dir = this->GeneratorTarget->GetFortranModuleDirectory( |
2363 | 0 | this->Makefile->GetHomeOutputDirectory()); |
2364 | 0 | } else if (lang == "CXX") { |
2365 | 0 | mod_dir = this->GetGlobalGenerator()->ExpandCFGIntDir( |
2366 | 0 | cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory), |
2367 | 0 | config); |
2368 | 0 | } |
2369 | 0 | if (mod_dir.empty()) { |
2370 | 0 | mod_dir = this->Makefile->GetCurrentBinaryDirectory(); |
2371 | 0 | } |
2372 | 0 | tdi["module-dir"] = mod_dir; |
2373 | |
|
2374 | 0 | if (lang == "Fortran") { |
2375 | 0 | tdi["submodule-sep"] = |
2376 | 0 | this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP"); |
2377 | 0 | tdi["submodule-ext"] = |
2378 | 0 | this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT"); |
2379 | 0 | } else if (lang == "CXX") { |
2380 | | // No extra information necessary. |
2381 | 0 | } |
2382 | |
|
2383 | 0 | tdi["dir-cur-bld"] = this->Makefile->GetCurrentBinaryDirectory(); |
2384 | 0 | tdi["dir-cur-src"] = this->Makefile->GetCurrentSourceDirectory(); |
2385 | 0 | tdi["dir-top-bld"] = this->Makefile->GetHomeOutputDirectory(); |
2386 | 0 | tdi["dir-top-src"] = this->Makefile->GetHomeDirectory(); |
2387 | |
|
2388 | 0 | Json::Value& tdi_include_dirs = tdi["include-dirs"] = Json::arrayValue; |
2389 | 0 | std::vector<std::string> includes; |
2390 | 0 | this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, |
2391 | 0 | lang, config); |
2392 | 0 | for (std::string const& i : includes) { |
2393 | | // Convert the include directories the same way we do for -I flags. |
2394 | | // See upstream ninja issue 1251. |
2395 | 0 | tdi_include_dirs.append(this->ConvertToNinjaPath(i)); |
2396 | 0 | } |
2397 | |
|
2398 | 0 | Json::Value& tdi_linked_target_dirs = tdi["linked-target-dirs"] = |
2399 | 0 | Json::arrayValue; |
2400 | 0 | auto const linked_directories = |
2401 | 0 | this->GetLinkedTargetDirectories(lang, config); |
2402 | 0 | for (std::string const& l : linked_directories.Direct) { |
2403 | 0 | tdi_linked_target_dirs.append(l); |
2404 | 0 | } |
2405 | |
|
2406 | 0 | Json::Value& tdi_forward_modules_from_target_dirs = |
2407 | 0 | tdi["forward-modules-from-target-dirs"] = Json::arrayValue; |
2408 | 0 | for (std::string const& l : linked_directories.Forward) { |
2409 | 0 | tdi_forward_modules_from_target_dirs.append(l); |
2410 | 0 | } |
2411 | | |
2412 | | // Record the native target support directory for non-imported synthetic |
2413 | | // targets |
2414 | 0 | if (this->GeneratorTarget->IsSynthetic()) { |
2415 | 0 | if (cmGeneratorTarget* nativeGT = |
2416 | 0 | this->LocalGenerator->FindGeneratorTargetToUse( |
2417 | 0 | this->GeneratorTarget->Target->GetTemplateName())) { |
2418 | 0 | if (!nativeGT->IsImported()) { |
2419 | 0 | std::string nativeDir = nativeGT->GetSupportDirectory(); |
2420 | 0 | if (this->GetGlobalGenerator()->IsMultiConfig()) { |
2421 | 0 | nativeDir = cmStrCat(nativeDir, '/', config); |
2422 | 0 | } |
2423 | 0 | tdi["native-target-dir"] = nativeDir; |
2424 | 0 | } |
2425 | 0 | } |
2426 | 0 | } |
2427 | |
|
2428 | 0 | cmDyndepGeneratorCallbacks cb; |
2429 | 0 | cb.ObjectFilePath = [this](cmSourceFile const* sf, std::string const& cnf) { |
2430 | 0 | return this->GetObjectFilePath(sf, cnf); |
2431 | 0 | }; |
2432 | 0 | cb.BmiFilePath = [this](cmSourceFile const* sf, std::string const& cnf) { |
2433 | 0 | return this->GetBmiFilePath(sf, cnf); |
2434 | 0 | }; |
2435 | |
|
2436 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
2437 | 0 | cmDyndepCollation::AddCollationInformation(tdi, this->GeneratorTarget, |
2438 | 0 | config, cb); |
2439 | 0 | #endif |
2440 | |
|
2441 | 0 | std::string const tdin = this->GetTargetDependInfoPath(lang, config); |
2442 | 0 | cmGeneratedFileStream tdif(tdin); |
2443 | 0 | tdif << tdi; |
2444 | 0 | } |
2445 | | |
2446 | | void cmNinjaTargetGenerator::EmitSwiftDependencyInfo( |
2447 | | cmSourceFile const* source, std::string const& config) |
2448 | 0 | { |
2449 | 0 | std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source); |
2450 | 0 | std::string const objectFilePath = |
2451 | 0 | this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)); |
2452 | 0 | std::string const swiftDepsPath = [source, objectFilePath]() -> std::string { |
2453 | 0 | if (cmValue name = source->GetProperty("Swift_DEPENDENCIES_FILE")) { |
2454 | 0 | return *name; |
2455 | 0 | } |
2456 | 0 | return cmStrCat(objectFilePath, ".swiftdeps"); |
2457 | 0 | }(); |
2458 | 0 | std::string const swiftDiaPath = [source, objectFilePath]() -> std::string { |
2459 | 0 | if (cmValue name = source->GetProperty("Swift_DIAGNOSTICS_FILE")) { |
2460 | 0 | return *name; |
2461 | 0 | } |
2462 | 0 | return cmStrCat(objectFilePath, ".dia"); |
2463 | 0 | }(); |
2464 | 0 | std::string const makeDepsPath = [this, source, config]() -> std::string { |
2465 | 0 | cmLocalNinjaGenerator const* local = this->GetLocalGenerator(); |
2466 | 0 | std::string const objectFileName = |
2467 | 0 | this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)); |
2468 | 0 | std::string const objectFileDir = |
2469 | 0 | cmSystemTools::GetFilenamePath(objectFileName); |
2470 | |
|
2471 | 0 | if (this->Makefile->IsOn("CMAKE_Swift_DEPFLE_EXTNSION_REPLACE")) { |
2472 | 0 | std::string dependFileName = cmStrCat( |
2473 | 0 | cmSystemTools::GetFilenameWithoutLastExtension(objectFileName), ".d"); |
2474 | 0 | return local->ConvertToOutputFormat( |
2475 | 0 | cmStrCat(objectFileDir, '/', dependFileName), |
2476 | 0 | cmOutputConverter::SHELL); |
2477 | 0 | } |
2478 | 0 | return local->ConvertToOutputFormat(cmStrCat(objectFileName, ".d"), |
2479 | 0 | cmOutputConverter::SHELL); |
2480 | 0 | }(); |
2481 | | |
2482 | | // build the source file mapping |
2483 | | // https://github.com/apple/swift/blob/master/docs/Driver.md#output-file-maps |
2484 | 0 | Json::Value entry = Json::Value(Json::objectValue); |
2485 | 0 | entry["object"] = objectFilePath; |
2486 | 0 | entry["dependencies"] = makeDepsPath; |
2487 | 0 | entry["swift-dependencies"] = swiftDepsPath; |
2488 | 0 | entry["diagnostics"] = swiftDiaPath; |
2489 | 0 | this->Configs[config].SwiftOutputMap[sourceFilePath] = entry; |
2490 | 0 | } |
2491 | | |
2492 | | void cmNinjaTargetGenerator::ExportObjectCompileCommand( |
2493 | | std::string const& language, std::string const& sourceFileName, |
2494 | | std::string const& objectDir, std::string const& targetSupportDir, |
2495 | | std::string const& objectFileName, std::string const& objectFileDir, |
2496 | | std::string const& flags, std::string const& defines, |
2497 | | std::string const& includes, std::string const& targetCompilePdb, |
2498 | | std::string const& targetPdb, std::string const& outputConfig, |
2499 | | WithScanning withScanning) |
2500 | 0 | { |
2501 | 0 | if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) { |
2502 | 0 | return; |
2503 | 0 | } |
2504 | | |
2505 | 0 | cmRulePlaceholderExpander::RuleVariables compileObjectVars; |
2506 | 0 | compileObjectVars.Language = language.c_str(); |
2507 | |
|
2508 | 0 | std::string escapedSourceFileName = sourceFileName; |
2509 | |
|
2510 | 0 | if (!cmSystemTools::FileIsFullPath(sourceFileName)) { |
2511 | 0 | escapedSourceFileName = |
2512 | 0 | cmSystemTools::CollapseFullPath(escapedSourceFileName, |
2513 | 0 | this->GetGlobalGenerator() |
2514 | 0 | ->GetCMakeInstance() |
2515 | 0 | ->GetHomeOutputDirectory()); |
2516 | 0 | } |
2517 | |
|
2518 | 0 | escapedSourceFileName = this->LocalGenerator->ConvertToOutputFormat( |
2519 | 0 | escapedSourceFileName, cmOutputConverter::SHELL); |
2520 | |
|
2521 | 0 | std::string fullFlags = flags; |
2522 | 0 | if (withScanning == WithScanning::Yes) { |
2523 | 0 | std::string const modmapFormatVar = |
2524 | 0 | cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT"); |
2525 | 0 | std::string const modmapFormat = |
2526 | 0 | this->Makefile->GetSafeDefinition(modmapFormatVar); |
2527 | 0 | if (!modmapFormat.empty()) { |
2528 | 0 | std::string modmapFlags = this->GetMakefile()->GetRequiredDefinition( |
2529 | 0 | cmStrCat("CMAKE_", language, "_MODULE_MAP_FLAG")); |
2530 | | // XXX(modmap): If changing this path construction, change |
2531 | | // `cmGlobalNinjaGenerator::WriteDyndep` and |
2532 | | // `cmNinjaTargetGenerator::WriteObjectBuildStatement` to expect the |
2533 | | // corresponding file path. |
2534 | 0 | cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>", |
2535 | 0 | cmStrCat(objectFileName, ".modmap")); |
2536 | 0 | fullFlags += cmStrCat(' ', modmapFlags); |
2537 | 0 | } |
2538 | 0 | } |
2539 | |
|
2540 | 0 | compileObjectVars.Source = escapedSourceFileName.c_str(); |
2541 | 0 | std::string escapedObjectFileName = |
2542 | 0 | this->LocalGenerator->ConvertToOutputFormat(objectFileName, |
2543 | 0 | cmOutputConverter::SHELL); |
2544 | 0 | compileObjectVars.Object = escapedObjectFileName.c_str(); |
2545 | 0 | compileObjectVars.ObjectDir = objectDir.c_str(); |
2546 | 0 | compileObjectVars.TargetSupportDir = targetSupportDir.c_str(); |
2547 | 0 | compileObjectVars.ObjectFileDir = objectFileDir.c_str(); |
2548 | 0 | compileObjectVars.Flags = fullFlags.c_str(); |
2549 | 0 | compileObjectVars.Defines = defines.c_str(); |
2550 | 0 | compileObjectVars.Includes = includes.c_str(); |
2551 | 0 | compileObjectVars.TargetCompilePDB = targetCompilePdb.c_str(); |
2552 | 0 | compileObjectVars.TargetPDB = targetPdb.c_str(); |
2553 | | |
2554 | | // Rule for compiling object file. |
2555 | 0 | std::string cudaCompileMode; |
2556 | 0 | if (language == "CUDA") { |
2557 | 0 | if (this->GeneratorTarget->GetPropertyAsBool( |
2558 | 0 | "CUDA_SEPARABLE_COMPILATION")) { |
2559 | 0 | std::string const& rdcFlag = |
2560 | 0 | this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG"); |
2561 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, ' '); |
2562 | 0 | } |
2563 | 0 | static std::array<cm::string_view, 4> const compileModes{ |
2564 | 0 | { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s } |
2565 | 0 | }; |
2566 | 0 | bool useNormalCompileMode = true; |
2567 | 0 | for (cm::string_view mode : compileModes) { |
2568 | 0 | auto propName = cmStrCat("CUDA_", mode, "_COMPILATION"); |
2569 | 0 | auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG"); |
2570 | 0 | if (this->GeneratorTarget->GetPropertyAsBool(propName)) { |
2571 | 0 | std::string const& flag = |
2572 | 0 | this->Makefile->GetRequiredDefinition(defName); |
2573 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, flag); |
2574 | 0 | useNormalCompileMode = false; |
2575 | 0 | break; |
2576 | 0 | } |
2577 | 0 | } |
2578 | 0 | if (useNormalCompileMode) { |
2579 | 0 | std::string const& wholeFlag = |
2580 | 0 | this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG"); |
2581 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag); |
2582 | 0 | } |
2583 | 0 | compileObjectVars.CudaCompileMode = cudaCompileMode.c_str(); |
2584 | 0 | } |
2585 | |
|
2586 | 0 | std::string const cmdVar = this->GetCompileTemplateVar(language); |
2587 | 0 | std::string const& compileCmd = |
2588 | 0 | this->Makefile->GetRequiredDefinition(cmdVar); |
2589 | 0 | cmList compileCmds(compileCmd); |
2590 | |
|
2591 | 0 | auto rulePlaceholderExpander = |
2592 | 0 | this->GetLocalGenerator()->CreateRulePlaceholderExpander(); |
2593 | |
|
2594 | 0 | for (auto& i : compileCmds) { |
2595 | | // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS |
2596 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), i, |
2597 | 0 | compileObjectVars); |
2598 | 0 | } |
2599 | |
|
2600 | 0 | std::string cmdLine = this->GetLocalGenerator()->BuildCommandLine( |
2601 | 0 | compileCmds, outputConfig, outputConfig); |
2602 | |
|
2603 | 0 | this->GetGlobalGenerator()->AddCXXCompileCommand(cmdLine, sourceFileName, |
2604 | 0 | objectFileName); |
2605 | 0 | } |
2606 | | |
2607 | | void cmNinjaTargetGenerator::ExportSwiftObjectCompileCommand( |
2608 | | std::vector<cmSourceFile const*> const& moduleSourceFiles, |
2609 | | std::string const& moduleObjectFilename, std::string const& flags, |
2610 | | std::string const& defines, std::string const& includes, |
2611 | | std::string const& outputConfig, bool singleOutput) |
2612 | 0 | { |
2613 | 0 | if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) { |
2614 | 0 | return; |
2615 | 0 | } |
2616 | | |
2617 | 0 | auto escapeSourceFileName = [this](std::string srcFilename) -> std::string { |
2618 | 0 | if (!cmSystemTools::FileIsFullPath(srcFilename)) { |
2619 | 0 | srcFilename = |
2620 | 0 | cmSystemTools::CollapseFullPath(srcFilename, |
2621 | 0 | this->GetGlobalGenerator() |
2622 | 0 | ->GetCMakeInstance() |
2623 | 0 | ->GetHomeOutputDirectory()); |
2624 | 0 | } |
2625 | |
|
2626 | 0 | return this->LocalGenerator->ConvertToOutputFormat( |
2627 | 0 | srcFilename, cmOutputConverter::SHELL); |
2628 | 0 | }; |
2629 | 0 | auto escapedModuleObjectFilename = |
2630 | 0 | this->ConvertToNinjaPath(moduleObjectFilename); |
2631 | |
|
2632 | 0 | cmRulePlaceholderExpander::RuleVariables compileObjectVars; |
2633 | 0 | compileObjectVars.Language = "Swift"; |
2634 | 0 | compileObjectVars.Flags = flags.c_str(); |
2635 | 0 | compileObjectVars.Defines = defines.c_str(); |
2636 | 0 | compileObjectVars.Includes = includes.c_str(); |
2637 | | |
2638 | | // Build up the list of source files in the module |
2639 | 0 | std::vector<std::string> filenames; |
2640 | 0 | filenames.reserve(moduleSourceFiles.size()); |
2641 | 0 | for (cmSourceFile const* sf : moduleSourceFiles) { |
2642 | 0 | filenames.emplace_back( |
2643 | 0 | escapeSourceFileName(this->GetCompiledSourceNinjaPath(sf))); |
2644 | 0 | } |
2645 | | // Note that `escapedSourceFilenames` must remain alive until the |
2646 | | // compileObjectVars is consumed or Source will be a dangling pointer. |
2647 | 0 | std::string const escapedSourceFilenames = cmJoin(filenames, " "); |
2648 | 0 | compileObjectVars.Source = escapedSourceFilenames.c_str(); |
2649 | |
|
2650 | 0 | std::string const& compileCommand = |
2651 | 0 | this->Makefile->GetRequiredDefinition("CMAKE_Swift_COMPILE_OBJECT"); |
2652 | 0 | cmList compileCmds(compileCommand); |
2653 | |
|
2654 | 0 | auto rulePlaceholderExpander = |
2655 | 0 | this->GetLocalGenerator()->CreateRulePlaceholderExpander(); |
2656 | |
|
2657 | 0 | for (cmSourceFile const* sf : moduleSourceFiles) { |
2658 | 0 | std::string const sourceFilename = this->GetCompiledSourceNinjaPath(sf); |
2659 | 0 | std::string objectFilename = escapedModuleObjectFilename; |
2660 | |
|
2661 | 0 | if (!singleOutput) { |
2662 | | // If it's not single-output, each source file gets a separate object |
2663 | 0 | objectFilename = |
2664 | 0 | this->ConvertToNinjaPath(this->GetObjectFilePath(sf, outputConfig)); |
2665 | 0 | } |
2666 | 0 | compileObjectVars.Objects = objectFilename.c_str(); |
2667 | |
|
2668 | 0 | for (std::string& cmd : compileCmds) { |
2669 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), |
2670 | 0 | cmd, compileObjectVars); |
2671 | 0 | } |
2672 | |
|
2673 | 0 | std::string commandLine = this->GetLocalGenerator()->BuildCommandLine( |
2674 | 0 | compileCmds, outputConfig, outputConfig); |
2675 | |
|
2676 | 0 | this->GetGlobalGenerator()->AddCXXCompileCommand( |
2677 | 0 | commandLine, sourceFilename, objectFilename); |
2678 | 0 | } |
2679 | 0 | } |
2680 | | |
2681 | | void cmNinjaTargetGenerator::AdditionalCleanFiles(std::string const& config) |
2682 | 0 | { |
2683 | 0 | if (cmValue prop_value = |
2684 | 0 | this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) { |
2685 | 0 | cmLocalNinjaGenerator* lg = this->LocalGenerator; |
2686 | 0 | cmList cleanFiles(cmGeneratorExpression::Evaluate(*prop_value, lg, config, |
2687 | 0 | this->GeneratorTarget)); |
2688 | 0 | std::string const& binaryDir = lg->GetCurrentBinaryDirectory(); |
2689 | 0 | cmGlobalNinjaGenerator* gg = lg->GetGlobalNinjaGenerator(); |
2690 | 0 | for (auto const& cleanFile : cleanFiles) { |
2691 | | // Support relative paths |
2692 | 0 | gg->AddAdditionalCleanFile( |
2693 | 0 | cmSystemTools::CollapseFullPath(cleanFile, binaryDir), config); |
2694 | 0 | } |
2695 | 0 | } |
2696 | 0 | } |
2697 | | |
2698 | | cmNinjaDeps cmNinjaTargetGenerator::GetObjects(std::string const& config) const |
2699 | 0 | { |
2700 | 0 | auto const it = this->Configs.find(config); |
2701 | 0 | if (it != this->Configs.end()) { |
2702 | 0 | return it->second.Objects; |
2703 | 0 | } |
2704 | 0 | return {}; |
2705 | 0 | } |
2706 | | |
2707 | | std::string cmNinjaTargetGenerator::GetSwiftModuleOutput( |
2708 | | std::string const& config) const |
2709 | 0 | { |
2710 | 0 | auto const it = this->Configs.find(config); |
2711 | 0 | if (it != this->Configs.end()) { |
2712 | 0 | return it->second.SwiftModuleOutput; |
2713 | 0 | } |
2714 | 0 | return {}; |
2715 | 0 | } |
2716 | | |
2717 | | void cmNinjaTargetGenerator::EnsureDirectoryExists( |
2718 | | std::string const& path) const |
2719 | 0 | { |
2720 | 0 | if (cmSystemTools::FileIsFullPath(path)) { |
2721 | 0 | cmSystemTools::MakeDirectory(path); |
2722 | 0 | } else { |
2723 | 0 | cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator(); |
2724 | 0 | std::string fullPath = gg->GetCMakeInstance()->GetHomeOutputDirectory(); |
2725 | | // Also ensures there is a trailing slash. |
2726 | 0 | gg->StripNinjaOutputPathPrefixAsSuffix(fullPath); |
2727 | 0 | fullPath += path; |
2728 | 0 | cmSystemTools::MakeDirectory(fullPath); |
2729 | 0 | } |
2730 | 0 | } |
2731 | | |
2732 | | void cmNinjaTargetGenerator::EnsureParentDirectoryExists( |
2733 | | std::string const& path) const |
2734 | 0 | { |
2735 | 0 | this->EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path)); |
2736 | 0 | } |
2737 | | |
2738 | | void cmNinjaTargetGenerator::MacOSXContentGeneratorType::operator()( |
2739 | | cmSourceFile const& source, char const* pkgloc, std::string const& config) |
2740 | 0 | { |
2741 | | // Skip OS X content when not building a Framework or Bundle. |
2742 | 0 | if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) { |
2743 | 0 | return; |
2744 | 0 | } |
2745 | | |
2746 | 0 | std::string macdir = |
2747 | 0 | this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc, |
2748 | 0 | config); |
2749 | | |
2750 | | // Reject files that collide with files from the Ninja file's native config. |
2751 | 0 | if (config != this->FileConfig) { |
2752 | 0 | std::string nativeMacdir = |
2753 | 0 | this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory( |
2754 | 0 | pkgloc, this->FileConfig); |
2755 | 0 | if (macdir == nativeMacdir) { |
2756 | 0 | return; |
2757 | 0 | } |
2758 | 0 | } |
2759 | | |
2760 | | // Get the input file location. |
2761 | 0 | std::string input = source.GetFullPath(); |
2762 | 0 | input = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(input); |
2763 | | |
2764 | | // Get the output file location. |
2765 | 0 | std::string output = |
2766 | 0 | cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input)); |
2767 | 0 | output = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(output); |
2768 | | |
2769 | | // Write a build statement to copy the content into the bundle. |
2770 | 0 | this->Generator->GetGlobalGenerator()->WriteMacOSXContentBuild( |
2771 | 0 | input, output, this->FileConfig); |
2772 | | |
2773 | | // Add as a dependency to the target so that it gets called. |
2774 | 0 | this->Generator->Configs[config].ExtraFiles.push_back(std::move(output)); |
2775 | 0 | } |
2776 | | |
2777 | | void cmNinjaTargetGenerator::AddDepfileBinding(cmNinjaVars& vars, |
2778 | | std::string depfile) const |
2779 | 0 | { |
2780 | 0 | std::string depfileForShell = this->ConvertToOutputFormatForShell(depfile); |
2781 | 0 | if (depfile != depfileForShell) { |
2782 | 0 | vars["depfile"] = std::move(depfile); |
2783 | 0 | } |
2784 | 0 | vars["DEP_FILE"] = std::move(depfileForShell); |
2785 | 0 | } |
2786 | | |
2787 | | void cmNinjaTargetGenerator::RemoveDepfileBinding(cmNinjaVars& vars) const |
2788 | 0 | { |
2789 | 0 | vars.erase("DEP_FILE"); |
2790 | 0 | vars.erase("depfile"); |
2791 | 0 | } |
2792 | | |
2793 | | void cmNinjaTargetGenerator::addPoolNinjaVariable( |
2794 | | std::string const& pool_property, std::string const& config, |
2795 | | cmGeneratorTarget* target, cmSourceFile const* source, cmNinjaVars& vars) |
2796 | 0 | { |
2797 | | // First check file set properties, then if not found the current source |
2798 | | // properties, then if not found, its target ones. Allows to override a |
2799 | | // target-wide compile pool with file set-specific or source-specific one. |
2800 | 0 | cmValue pool = {}; |
2801 | 0 | if (source) { |
2802 | 0 | cmGeneratorFileSet const* fileSet = |
2803 | 0 | target->GetFileSetForSource(config, source); |
2804 | 0 | if (fileSet) { |
2805 | 0 | pool = fileSet->GetProperty(pool_property); |
2806 | 0 | } |
2807 | 0 | if (!pool) { |
2808 | 0 | pool = source->GetProperty(pool_property); |
2809 | 0 | } |
2810 | 0 | } |
2811 | 0 | if (!pool) { |
2812 | 0 | pool = target->GetProperty(pool_property); |
2813 | 0 | } |
2814 | 0 | if (pool) { |
2815 | 0 | vars["pool"] = *pool; |
2816 | 0 | } |
2817 | 0 | } |
2818 | | |
2819 | | bool cmNinjaTargetGenerator::ForceResponseFile() |
2820 | 0 | { |
2821 | 0 | static std::string const forceRspFile = "CMAKE_NINJA_FORCE_RESPONSE_FILE"; |
2822 | 0 | return (this->GetMakefile()->IsDefinitionSet(forceRspFile) || |
2823 | 0 | cmSystemTools::HasEnv(forceRspFile)); |
2824 | 0 | } |