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