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