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