1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmNinjaTargetGenerator.h"
4
5 #include <algorithm>
6 #include <cassert>
7 #include <iterator>
8 #include <map>
9 #include <ostream>
10 #include <unordered_map>
11 #include <unordered_set>
12 #include <utility>
13
14 #include <cm/memory>
15 #include <cm/string_view>
16 #include <cmext/algorithm>
17 #include <cmext/string_view>
18
19 #include <cm3p/json/value.h>
20 #include <cm3p/json/writer.h>
21
22 #include "cmComputeLinkInformation.h"
23 #include "cmCustomCommandGenerator.h"
24 #include "cmGeneratedFileStream.h"
25 #include "cmGeneratorExpression.h"
26 #include "cmGeneratorTarget.h"
27 #include "cmGlobalNinjaGenerator.h"
28 #include "cmLocalGenerator.h"
29 #include "cmLocalNinjaGenerator.h"
30 #include "cmMakefile.h"
31 #include "cmNinjaNormalTargetGenerator.h"
32 #include "cmNinjaUtilityTargetGenerator.h"
33 #include "cmOutputConverter.h"
34 #include "cmRange.h"
35 #include "cmRulePlaceholderExpander.h"
36 #include "cmSourceFile.h"
37 #include "cmStandardLevelResolver.h"
38 #include "cmState.h"
39 #include "cmStateTypes.h"
40 #include "cmStringAlgorithms.h"
41 #include "cmSystemTools.h"
42 #include "cmValue.h"
43 #include "cmake.h"
44
New(cmGeneratorTarget * target)45 std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New(
46 cmGeneratorTarget* target)
47 {
48 switch (target->GetType()) {
49 case cmStateEnums::EXECUTABLE:
50 case cmStateEnums::SHARED_LIBRARY:
51 case cmStateEnums::STATIC_LIBRARY:
52 case cmStateEnums::MODULE_LIBRARY:
53 case cmStateEnums::OBJECT_LIBRARY:
54 return cm::make_unique<cmNinjaNormalTargetGenerator>(target);
55
56 case cmStateEnums::UTILITY:
57 case cmStateEnums::INTERFACE_LIBRARY:
58 case cmStateEnums::GLOBAL_TARGET:
59 return cm::make_unique<cmNinjaUtilityTargetGenerator>(target);
60
61 default:
62 return std::unique_ptr<cmNinjaTargetGenerator>();
63 }
64 }
65
cmNinjaTargetGenerator(cmGeneratorTarget * target)66 cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmGeneratorTarget* target)
67 : cmCommonTargetGenerator(target)
68 , OSXBundleGenerator(nullptr)
69 , LocalGenerator(
70 static_cast<cmLocalNinjaGenerator*>(target->GetLocalGenerator()))
71 {
72 for (auto const& fileConfig :
73 target->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) {
74 this->Configs[fileConfig].MacOSXContentGenerator =
75 cm::make_unique<MacOSXContentGeneratorType>(this, fileConfig);
76 }
77 }
78
79 cmNinjaTargetGenerator::~cmNinjaTargetGenerator() = default;
80
GetImplFileStream(const std::string & config) const81 cmGeneratedFileStream& cmNinjaTargetGenerator::GetImplFileStream(
82 const std::string& config) const
83 {
84 return *this->GetGlobalGenerator()->GetImplFileStream(config);
85 }
86
GetCommonFileStream() const87 cmGeneratedFileStream& cmNinjaTargetGenerator::GetCommonFileStream() const
88 {
89 return *this->GetGlobalGenerator()->GetCommonFileStream();
90 }
91
GetRulesFileStream() const92 cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const
93 {
94 return *this->GetGlobalGenerator()->GetRulesFileStream();
95 }
96
GetGlobalGenerator() const97 cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const
98 {
99 return this->LocalGenerator->GetGlobalNinjaGenerator();
100 }
101
LanguageCompilerRule(const std::string & lang,const std::string & config) const102 std::string cmNinjaTargetGenerator::LanguageCompilerRule(
103 const std::string& lang, const std::string& config) const
104 {
105 return cmStrCat(
106 lang, "_COMPILER__",
107 cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
108 '_', config);
109 }
110
LanguagePreprocessAndScanRule(std::string const & lang,const std::string & config) const111 std::string cmNinjaTargetGenerator::LanguagePreprocessAndScanRule(
112 std::string const& lang, const std::string& config) const
113 {
114 return cmStrCat(
115 lang, "_PREPROCESS_SCAN__",
116 cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
117 '_', config);
118 }
119
LanguageScanRule(std::string const & lang,const std::string & config) const120 std::string cmNinjaTargetGenerator::LanguageScanRule(
121 std::string const& lang, const std::string& config) const
122 {
123 return cmStrCat(
124 lang, "_SCAN__",
125 cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
126 '_', config);
127 }
128
NeedExplicitPreprocessing(std::string const & lang) const129 bool cmNinjaTargetGenerator::NeedExplicitPreprocessing(
130 std::string const& lang) const
131 {
132 return lang == "Fortran";
133 }
134
CompileWithDefines(std::string const & lang) const135 bool cmNinjaTargetGenerator::CompileWithDefines(std::string const& lang) const
136 {
137 return this->Makefile->IsOn(
138 cmStrCat("CMAKE_", lang, "_COMPILE_WITH_DEFINES"));
139 }
140
LanguageDyndepRule(const std::string & lang,const std::string & config) const141 std::string cmNinjaTargetGenerator::LanguageDyndepRule(
142 const std::string& lang, const std::string& config) const
143 {
144 return cmStrCat(
145 lang, "_DYNDEP__",
146 cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
147 '_', config);
148 }
149
NeedCxxModuleSupport(std::string const & lang,std::string const & config) const150 bool cmNinjaTargetGenerator::NeedCxxModuleSupport(
151 std::string const& lang, std::string const& config) const
152 {
153 if (lang != "CXX") {
154 return false;
155 }
156 if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) {
157 return false;
158 }
159 cmGeneratorTarget const* tgt = this->GetGeneratorTarget();
160 cmStandardLevelResolver standardResolver(this->Makefile);
161 bool const uses_cxx20 =
162 standardResolver.HaveStandardAvailable(tgt, "CXX", config, "cxx_std_20");
163 return uses_cxx20 && this->GetGlobalGenerator()->CheckCxxModuleSupport();
164 }
165
NeedDyndep(std::string const & lang,std::string const & config) const166 bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang,
167 std::string const& config) const
168 {
169 return lang == "Fortran" || this->NeedCxxModuleSupport(lang, config);
170 }
171
OrderDependsTargetForTarget(const std::string & config)172 std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget(
173 const std::string& config)
174 {
175 return this->GetGlobalGenerator()->OrderDependsTargetForTarget(
176 this->GeneratorTarget, config);
177 }
178
179 // TODO: Most of the code is picked up from
180 // void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink),
181 // void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
182 // Refactor it.
ComputeFlagsForObject(cmSourceFile const * source,const std::string & language,const std::string & config)183 std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
184 cmSourceFile const* source, const std::string& language,
185 const std::string& config)
186 {
187 std::vector<std::string> architectures;
188 std::unordered_map<std::string, std::string> pchSources;
189 this->GeneratorTarget->GetAppleArchs(config, architectures);
190 if (architectures.empty()) {
191 architectures.emplace_back();
192 }
193
194 std::string filterArch;
195 for (const std::string& arch : architectures) {
196 const std::string pchSource =
197 this->GeneratorTarget->GetPchSource(config, language, arch);
198 if (pchSource == source->GetFullPath()) {
199 filterArch = arch;
200 }
201 if (!pchSource.empty()) {
202 pchSources.insert(std::make_pair(pchSource, arch));
203 }
204 }
205
206 std::string flags;
207 // Explicitly add the explicit language flag before any other flag
208 // so user flags can override it.
209 this->GeneratorTarget->AddExplicitLanguageFlags(flags, *source);
210
211 if (!flags.empty()) {
212 flags += " ";
213 }
214 flags += this->GetFlags(language, config, filterArch);
215
216 // Add Fortran format flags.
217 if (language == "Fortran") {
218 this->AppendFortranFormatFlags(flags, *source);
219 this->AppendFortranPreprocessFlags(flags, *source);
220 }
221
222 // Add source file specific flags.
223 cmGeneratorExpressionInterpreter genexInterpreter(
224 this->LocalGenerator, config, this->GeneratorTarget, language);
225
226 const std::string COMPILE_FLAGS("COMPILE_FLAGS");
227 if (cmValue cflags = source->GetProperty(COMPILE_FLAGS)) {
228 this->LocalGenerator->AppendFlags(
229 flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS));
230 }
231
232 const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
233 if (cmValue coptions = source->GetProperty(COMPILE_OPTIONS)) {
234 this->LocalGenerator->AppendCompileOptions(
235 flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
236 }
237
238 // Add precompile headers compile options.
239 if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
240 std::string pchOptions;
241 auto pchIt = pchSources.find(source->GetFullPath());
242 if (pchIt != pchSources.end()) {
243 pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions(
244 config, language, pchIt->second);
245 } else {
246 pchOptions =
247 this->GeneratorTarget->GetPchUseCompileOptions(config, language);
248 }
249
250 this->LocalGenerator->AppendCompileOptions(
251 flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
252 }
253
254 return flags;
255 }
256
AddIncludeFlags(std::string & languageFlags,std::string const & language,const std::string & config)257 void cmNinjaTargetGenerator::AddIncludeFlags(std::string& languageFlags,
258 std::string const& language,
259 const std::string& config)
260 {
261 std::vector<std::string> includes;
262 this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
263 language, config);
264 // Add include directory flags.
265 std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
266 includes, this->GeneratorTarget, language, config, false,
267 // full include paths for RC needed by cmcldeps
268 language == "RC" ? cmLocalGenerator::IncludePathStyle::Absolute
269 : cmLocalGenerator::IncludePathStyle::Default);
270 if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
271 std::replace(includeFlags.begin(), includeFlags.end(), '\\', '/');
272 }
273
274 this->LocalGenerator->AppendFlags(languageFlags, includeFlags);
275 }
276
277 // TODO: Refactor with
278 // void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
ComputeDefines(cmSourceFile const * source,const std::string & language,const std::string & config)279 std::string cmNinjaTargetGenerator::ComputeDefines(cmSourceFile const* source,
280 const std::string& language,
281 const std::string& config)
282 {
283 std::set<std::string> defines;
284 cmGeneratorExpressionInterpreter genexInterpreter(
285 this->LocalGenerator, config, this->GeneratorTarget, language);
286
287 // Seriously??
288 if (this->GetGlobalGenerator()->IsMultiConfig()) {
289 defines.insert(cmStrCat("CMAKE_INTDIR=\"", config, '"'));
290 }
291
292 const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
293 if (cmValue compile_defs = source->GetProperty(COMPILE_DEFINITIONS)) {
294 this->LocalGenerator->AppendDefines(
295 defines, genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS));
296 }
297
298 std::string defPropName =
299 cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config));
300 if (cmValue config_compile_defs = source->GetProperty(defPropName)) {
301 this->LocalGenerator->AppendDefines(
302 defines,
303 genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS));
304 }
305
306 std::string definesString = this->GetDefines(language, config);
307 this->LocalGenerator->JoinDefines(defines, definesString, language);
308
309 return definesString;
310 }
311
ComputeIncludes(cmSourceFile const * source,const std::string & language,const std::string & config)312 std::string cmNinjaTargetGenerator::ComputeIncludes(
313 cmSourceFile const* source, const std::string& language,
314 const std::string& config)
315 {
316 std::vector<std::string> includes;
317 cmGeneratorExpressionInterpreter genexInterpreter(
318 this->LocalGenerator, config, this->GeneratorTarget, language);
319
320 const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
321 if (cmValue cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) {
322 this->LocalGenerator->AppendIncludeDirectories(
323 includes, genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES),
324 *source);
325 }
326
327 std::string includesString = this->LocalGenerator->GetIncludeFlags(
328 includes, this->GeneratorTarget, language, config, false,
329 cmLocalGenerator::IncludePathStyle::Absolute);
330 this->LocalGenerator->AppendFlags(includesString,
331 this->GetIncludes(language, config));
332
333 return includesString;
334 }
335
ComputeLinkDeps(const std::string & linkLanguage,const std::string & config,bool ignoreType) const336 cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps(
337 const std::string& linkLanguage, const std::string& config,
338 bool ignoreType) const
339 {
340 // Static libraries never depend on other targets for linking.
341 if (!ignoreType &&
342 (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
343 this->GeneratorTarget->GetType() == cmStateEnums::OBJECT_LIBRARY)) {
344 return cmNinjaDeps();
345 }
346
347 cmComputeLinkInformation* cli =
348 this->GeneratorTarget->GetLinkInformation(config);
349 if (!cli) {
350 return cmNinjaDeps();
351 }
352
353 const std::vector<std::string>& deps = cli->GetDepends();
354 cmNinjaDeps result(deps.size());
355 std::transform(deps.begin(), deps.end(), result.begin(),
356 this->MapToNinjaPath());
357
358 // Add a dependency on the link definitions file, if any.
359 if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
360 this->GeneratorTarget->GetModuleDefinitionInfo(config)) {
361 for (cmSourceFile const* src : mdi->Sources) {
362 result.push_back(this->ConvertToNinjaPath(src->GetFullPath()));
363 }
364 }
365
366 // Add a dependency on user-specified manifest files, if any.
367 std::vector<cmSourceFile const*> manifest_srcs;
368 this->GeneratorTarget->GetManifests(manifest_srcs, config);
369 for (cmSourceFile const* manifest_src : manifest_srcs) {
370 result.push_back(this->ConvertToNinjaPath(manifest_src->GetFullPath()));
371 }
372
373 // Add user-specified dependencies.
374 std::vector<std::string> linkDeps;
375 this->GeneratorTarget->GetLinkDepends(linkDeps, config, linkLanguage);
376 std::transform(linkDeps.begin(), linkDeps.end(), std::back_inserter(result),
377 this->MapToNinjaPath());
378
379 return result;
380 }
381
GetCompiledSourceNinjaPath(cmSourceFile const * source) const382 std::string cmNinjaTargetGenerator::GetCompiledSourceNinjaPath(
383 cmSourceFile const* source) const
384 {
385 // Pass source files to the compiler by absolute path.
386 return this->ConvertToNinjaAbsPath(source->GetFullPath());
387 }
388
GetObjectFilePath(cmSourceFile const * source,const std::string & config) const389 std::string cmNinjaTargetGenerator::GetObjectFilePath(
390 cmSourceFile const* source, const std::string& config) const
391 {
392 std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
393 if (!path.empty()) {
394 path += '/';
395 }
396 std::string const& objectName = this->GeneratorTarget->GetObjectName(source);
397 path += cmStrCat(
398 this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
399 this->GetGlobalGenerator()->ConfigDirectory(config), '/', objectName);
400 return path;
401 }
402
GetPreprocessedFilePath(cmSourceFile const * source,const std::string & config) const403 std::string cmNinjaTargetGenerator::GetPreprocessedFilePath(
404 cmSourceFile const* source, const std::string& config) const
405 {
406 // Choose an extension to compile already-preprocessed source.
407 std::string ppExt = source->GetExtension();
408 if (cmHasLiteralPrefix(ppExt, "F")) {
409 // Some Fortran compilers automatically enable preprocessing for
410 // upper-case extensions. Since the source is already preprocessed,
411 // use a lower-case extension.
412 ppExt = cmSystemTools::LowerCase(ppExt);
413 }
414 if (ppExt == "fpp") {
415 // Some Fortran compilers automatically enable preprocessing for
416 // the ".fpp" extension. Since the source is already preprocessed,
417 // use the ".f" extension.
418 ppExt = "f";
419 }
420
421 // Take the object file name and replace the extension.
422 std::string const& objName = this->GeneratorTarget->GetObjectName(source);
423 std::string const& objExt =
424 this->GetGlobalGenerator()->GetLanguageOutputExtension(*source);
425 assert(objName.size() >= objExt.size());
426 std::string const ppName =
427 cmStrCat(objName.substr(0, objName.size() - objExt.size()), "-pp.", ppExt);
428
429 std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
430 if (!path.empty()) {
431 path += '/';
432 }
433 path +=
434 cmStrCat(this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
435 this->GetGlobalGenerator()->ConfigDirectory(config), '/', ppName);
436 return path;
437 }
438
GetDyndepFilePath(std::string const & lang,const std::string & config) const439 std::string cmNinjaTargetGenerator::GetDyndepFilePath(
440 std::string const& lang, const std::string& config) const
441 {
442 std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
443 if (!path.empty()) {
444 path += '/';
445 }
446 path += cmStrCat(
447 this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
448 this->GetGlobalGenerator()->ConfigDirectory(config), '/', lang, ".dd");
449 return path;
450 }
451
GetTargetDependInfoPath(std::string const & lang,const std::string & config) const452 std::string cmNinjaTargetGenerator::GetTargetDependInfoPath(
453 std::string const& lang, const std::string& config) const
454 {
455 std::string path =
456 cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/',
457 this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
458 this->GetGlobalGenerator()->ConfigDirectory(config), '/', lang,
459 "DependInfo.json");
460 return path;
461 }
462
GetTargetOutputDir(const std::string & config) const463 std::string cmNinjaTargetGenerator::GetTargetOutputDir(
464 const std::string& config) const
465 {
466 std::string dir = this->GeneratorTarget->GetDirectory(config);
467 return this->ConvertToNinjaPath(dir);
468 }
469
GetTargetFilePath(const std::string & name,const std::string & config) const470 std::string cmNinjaTargetGenerator::GetTargetFilePath(
471 const std::string& name, const std::string& config) const
472 {
473 std::string path = this->GetTargetOutputDir(config);
474 if (path.empty() || path == ".") {
475 return name;
476 }
477 path += cmStrCat('/', name);
478 return path;
479 }
480
GetTargetName() const481 std::string cmNinjaTargetGenerator::GetTargetName() const
482 {
483 return this->GeneratorTarget->GetName();
484 }
485
SetMsvcTargetPdbVariable(cmNinjaVars & vars,const std::string & config) const486 bool cmNinjaTargetGenerator::SetMsvcTargetPdbVariable(
487 cmNinjaVars& vars, const std::string& config) const
488 {
489 cmMakefile* mf = this->GetMakefile();
490 if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") ||
491 mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID") ||
492 mf->GetDefinition("MSVC_CUDA_ARCHITECTURE_ID")) {
493 std::string pdbPath;
494 std::string compilePdbPath = this->ComputeTargetCompilePDB(config);
495 if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE ||
496 this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
497 this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
498 this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
499 pdbPath = cmStrCat(this->GeneratorTarget->GetPDBDirectory(config), '/',
500 this->GeneratorTarget->GetPDBName(config));
501 }
502
503 vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
504 this->ConvertToNinjaPath(pdbPath), cmOutputConverter::SHELL);
505 vars["TARGET_COMPILE_PDB"] =
506 this->GetLocalGenerator()->ConvertToOutputFormat(
507 this->ConvertToNinjaPath(compilePdbPath), cmOutputConverter::SHELL);
508
509 this->EnsureParentDirectoryExists(pdbPath);
510 this->EnsureParentDirectoryExists(compilePdbPath);
511 return true;
512 }
513 return false;
514 }
515
WriteLanguageRules(const std::string & language,const std::string & config)516 void cmNinjaTargetGenerator::WriteLanguageRules(const std::string& language,
517 const std::string& config)
518 {
519 #ifdef NINJA_GEN_VERBOSE_FILES
520 this->GetRulesFileStream() << "# Rules for language " << language << "\n\n";
521 #endif
522 this->WriteCompileRule(language, config);
523 }
524
525 namespace {
526 // Create the command to run the dependency scanner
GetScanCommand(const std::string & cmakeCmd,const std::string & tdi,const std::string & lang,const std::string & ppFile,const std::string & ddiFile)527 std::string GetScanCommand(const std::string& cmakeCmd, const std::string& tdi,
528 const std::string& lang, const std::string& ppFile,
529 const std::string& ddiFile)
530 {
531 return cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi,
532 " --lang=", lang, " --pp=", ppFile,
533 " --dep=$DEP_FILE --obj=$OBJ_FILE --ddi=", ddiFile);
534 }
535
536 // Helper function to create dependency scanning rule that may or may
537 // not perform explicit preprocessing too.
GetScanRule(std::string const & ruleName,std::string const & ppFileName,cmRulePlaceholderExpander::RuleVariables const & vars,const std::string & responseFlag,const std::string & flags,cmRulePlaceholderExpander * const rulePlaceholderExpander,cmLocalNinjaGenerator * generator,std::vector<std::string> scanCmds,const std::string & outputConfig)538 cmNinjaRule GetScanRule(
539 std::string const& ruleName, std::string const& ppFileName,
540 cmRulePlaceholderExpander::RuleVariables const& vars,
541 const std::string& responseFlag, const std::string& flags,
542 cmRulePlaceholderExpander* const rulePlaceholderExpander,
543 cmLocalNinjaGenerator* generator, std::vector<std::string> scanCmds,
544 const std::string& outputConfig)
545 {
546 cmNinjaRule rule(ruleName);
547 // Scanning always uses a depfile for preprocessor dependencies.
548 rule.DepType = ""; // no deps= for multiple outputs
549 rule.DepFile = "$DEP_FILE";
550
551 cmRulePlaceholderExpander::RuleVariables scanVars;
552 scanVars.CMTargetName = vars.CMTargetName;
553 scanVars.CMTargetType = vars.CMTargetType;
554 scanVars.Language = vars.Language;
555 scanVars.Object = "$OBJ_FILE";
556 scanVars.PreprocessedSource = ppFileName.c_str();
557 scanVars.DynDepFile = "$DYNDEP_INTERMEDIATE_FILE";
558 scanVars.DependencyFile = rule.DepFile.c_str();
559 scanVars.DependencyTarget = "$out";
560
561 // Scanning needs the same preprocessor settings as direct compilation would.
562 scanVars.Source = vars.Source;
563 scanVars.Defines = vars.Defines;
564 scanVars.Includes = vars.Includes;
565
566 // Scanning needs the compilation flags too.
567 std::string scanFlags = flags;
568
569 // If using a response file, move defines, includes, and flags into it.
570 if (!responseFlag.empty()) {
571 rule.RspFile = "$RSP_FILE";
572 rule.RspContent =
573 cmStrCat(' ', scanVars.Defines, ' ', scanVars.Includes, ' ', scanFlags);
574 scanFlags = cmStrCat(responseFlag, rule.RspFile);
575 scanVars.Defines = "";
576 scanVars.Includes = "";
577 }
578
579 scanVars.Flags = scanFlags.c_str();
580
581 // Rule for scanning a source file.
582 for (std::string& scanCmd : scanCmds) {
583 rulePlaceholderExpander->ExpandRuleVariables(generator, scanCmd, scanVars);
584 }
585 rule.Command =
586 generator->BuildCommandLine(scanCmds, outputConfig, outputConfig);
587
588 return rule;
589 }
590 }
591
WriteCompileRule(const std::string & lang,const std::string & config)592 void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
593 const std::string& config)
594 {
595 cmRulePlaceholderExpander::RuleVariables vars;
596 vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
597 vars.CMTargetType =
598 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str();
599 vars.Language = lang.c_str();
600 vars.Source = "$in";
601 vars.Object = "$out";
602 vars.Defines = "$DEFINES";
603 vars.Includes = "$INCLUDES";
604 vars.TargetPDB = "$TARGET_PDB";
605 vars.TargetCompilePDB = "$TARGET_COMPILE_PDB";
606 vars.ObjectDir = "$OBJECT_DIR";
607 vars.ObjectFileDir = "$OBJECT_FILE_DIR";
608 vars.ISPCHeader = "$ISPC_HEADER_FILE";
609
610 cmMakefile* mf = this->GetMakefile();
611
612 // For some cases we scan to dynamically discover dependencies.
613 bool const needDyndep = this->NeedDyndep(lang, config);
614 bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(lang);
615
616 std::string flags = "$FLAGS";
617
618 std::string responseFlag;
619 bool const lang_supports_response = lang != "RC";
620 if (lang_supports_response && this->ForceResponseFile()) {
621 std::string const responseFlagVar =
622 cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_FLAG");
623 responseFlag = this->Makefile->GetSafeDefinition(responseFlagVar);
624 if (responseFlag.empty() && lang != "CUDA") {
625 responseFlag = "@";
626 }
627 }
628 std::string const modmapFormatVar =
629 cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FORMAT");
630 std::string const modmapFormat =
631 this->Makefile->GetSafeDefinition(modmapFormatVar);
632
633 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
634 this->GetLocalGenerator()->CreateRulePlaceholderExpander());
635
636 std::string const tdi = this->GetLocalGenerator()->ConvertToOutputFormat(
637 this->ConvertToNinjaPath(this->GetTargetDependInfoPath(lang, config)),
638 cmLocalGenerator::SHELL);
639
640 std::string launcher;
641 cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
642 this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE");
643 if (cmNonempty(val)) {
644 launcher = cmStrCat(*val, ' ');
645 }
646
647 std::string const cmakeCmd =
648 this->GetLocalGenerator()->ConvertToOutputFormat(
649 cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
650
651 if (needDyndep) {
652 // Rule to scan dependencies of sources that need preprocessing.
653 {
654 std::vector<std::string> scanCommands;
655 std::string scanRuleName;
656 std::string ppFileName;
657 if (compilationPreprocesses) {
658 scanRuleName = this->LanguageScanRule(lang, config);
659 ppFileName = "$PREPROCESSED_OUTPUT_FILE";
660 std::string const& scanCommand = mf->GetRequiredDefinition(
661 cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_SCANDEP_SOURCE"));
662 cmExpandList(scanCommand, scanCommands);
663 for (std::string& i : scanCommands) {
664 i = cmStrCat(launcher, i);
665 }
666 } else {
667 scanRuleName = this->LanguagePreprocessAndScanRule(lang, config);
668 ppFileName = "$out";
669 std::string const& ppCommmand = mf->GetRequiredDefinition(
670 cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE"));
671 cmExpandList(ppCommmand, scanCommands);
672 for (std::string& i : scanCommands) {
673 i = cmStrCat(launcher, i);
674 }
675 scanCommands.emplace_back(GetScanCommand(cmakeCmd, tdi, lang, "$out",
676 "$DYNDEP_INTERMEDIATE_FILE"));
677 }
678
679 auto scanRule =
680 GetScanRule(scanRuleName, ppFileName, vars, responseFlag, flags,
681 rulePlaceholderExpander.get(), this->GetLocalGenerator(),
682 std::move(scanCommands), config);
683
684 scanRule.Comment =
685 cmStrCat("Rule for generating ", lang, " dependencies.");
686 if (compilationPreprocesses) {
687 scanRule.Description =
688 cmStrCat("Scanning $in for ", lang, " dependencies");
689 } else {
690 scanRule.Description =
691 cmStrCat("Building ", lang, " preprocessed $out");
692 }
693
694 this->GetGlobalGenerator()->AddRule(scanRule);
695 }
696
697 if (!compilationPreprocesses) {
698 // Compilation will not preprocess, so it does not need the defines
699 // unless the compiler wants them for some other purpose.
700 if (!this->CompileWithDefines(lang)) {
701 vars.Defines = "";
702 }
703
704 // Rule to scan dependencies of sources that do not need preprocessing.
705 std::string const& scanRuleName = this->LanguageScanRule(lang, config);
706 std::vector<std::string> scanCommands;
707 scanCommands.emplace_back(
708 GetScanCommand(cmakeCmd, tdi, lang, "$in", "$out"));
709
710 auto scanRule = GetScanRule(
711 scanRuleName, "", vars, "", flags, rulePlaceholderExpander.get(),
712 this->GetLocalGenerator(), std::move(scanCommands), config);
713
714 // Write the rule for generating dependencies for the given language.
715 scanRule.Comment = cmStrCat("Rule for generating ", lang,
716 " dependencies on non-preprocessed files.");
717 scanRule.Description =
718 cmStrCat("Generating ", lang, " dependencies for $in");
719
720 this->GetGlobalGenerator()->AddRule(scanRule);
721 }
722
723 // Write the rule for ninja dyndep file generation.
724 cmNinjaRule rule(this->LanguageDyndepRule(lang, config));
725 // Command line length is almost always limited -> use response file for
726 // dyndep rules
727 rule.RspFile = "$out.rsp";
728 rule.RspContent = "$in";
729
730 // Run CMake dependency scanner on the source file (using the preprocessed
731 // source if that was performed).
732 std::string ddModmapArg;
733 if (!modmapFormat.empty()) {
734 ddModmapArg += cmStrCat(" --modmapfmt=", modmapFormat);
735 }
736 {
737 std::vector<std::string> ddCmds;
738 {
739 std::string ccmd = cmStrCat(
740 cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi, " --lang=", lang,
741 ddModmapArg, " --dd=$out @", rule.RspFile);
742 ddCmds.emplace_back(std::move(ccmd));
743 }
744 rule.Command =
745 this->GetLocalGenerator()->BuildCommandLine(ddCmds, config, config);
746 }
747 rule.Comment =
748 cmStrCat("Rule to generate ninja dyndep files for ", lang, '.');
749 rule.Description = cmStrCat("Generating ", lang, " dyndep file $out");
750 this->GetGlobalGenerator()->AddRule(rule);
751 }
752
753 cmNinjaRule rule(this->LanguageCompilerRule(lang, config));
754 // If using a response file, move defines, includes, and flags into it.
755 if (!responseFlag.empty()) {
756 rule.RspFile = "$RSP_FILE";
757 rule.RspContent =
758 cmStrCat(' ', vars.Defines, ' ', vars.Includes, ' ', flags);
759 flags = cmStrCat(responseFlag, rule.RspFile);
760 vars.Defines = "";
761 vars.Includes = "";
762 }
763
764 // Tell ninja dependency format so all deps can be loaded into a database
765 std::string cldeps;
766 if (!compilationPreprocesses) {
767 // The compiler will not do preprocessing, so it has no such dependencies.
768 } else if (mf->IsOn(cmStrCat("CMAKE_NINJA_CMCLDEPS_", lang))) {
769 // For the MS resource compiler we need cmcldeps, but skip dependencies
770 // for source-file try_compile cases because they are always fresh.
771 if (!mf->GetIsSourceFileTryCompile()) {
772 rule.DepType = "gcc";
773 rule.DepFile = "$DEP_FILE";
774 cmValue d = mf->GetDefinition("CMAKE_C_COMPILER");
775 const std::string cl =
776 d ? *d : mf->GetSafeDefinition("CMAKE_CXX_COMPILER");
777 std::string cmcldepsPath;
778 cmSystemTools::GetShortPath(cmSystemTools::GetCMClDepsCommand(),
779 cmcldepsPath);
780 cldeps = cmStrCat(cmcldepsPath, ' ', lang, ' ', vars.Source,
781 " $DEP_FILE $out \"",
782 mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"),
783 "\" \"", cl, "\" ");
784 }
785 } else {
786 const auto& depType = this->GetMakefile()->GetSafeDefinition(
787 cmStrCat("CMAKE_", lang, "_DEPFILE_FORMAT"));
788 if (depType == "msvc"_s) {
789 rule.DepType = "msvc";
790 rule.DepFile.clear();
791 } else {
792 rule.DepType = "gcc";
793 rule.DepFile = "$DEP_FILE";
794 }
795 vars.DependencyFile = rule.DepFile.c_str();
796 vars.DependencyTarget = "$out";
797
798 const std::string flagsName = cmStrCat("CMAKE_DEPFILE_FLAGS_", lang);
799 std::string depfileFlags = mf->GetSafeDefinition(flagsName);
800 if (!depfileFlags.empty()) {
801 rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
802 depfileFlags, vars);
803 flags += cmStrCat(' ', depfileFlags);
804 }
805 }
806
807 if (needDyndep && !modmapFormat.empty()) {
808 std::string modmapFlags = mf->GetRequiredDefinition(
809 cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FLAG"));
810 cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>",
811 "$DYNDEP_MODULE_MAP_FILE");
812 flags += cmStrCat(' ', modmapFlags);
813 }
814
815 vars.Flags = flags.c_str();
816 vars.DependencyFile = rule.DepFile.c_str();
817
818 // Rule for compiling object file.
819 std::vector<std::string> compileCmds;
820 if (lang == "CUDA") {
821 std::string cmdVar;
822 if (this->GeneratorTarget->GetPropertyAsBool(
823 "CUDA_SEPARABLE_COMPILATION")) {
824 cmdVar = "CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION";
825 } else if (this->GeneratorTarget->GetPropertyAsBool(
826 "CUDA_PTX_COMPILATION")) {
827 cmdVar = "CMAKE_CUDA_COMPILE_PTX_COMPILATION";
828 } else {
829 cmdVar = "CMAKE_CUDA_COMPILE_WHOLE_COMPILATION";
830 }
831 const std::string& compileCmd = mf->GetRequiredDefinition(cmdVar);
832 cmExpandList(compileCmd, compileCmds);
833 } else {
834 const std::string cmdVar = cmStrCat("CMAKE_", lang, "_COMPILE_OBJECT");
835 const std::string& compileCmd = mf->GetRequiredDefinition(cmdVar);
836 cmExpandList(compileCmd, compileCmds);
837 }
838
839 // See if we need to use a compiler launcher like ccache or distcc
840 std::string compilerLauncher;
841 if (!compileCmds.empty() &&
842 (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" ||
843 lang == "HIP" || lang == "ISPC" || lang == "OBJC" ||
844 lang == "OBJCXX")) {
845 std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER");
846 cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
847 if (cmNonempty(clauncher)) {
848 compilerLauncher = *clauncher;
849 }
850 }
851
852 // Maybe insert an include-what-you-use runner.
853 if (!compileCmds.empty() &&
854 (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) {
855 std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY");
856 cmValue tidy = this->GeneratorTarget->GetProperty(tidy_prop);
857 cmValue iwyu = nullptr;
858 cmValue cpplint = nullptr;
859 cmValue cppcheck = nullptr;
860 if (lang == "C" || lang == "CXX") {
861 std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE");
862 iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
863 std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT");
864 cpplint = this->GeneratorTarget->GetProperty(cpplint_prop);
865 std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK");
866 cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop);
867 }
868 if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
869 cmNonempty(cppcheck)) {
870 std::string run_iwyu = cmStrCat(cmakeCmd, " -E __run_co_compile");
871 if (!compilerLauncher.empty()) {
872 // In __run_co_compile case the launcher command is supplied
873 // via --launcher=<maybe-list> and consumed
874 run_iwyu +=
875 cmStrCat(" --launcher=",
876 this->LocalGenerator->EscapeForShell(compilerLauncher));
877 compilerLauncher.clear();
878 }
879 if (cmNonempty(iwyu)) {
880 run_iwyu += " --iwyu=";
881
882 // Only add --driver-mode if it is not already specified, as adding
883 // it unconditionally might override a user-specified driver-mode
884 if (iwyu.Get()->find("--driver-mode=") == std::string::npos) {
885 cmValue p = this->Makefile->GetDefinition(
886 cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE"));
887 std::string driverMode;
888
889 if (cmNonempty(p)) {
890 driverMode = *p;
891 } else {
892 driverMode = lang == "C" ? "gcc" : "g++";
893 }
894
895 run_iwyu += this->LocalGenerator->EscapeForShell(
896 cmStrCat(*iwyu, ";--driver-mode=", driverMode));
897 } else {
898 run_iwyu += this->LocalGenerator->EscapeForShell(*iwyu);
899 }
900 }
901 if (cmNonempty(tidy)) {
902 run_iwyu += " --tidy=";
903 cmValue p = this->Makefile->GetDefinition(
904 cmStrCat("CMAKE_", lang, "_CLANG_TIDY_DRIVER_MODE"));
905 std::string driverMode;
906 if (cmNonempty(p)) {
907 driverMode = *p;
908 } else {
909 driverMode = lang == "C" ? "gcc" : "g++";
910 }
911 run_iwyu += this->GetLocalGenerator()->EscapeForShell(
912 cmStrCat(*tidy, ";--extra-arg-before=--driver-mode=", driverMode));
913 }
914 if (cmNonempty(cpplint)) {
915 run_iwyu += cmStrCat(
916 " --cpplint=", this->GetLocalGenerator()->EscapeForShell(*cpplint));
917 }
918 if (cmNonempty(cppcheck)) {
919 run_iwyu +=
920 cmStrCat(" --cppcheck=",
921 this->GetLocalGenerator()->EscapeForShell(*cppcheck));
922 }
923 if (cmNonempty(tidy) || cmNonempty(cpplint) || cmNonempty(cppcheck)) {
924 run_iwyu += " --source=$in";
925 }
926 run_iwyu += " -- ";
927 compileCmds.front().insert(0, run_iwyu);
928 }
929 }
930
931 // If compiler launcher was specified and not consumed above, it
932 // goes to the beginning of the command line.
933 if (!compileCmds.empty() && !compilerLauncher.empty()) {
934 std::vector<std::string> args = cmExpandedList(compilerLauncher, true);
935 if (!args.empty()) {
936 args[0] = this->LocalGenerator->ConvertToOutputFormat(
937 args[0], cmOutputConverter::SHELL);
938 for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) {
939 i = this->LocalGenerator->EscapeForShell(i);
940 }
941 }
942 compileCmds.front().insert(0, cmStrCat(cmJoin(args, " "), ' '));
943 }
944
945 if (!compileCmds.empty()) {
946 compileCmds.front().insert(0, cldeps);
947 }
948
949 const auto& extraCommands = this->GetMakefile()->GetSafeDefinition(
950 cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS"));
951 if (!extraCommands.empty()) {
952 auto commandList = cmExpandedList(extraCommands);
953 compileCmds.insert(compileCmds.end(), commandList.cbegin(),
954 commandList.cend());
955 }
956
957 for (std::string& i : compileCmds) {
958 i = cmStrCat(launcher, i);
959 rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), i,
960 vars);
961 }
962
963 rule.Command =
964 this->GetLocalGenerator()->BuildCommandLine(compileCmds, config, config);
965
966 // Write the rule for compiling file of the given language.
967 rule.Comment = cmStrCat("Rule for compiling ", lang, " files.");
968 rule.Description = cmStrCat("Building ", lang, " object $out");
969 this->GetGlobalGenerator()->AddRule(rule);
970 }
971
WriteObjectBuildStatements(const std::string & config,const std::string & fileConfig,bool firstForConfig)972 void cmNinjaTargetGenerator::WriteObjectBuildStatements(
973 const std::string& config, const std::string& fileConfig,
974 bool firstForConfig)
975 {
976 // Write comments.
977 cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
978 this->GetImplFileStream(fileConfig)
979 << "# Object build statements for "
980 << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
981 << " target " << this->GetTargetName() << "\n\n";
982
983 {
984 std::vector<cmSourceFile const*> customCommands;
985 this->GeneratorTarget->GetCustomCommands(customCommands, config);
986 for (cmSourceFile const* sf : customCommands) {
987 cmCustomCommand const* cc = sf->GetCustomCommand();
988 this->GetLocalGenerator()->AddCustomCommandTarget(
989 cc, this->GetGeneratorTarget());
990 // Record the custom commands for this target. The container is used
991 // in WriteObjectBuildStatement when called in a loop below.
992 this->Configs[config].CustomCommands.push_back(cc);
993 }
994 }
995 {
996 std::vector<cmSourceFile const*> headerSources;
997 this->GeneratorTarget->GetHeaderSources(headerSources, config);
998 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
999 headerSources, this->Configs[fileConfig].MacOSXContentGenerator.get(),
1000 config);
1001 }
1002 {
1003 std::vector<cmSourceFile const*> extraSources;
1004 this->GeneratorTarget->GetExtraSources(extraSources, config);
1005 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
1006 extraSources, this->Configs[fileConfig].MacOSXContentGenerator.get(),
1007 config);
1008 }
1009 if (firstForConfig) {
1010 cmValue pchExtension =
1011 this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
1012
1013 std::vector<cmSourceFile const*> externalObjects;
1014 this->GeneratorTarget->GetExternalObjects(externalObjects, config);
1015 for (cmSourceFile const* sf : externalObjects) {
1016 auto objectFileName = this->GetGlobalGenerator()->ExpandCFGIntDir(
1017 this->ConvertToNinjaPath(sf->GetFullPath()), config);
1018 if (!cmHasSuffix(objectFileName, pchExtension)) {
1019 this->Configs[config].Objects.push_back(objectFileName);
1020 }
1021 }
1022 }
1023
1024 {
1025 cmNinjaBuild build("phony");
1026 build.Comment =
1027 cmStrCat("Order-only phony target for ", this->GetTargetName());
1028 build.Outputs.push_back(this->OrderDependsTargetForTarget(config));
1029
1030 cmNinjaDeps& orderOnlyDeps = build.OrderOnlyDeps;
1031 this->GetLocalGenerator()->AppendTargetDepends(
1032 this->GeneratorTarget, orderOnlyDeps, config, fileConfig,
1033 DependOnTargetOrdering);
1034
1035 // Add order-only dependencies on other files associated with the target.
1036 cm::append(orderOnlyDeps, this->Configs[config].ExtraFiles);
1037
1038 // Add order-only dependencies on custom command outputs.
1039 for (cmCustomCommand const* cc : this->Configs[config].CustomCommands) {
1040 cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator());
1041 const std::vector<std::string>& ccoutputs = ccg.GetOutputs();
1042 const std::vector<std::string>& ccbyproducts = ccg.GetByproducts();
1043 std::transform(ccoutputs.begin(), ccoutputs.end(),
1044 std::back_inserter(orderOnlyDeps),
1045 this->MapToNinjaPath());
1046 std::transform(ccbyproducts.begin(), ccbyproducts.end(),
1047 std::back_inserter(orderOnlyDeps),
1048 this->MapToNinjaPath());
1049 }
1050
1051 std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end());
1052 orderOnlyDeps.erase(
1053 std::unique(orderOnlyDeps.begin(), orderOnlyDeps.end()),
1054 orderOnlyDeps.end());
1055
1056 // The phony target must depend on at least one input or ninja will explain
1057 // that "output ... of phony edge with no inputs doesn't exist" and
1058 // consider the phony output "dirty".
1059 if (orderOnlyDeps.empty()) {
1060 // Any path that always exists will work here. It would be nice to
1061 // use just "." but that is not supported by Ninja < 1.7.
1062 std::string tgtDir = cmStrCat(
1063 this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
1064 this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget));
1065 orderOnlyDeps.push_back(this->ConvertToNinjaPath(tgtDir));
1066 }
1067
1068 this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1069 build);
1070 }
1071
1072 {
1073 std::vector<cmSourceFile const*> objectSources;
1074 this->GeneratorTarget->GetObjectSources(objectSources, config);
1075
1076 for (cmSourceFile const* sf : objectSources) {
1077 this->WriteObjectBuildStatement(sf, config, fileConfig, firstForConfig);
1078 }
1079 }
1080
1081 for (auto const& langDDIFiles : this->Configs[config].DDIFiles) {
1082 std::string const& language = langDDIFiles.first;
1083 cmNinjaDeps const& ddiFiles = langDDIFiles.second;
1084
1085 cmNinjaBuild build(this->LanguageDyndepRule(language, config));
1086 build.Outputs.push_back(this->GetDyndepFilePath(language, config));
1087 build.ExplicitDeps = ddiFiles;
1088
1089 this->WriteTargetDependInfo(language, config);
1090
1091 // Make sure dyndep files for all our dependencies have already
1092 // been generated so that the '<LANG>Modules.json' files they
1093 // produced as side-effects are available for us to read.
1094 // Ideally we should depend on the '<LANG>Modules.json' files
1095 // from our dependencies directly, but we don't know which of
1096 // our dependencies produces them. Fixing this will require
1097 // refactoring the Ninja generator to generate targets in
1098 // dependency order so that we can collect the needed information.
1099 this->GetLocalGenerator()->AppendTargetDepends(
1100 this->GeneratorTarget, build.OrderOnlyDeps, config, fileConfig,
1101 DependOnTargetArtifact);
1102
1103 this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1104 build);
1105 }
1106
1107 this->GetImplFileStream(fileConfig) << "\n";
1108
1109 if (!this->Configs[config].SwiftOutputMap.empty()) {
1110 std::string const mapFilePath =
1111 cmStrCat(this->GeneratorTarget->GetSupportDirectory(), '/', config, '/',
1112 "output-file-map.json");
1113 std::string const targetSwiftDepsPath = [this, config]() -> std::string {
1114 cmGeneratorTarget const* target = this->GeneratorTarget;
1115 if (cmValue name = target->GetProperty("Swift_DEPENDENCIES_FILE")) {
1116 return *name;
1117 }
1118 return this->ConvertToNinjaPath(
1119 cmStrCat(target->GetSupportDirectory(), '/', config, '/',
1120 target->GetName(), ".swiftdeps"));
1121 }();
1122
1123 // build the global target dependencies
1124 // https://github.com/apple/swift/blob/master/docs/Driver.md#output-file-maps
1125 Json::Value deps(Json::objectValue);
1126 deps["swift-dependencies"] = targetSwiftDepsPath;
1127 this->Configs[config].SwiftOutputMap[""] = deps;
1128
1129 cmGeneratedFileStream output(mapFilePath);
1130 output << this->Configs[config].SwiftOutputMap;
1131 }
1132 }
1133
1134 namespace {
GetScanBuildStatement(const std::string & ruleName,const std::string & ppFileName,bool compilePP,bool compilePPWithDefines,cmNinjaBuild & objBuild,cmNinjaVars & vars,std::string const & modmapFormat,const std::string & objectFileName,cmLocalGenerator * lg)1135 cmNinjaBuild GetScanBuildStatement(const std::string& ruleName,
1136 const std::string& ppFileName,
1137 bool compilePP, bool compilePPWithDefines,
1138 cmNinjaBuild& objBuild, cmNinjaVars& vars,
1139 std::string const& modmapFormat,
1140 const std::string& objectFileName,
1141 cmLocalGenerator* lg)
1142 {
1143 cmNinjaBuild scanBuild(ruleName);
1144
1145 scanBuild.RspFile = "$out.rsp";
1146
1147 if (compilePP) {
1148 // Move compilation dependencies to the scan/preprocessing build statement.
1149 std::swap(scanBuild.ExplicitDeps, objBuild.ExplicitDeps);
1150 std::swap(scanBuild.ImplicitDeps, objBuild.ImplicitDeps);
1151 std::swap(scanBuild.OrderOnlyDeps, objBuild.OrderOnlyDeps);
1152 std::swap(scanBuild.Variables["IN_ABS"], vars["IN_ABS"]);
1153
1154 // The actual compilation will now use the preprocessed source.
1155 objBuild.ExplicitDeps.push_back(ppFileName);
1156 } else {
1157 // Copy compilation dependencies to the scan/preprocessing build statement.
1158 scanBuild.ExplicitDeps = objBuild.ExplicitDeps;
1159 scanBuild.ImplicitDeps = objBuild.ImplicitDeps;
1160 scanBuild.OrderOnlyDeps = objBuild.OrderOnlyDeps;
1161 scanBuild.Variables["IN_ABS"] = vars["IN_ABS"];
1162 }
1163
1164 // Scanning and compilation generally use the same flags.
1165 scanBuild.Variables["FLAGS"] = vars["FLAGS"];
1166
1167 if (compilePP && !compilePPWithDefines) {
1168 // Move preprocessor definitions to the scan/preprocessor build statement.
1169 std::swap(scanBuild.Variables["DEFINES"], vars["DEFINES"]);
1170 } else {
1171 // Copy preprocessor definitions to the scan/preprocessor build statement.
1172 scanBuild.Variables["DEFINES"] = vars["DEFINES"];
1173 }
1174
1175 // Copy include directories to the preprocessor build statement. The
1176 // Fortran compilation build statement still needs them for the INCLUDE
1177 // directive.
1178 scanBuild.Variables["INCLUDES"] = vars["INCLUDES"];
1179
1180 // Tell dependency scanner the object file that will result from
1181 // compiling the source.
1182 scanBuild.Variables["OBJ_FILE"] = objectFileName;
1183
1184 // Tell dependency scanner where to store dyndep intermediate results.
1185 std::string const& ddiFile = cmStrCat(objectFileName, ".ddi");
1186 scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile;
1187
1188 // Outputs of the scan/preprocessor build statement.
1189 if (compilePP) {
1190 scanBuild.Outputs.push_back(ppFileName);
1191 scanBuild.ImplicitOuts.push_back(ddiFile);
1192 } else {
1193 scanBuild.Outputs.push_back(ddiFile);
1194 scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] = ppFileName;
1195 }
1196
1197 // Scanning always uses a depfile for preprocessor dependencies.
1198 std::string const& depFileName = cmStrCat(scanBuild.Outputs.front(), ".d");
1199 scanBuild.Variables["DEP_FILE"] =
1200 lg->ConvertToOutputFormat(depFileName, cmOutputConverter::SHELL);
1201 if (compilePP) {
1202 // The actual compilation does not need a depfile because it
1203 // depends on the already-preprocessed source.
1204 vars.erase("DEP_FILE");
1205 }
1206
1207 if (!modmapFormat.empty()) {
1208 // XXX(modmap): If changing this path construction, change
1209 // `cmGlobalNinjaGenerator::WriteDyndep` to expect the corresponding
1210 // file path.
1211 std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap");
1212 scanBuild.Variables["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
1213 scanBuild.ImplicitOuts.push_back(ddModmapFile);
1214 }
1215
1216 return scanBuild;
1217 }
1218 }
1219
WriteObjectBuildStatement(cmSourceFile const * source,const std::string & config,const std::string & fileConfig,bool firstForConfig)1220 void cmNinjaTargetGenerator::WriteObjectBuildStatement(
1221 cmSourceFile const* source, const std::string& config,
1222 const std::string& fileConfig, bool firstForConfig)
1223 {
1224 std::string const language = source->GetLanguage();
1225 std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source);
1226 std::string const objectDir = this->ConvertToNinjaPath(
1227 cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
1228 this->GetGlobalGenerator()->ConfigDirectory(config)));
1229 std::string const objectFileName =
1230 this->ConvertToNinjaPath(this->GetObjectFilePath(source, config));
1231 std::string const objectFileDir =
1232 cmSystemTools::GetFilenamePath(objectFileName);
1233
1234 std::string cmakeVarLang = cmStrCat("CMAKE_", language);
1235
1236 // build response file name
1237 std::string cmakeLinkVar = cmStrCat(cmakeVarLang, "_RESPONSE_FILE_FLAG");
1238
1239 cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
1240
1241 bool const lang_supports_response =
1242 !(language == "RC" || (language == "CUDA" && !flag));
1243 int const commandLineLengthLimit =
1244 ((lang_supports_response && this->ForceResponseFile())) ? -1 : 0;
1245
1246 cmNinjaBuild objBuild(this->LanguageCompilerRule(language, config));
1247 cmNinjaVars& vars = objBuild.Variables;
1248 vars["FLAGS"] = this->ComputeFlagsForObject(source, language, config);
1249 vars["DEFINES"] = this->ComputeDefines(source, language, config);
1250 vars["INCLUDES"] = this->ComputeIncludes(source, language, config);
1251
1252 if (this->GetMakefile()->GetSafeDefinition(
1253 cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) {
1254 bool replaceExt(false);
1255 if (!language.empty()) {
1256 std::string repVar =
1257 cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE");
1258 replaceExt = this->Makefile->IsOn(repVar);
1259 }
1260 if (!replaceExt) {
1261 // use original code
1262 vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1263 cmStrCat(objectFileName, ".d"), cmOutputConverter::SHELL);
1264 } else {
1265 // Replace the original source file extension with the
1266 // depend file extension.
1267 std::string dependFileName = cmStrCat(
1268 cmSystemTools::GetFilenameWithoutLastExtension(objectFileName), ".d");
1269 vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1270 cmStrCat(objectFileDir, '/', dependFileName),
1271 cmOutputConverter::SHELL);
1272 }
1273 }
1274
1275 this->ExportObjectCompileCommand(
1276 language, sourceFilePath, objectDir, objectFileName, objectFileDir,
1277 vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config);
1278
1279 objBuild.Outputs.push_back(objectFileName);
1280 if (firstForConfig) {
1281 cmValue pchExtension =
1282 this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
1283 if (!cmHasSuffix(objectFileName, pchExtension)) {
1284 // Add this object to the list of object files.
1285 this->Configs[config].Objects.push_back(objectFileName);
1286 }
1287 }
1288
1289 objBuild.ExplicitDeps.push_back(sourceFilePath);
1290
1291 // Add precompile headers dependencies
1292 std::vector<std::string> depList;
1293
1294 std::vector<std::string> architectures;
1295 this->GeneratorTarget->GetAppleArchs(config, architectures);
1296 if (architectures.empty()) {
1297 architectures.emplace_back();
1298 }
1299
1300 std::unordered_set<std::string> pchSources;
1301 for (const std::string& arch : architectures) {
1302 const std::string pchSource =
1303 this->GeneratorTarget->GetPchSource(config, language, arch);
1304
1305 if (!pchSource.empty()) {
1306 pchSources.insert(pchSource);
1307 }
1308 }
1309
1310 if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
1311 for (const std::string& arch : architectures) {
1312 depList.push_back(
1313 this->GeneratorTarget->GetPchHeader(config, language, arch));
1314 if (pchSources.find(source->GetFullPath()) == pchSources.end()) {
1315 depList.push_back(
1316 this->GeneratorTarget->GetPchFile(config, language, arch));
1317 }
1318 }
1319 }
1320
1321 if (cmValue objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
1322 std::vector<std::string> objDepList = cmExpandedList(*objectDeps);
1323 std::copy(objDepList.begin(), objDepList.end(),
1324 std::back_inserter(depList));
1325 }
1326
1327 if (!depList.empty()) {
1328 for (std::string& odi : depList) {
1329 if (cmSystemTools::FileIsFullPath(odi)) {
1330 odi = cmSystemTools::CollapseFullPath(odi);
1331 }
1332 }
1333 std::transform(depList.begin(), depList.end(),
1334 std::back_inserter(objBuild.ImplicitDeps),
1335 this->MapToNinjaPath());
1336 }
1337
1338 objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config));
1339
1340 // If the source file is GENERATED and does not have a custom command
1341 // (either attached to this source file or another one), assume that one of
1342 // the target dependencies, OBJECT_DEPENDS or header file custom commands
1343 // will rebuild the file.
1344 if (source->GetIsGenerated() &&
1345 !source->GetPropertyAsBool("__CMAKE_GENERATED_BY_CMAKE") &&
1346 !source->GetCustomCommand() &&
1347 !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFilePath)) {
1348 this->GetGlobalGenerator()->AddAssumedSourceDependencies(
1349 sourceFilePath, objBuild.OrderOnlyDeps);
1350 }
1351
1352 // For some cases we scan to dynamically discover dependencies.
1353 bool const needDyndep = this->NeedDyndep(language, config);
1354 bool const compilationPreprocesses =
1355 !this->NeedExplicitPreprocessing(language);
1356
1357 std::string modmapFormat;
1358 if (needDyndep) {
1359 std::string const modmapFormatVar =
1360 cmStrCat("CMAKE_EXPERIMENTAL_", language, "_MODULE_MAP_FORMAT");
1361 modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar);
1362 }
1363
1364 if (needDyndep) {
1365 // If source/target has preprocessing turned off, we still need to
1366 // generate an explicit dependency step
1367 const auto srcpp = source->GetSafeProperty("Fortran_PREPROCESS");
1368 cmOutputConverter::FortranPreprocess preprocess =
1369 cmOutputConverter::GetFortranPreprocess(srcpp);
1370 if (preprocess == cmOutputConverter::FortranPreprocess::Unset) {
1371 const auto& tgtpp =
1372 this->GeneratorTarget->GetSafeProperty("Fortran_PREPROCESS");
1373 preprocess = cmOutputConverter::GetFortranPreprocess(tgtpp);
1374 }
1375
1376 bool const compilePP = !compilationPreprocesses &&
1377 (preprocess != cmOutputConverter::FortranPreprocess::NotNeeded);
1378 bool const compilePPWithDefines =
1379 compilePP && this->CompileWithDefines(language);
1380
1381 std::string scanRuleName;
1382 std::string ppFileName;
1383 if (compilePP) {
1384 scanRuleName = this->LanguagePreprocessAndScanRule(language, config);
1385 ppFileName = this->ConvertToNinjaPath(
1386 this->GetPreprocessedFilePath(source, config));
1387 } else {
1388 scanRuleName = this->LanguageScanRule(language, config);
1389 ppFileName = cmStrCat(objectFileName, ".ddi.i");
1390 }
1391
1392 cmNinjaBuild ppBuild = GetScanBuildStatement(
1393 scanRuleName, ppFileName, compilePP, compilePPWithDefines, objBuild,
1394 vars, modmapFormat, objectFileName, this->LocalGenerator);
1395
1396 if (compilePP) {
1397 // In case compilation requires flags that are incompatible with
1398 // preprocessing, include them here.
1399 std::string const& postFlag = this->Makefile->GetSafeDefinition(
1400 cmStrCat("CMAKE_", language, "_POSTPROCESS_FLAG"));
1401 this->LocalGenerator->AppendFlags(vars["FLAGS"], postFlag);
1402
1403 // Prepend source file's original directory as an include directory
1404 // so e.g. Fortran INCLUDE statements can look for files in it.
1405 std::vector<std::string> sourceDirectory;
1406 sourceDirectory.push_back(
1407 cmSystemTools::GetParentDirectory(source->GetFullPath()));
1408
1409 std::string sourceDirectoryFlag = this->LocalGenerator->GetIncludeFlags(
1410 sourceDirectory, this->GeneratorTarget, language, config, false,
1411 cmLocalGenerator::IncludePathStyle::Default);
1412
1413 vars["INCLUDES"] = cmStrCat(sourceDirectoryFlag, ' ', vars["INCLUDES"]);
1414 }
1415
1416 if (firstForConfig) {
1417 std::string const ddiFile = cmStrCat(objectFileName, ".ddi");
1418 this->Configs[config].DDIFiles[language].push_back(ddiFile);
1419 }
1420
1421 this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
1422 ppBuild.Variables);
1423
1424 this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1425 ppBuild, commandLineLengthLimit);
1426
1427 std::string const dyndep = this->GetDyndepFilePath(language, config);
1428 objBuild.OrderOnlyDeps.push_back(dyndep);
1429 vars["dyndep"] = dyndep;
1430
1431 if (!modmapFormat.empty()) {
1432 std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap");
1433 vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
1434 objBuild.OrderOnlyDeps.push_back(ddModmapFile);
1435 }
1436 }
1437
1438 this->EnsureParentDirectoryExists(objectFileName);
1439
1440 vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1441 objectDir, cmOutputConverter::SHELL);
1442 vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1443 objectFileDir, cmOutputConverter::SHELL);
1444
1445 this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
1446 vars);
1447
1448 if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
1449 auto pchIt = pchSources.find(source->GetFullPath());
1450 if (pchIt != pchSources.end()) {
1451 this->addPoolNinjaVariable("JOB_POOL_PRECOMPILE_HEADER",
1452 this->GetGeneratorTarget(), vars);
1453 }
1454 }
1455
1456 this->SetMsvcTargetPdbVariable(vars, config);
1457
1458 objBuild.RspFile = cmStrCat(objectFileName, ".rsp");
1459
1460 if (language == "ISPC") {
1461 std::string const& objectName =
1462 this->GeneratorTarget->GetObjectName(source);
1463 std::string ispcSource =
1464 cmSystemTools::GetFilenameWithoutLastExtension(objectName);
1465 ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource);
1466
1467 cmValue ispcSuffixProp =
1468 this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX");
1469 assert(ispcSuffixProp);
1470
1471 std::string ispcHeaderDirectory =
1472 this->GeneratorTarget->GetObjectDirectory(config);
1473 if (cmValue prop =
1474 this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) {
1475 ispcHeaderDirectory =
1476 cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop);
1477 }
1478
1479 std::string ispcHeader =
1480 cmStrCat(ispcHeaderDirectory, '/', ispcSource, *ispcSuffixProp);
1481 ispcHeader = this->ConvertToNinjaPath(ispcHeader);
1482
1483 // Make sure ninja knows what command generates the header
1484 objBuild.ImplicitOuts.push_back(ispcHeader);
1485
1486 // Make sure ninja knows how to clean the generated header
1487 this->GetGlobalGenerator()->AddAdditionalCleanFile(ispcHeader, config);
1488
1489 auto ispcSuffixes =
1490 detail::ComputeISPCObjectSuffixes(this->GeneratorTarget);
1491 if (ispcSuffixes.size() > 1) {
1492 std::string rootObjectDir =
1493 this->GeneratorTarget->GetObjectDirectory(config);
1494 auto ispcSideEfffectObjects = detail::ComputeISPCExtraObjects(
1495 objectName, rootObjectDir, ispcSuffixes);
1496
1497 for (auto sideEffect : ispcSideEfffectObjects) {
1498 sideEffect = this->ConvertToNinjaPath(sideEffect);
1499 objBuild.ImplicitOuts.emplace_back(sideEffect);
1500 this->GetGlobalGenerator()->AddAdditionalCleanFile(sideEffect, config);
1501 }
1502 }
1503
1504 vars["ISPC_HEADER_FILE"] =
1505 this->GetLocalGenerator()->ConvertToOutputFormat(
1506 ispcHeader, cmOutputConverter::SHELL);
1507 } else {
1508 auto headers = this->GeneratorTarget->GetGeneratedISPCHeaders(config);
1509 if (!headers.empty()) {
1510 std::transform(headers.begin(), headers.end(), headers.begin(),
1511 this->MapToNinjaPath());
1512 objBuild.OrderOnlyDeps.insert(objBuild.OrderOnlyDeps.end(),
1513 headers.begin(), headers.end());
1514 }
1515 }
1516
1517 if (language == "Swift") {
1518 this->EmitSwiftDependencyInfo(source, config);
1519 } else {
1520 this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1521 objBuild, commandLineLengthLimit);
1522 }
1523
1524 if (cmValue objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) {
1525 std::string evaluatedObjectOutputs = cmGeneratorExpression::Evaluate(
1526 *objectOutputs, this->LocalGenerator, config);
1527
1528 if (!evaluatedObjectOutputs.empty()) {
1529 cmNinjaBuild build("phony");
1530 build.Comment = "Additional output files.";
1531 build.Outputs = cmExpandedList(evaluatedObjectOutputs);
1532 std::transform(build.Outputs.begin(), build.Outputs.end(),
1533 build.Outputs.begin(), this->MapToNinjaPath());
1534 build.ExplicitDeps = objBuild.Outputs;
1535 this->GetGlobalGenerator()->WriteBuild(
1536 this->GetImplFileStream(fileConfig), build);
1537 }
1538 }
1539 }
1540
WriteTargetDependInfo(std::string const & lang,const std::string & config)1541 void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
1542 const std::string& config)
1543 {
1544 Json::Value tdi(Json::objectValue);
1545 tdi["language"] = lang;
1546 tdi["compiler-id"] = this->Makefile->GetSafeDefinition(
1547 cmStrCat("CMAKE_", lang, "_COMPILER_ID"));
1548
1549 std::string mod_dir;
1550 if (lang == "Fortran") {
1551 mod_dir = this->GeneratorTarget->GetFortranModuleDirectory(
1552 this->Makefile->GetHomeOutputDirectory());
1553 } else if (lang == "CXX") {
1554 mod_dir =
1555 cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory);
1556 }
1557 if (mod_dir.empty()) {
1558 mod_dir = this->Makefile->GetCurrentBinaryDirectory();
1559 }
1560 tdi["module-dir"] = mod_dir;
1561
1562 if (lang == "Fortran") {
1563 tdi["submodule-sep"] =
1564 this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP");
1565 tdi["submodule-ext"] =
1566 this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT");
1567 } else if (lang == "CXX") {
1568 // No extra information necessary.
1569 }
1570
1571 tdi["dir-cur-bld"] = this->Makefile->GetCurrentBinaryDirectory();
1572 tdi["dir-cur-src"] = this->Makefile->GetCurrentSourceDirectory();
1573 tdi["dir-top-bld"] = this->Makefile->GetHomeOutputDirectory();
1574 tdi["dir-top-src"] = this->Makefile->GetHomeDirectory();
1575
1576 Json::Value& tdi_include_dirs = tdi["include-dirs"] = Json::arrayValue;
1577 std::vector<std::string> includes;
1578 this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
1579 lang, config);
1580 for (std::string const& i : includes) {
1581 // Convert the include directories the same way we do for -I flags.
1582 // See upstream ninja issue 1251.
1583 tdi_include_dirs.append(this->ConvertToNinjaPath(i));
1584 }
1585
1586 Json::Value& tdi_linked_target_dirs = tdi["linked-target-dirs"] =
1587 Json::arrayValue;
1588 for (std::string const& l : this->GetLinkedTargetDirectories(config)) {
1589 tdi_linked_target_dirs.append(l);
1590 }
1591
1592 std::string const tdin = this->GetTargetDependInfoPath(lang, config);
1593 cmGeneratedFileStream tdif(tdin);
1594 tdif << tdi;
1595 }
1596
EmitSwiftDependencyInfo(cmSourceFile const * source,const std::string & config)1597 void cmNinjaTargetGenerator::EmitSwiftDependencyInfo(
1598 cmSourceFile const* source, const std::string& config)
1599 {
1600 std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source);
1601 std::string const objectFilePath =
1602 this->ConvertToNinjaPath(this->GetObjectFilePath(source, config));
1603 std::string const swiftDepsPath = [source, objectFilePath]() -> std::string {
1604 if (cmValue name = source->GetProperty("Swift_DEPENDENCIES_FILE")) {
1605 return *name;
1606 }
1607 return cmStrCat(objectFilePath, ".swiftdeps");
1608 }();
1609 std::string const swiftDiaPath = [source, objectFilePath]() -> std::string {
1610 if (cmValue name = source->GetProperty("Swift_DIAGNOSTICS_FILE")) {
1611 return *name;
1612 }
1613 return cmStrCat(objectFilePath, ".dia");
1614 }();
1615 std::string const makeDepsPath = [this, source, config]() -> std::string {
1616 cmLocalNinjaGenerator const* local = this->GetLocalGenerator();
1617 std::string const objectFileName =
1618 this->ConvertToNinjaPath(this->GetObjectFilePath(source, config));
1619 std::string const objectFileDir =
1620 cmSystemTools::GetFilenamePath(objectFileName);
1621
1622 if (this->Makefile->IsOn("CMAKE_Swift_DEPFLE_EXTNSION_REPLACE")) {
1623 std::string dependFileName = cmStrCat(
1624 cmSystemTools::GetFilenameWithoutLastExtension(objectFileName), ".d");
1625 return local->ConvertToOutputFormat(
1626 cmStrCat(objectFileDir, '/', dependFileName),
1627 cmOutputConverter::SHELL);
1628 }
1629 return local->ConvertToOutputFormat(cmStrCat(objectFileName, ".d"),
1630 cmOutputConverter::SHELL);
1631 }();
1632
1633 // build the source file mapping
1634 // https://github.com/apple/swift/blob/master/docs/Driver.md#output-file-maps
1635 Json::Value entry = Json::Value(Json::objectValue);
1636 entry["object"] = objectFilePath;
1637 entry["dependencies"] = makeDepsPath;
1638 entry["swift-dependencies"] = swiftDepsPath;
1639 entry["diagnostics"] = swiftDiaPath;
1640 this->Configs[config].SwiftOutputMap[sourceFilePath] = entry;
1641 }
1642
ExportObjectCompileCommand(std::string const & language,std::string const & sourceFileName,std::string const & objectDir,std::string const & objectFileName,std::string const & objectFileDir,std::string const & flags,std::string const & defines,std::string const & includes,std::string const & outputConfig)1643 void cmNinjaTargetGenerator::ExportObjectCompileCommand(
1644 std::string const& language, std::string const& sourceFileName,
1645 std::string const& objectDir, std::string const& objectFileName,
1646 std::string const& objectFileDir, std::string const& flags,
1647 std::string const& defines, std::string const& includes,
1648 std::string const& outputConfig)
1649 {
1650 if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) {
1651 return;
1652 }
1653
1654 cmRulePlaceholderExpander::RuleVariables compileObjectVars;
1655 compileObjectVars.Language = language.c_str();
1656
1657 std::string escapedSourceFileName = sourceFileName;
1658
1659 if (!cmSystemTools::FileIsFullPath(sourceFileName)) {
1660 escapedSourceFileName =
1661 cmSystemTools::CollapseFullPath(escapedSourceFileName,
1662 this->GetGlobalGenerator()
1663 ->GetCMakeInstance()
1664 ->GetHomeOutputDirectory());
1665 }
1666
1667 escapedSourceFileName = this->LocalGenerator->ConvertToOutputFormat(
1668 escapedSourceFileName, cmOutputConverter::SHELL);
1669
1670 compileObjectVars.Source = escapedSourceFileName.c_str();
1671 compileObjectVars.Object = objectFileName.c_str();
1672 compileObjectVars.ObjectDir = objectDir.c_str();
1673 compileObjectVars.ObjectFileDir = objectFileDir.c_str();
1674 compileObjectVars.Flags = flags.c_str();
1675 compileObjectVars.Defines = defines.c_str();
1676 compileObjectVars.Includes = includes.c_str();
1677
1678 // Rule for compiling object file.
1679 std::vector<std::string> compileCmds;
1680 if (language == "CUDA") {
1681 std::string cmdVar;
1682 if (this->GeneratorTarget->GetPropertyAsBool(
1683 "CUDA_SEPARABLE_COMPILATION")) {
1684 cmdVar = "CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION";
1685 } else if (this->GeneratorTarget->GetPropertyAsBool(
1686 "CUDA_PTX_COMPILATION")) {
1687 cmdVar = "CMAKE_CUDA_COMPILE_PTX_COMPILATION";
1688 } else {
1689 cmdVar = "CMAKE_CUDA_COMPILE_WHOLE_COMPILATION";
1690 }
1691 const std::string& compileCmd =
1692 this->GetMakefile()->GetRequiredDefinition(cmdVar);
1693 cmExpandList(compileCmd, compileCmds);
1694 } else {
1695 const std::string cmdVar = cmStrCat("CMAKE_", language, "_COMPILE_OBJECT");
1696 const std::string& compileCmd =
1697 this->GetMakefile()->GetRequiredDefinition(cmdVar);
1698 cmExpandList(compileCmd, compileCmds);
1699 }
1700
1701 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
1702 this->GetLocalGenerator()->CreateRulePlaceholderExpander());
1703
1704 for (std::string& i : compileCmds) {
1705 // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS
1706 rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), i,
1707 compileObjectVars);
1708 }
1709
1710 std::string cmdLine = this->GetLocalGenerator()->BuildCommandLine(
1711 compileCmds, outputConfig, outputConfig);
1712
1713 this->GetGlobalGenerator()->AddCXXCompileCommand(cmdLine, sourceFileName);
1714 }
1715
AdditionalCleanFiles(const std::string & config)1716 void cmNinjaTargetGenerator::AdditionalCleanFiles(const std::string& config)
1717 {
1718 if (cmValue prop_value =
1719 this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
1720 cmLocalNinjaGenerator* lg = this->LocalGenerator;
1721 std::vector<std::string> cleanFiles;
1722 cmExpandList(cmGeneratorExpression::Evaluate(*prop_value, lg, config,
1723 this->GeneratorTarget),
1724 cleanFiles);
1725 std::string const& binaryDir = lg->GetCurrentBinaryDirectory();
1726 cmGlobalNinjaGenerator* gg = lg->GetGlobalNinjaGenerator();
1727 for (std::string const& cleanFile : cleanFiles) {
1728 // Support relative paths
1729 gg->AddAdditionalCleanFile(
1730 cmSystemTools::CollapseFullPath(cleanFile, binaryDir), config);
1731 }
1732 }
1733 }
1734
GetObjects(const std::string & config) const1735 cmNinjaDeps cmNinjaTargetGenerator::GetObjects(const std::string& config) const
1736 {
1737 auto const it = this->Configs.find(config);
1738 if (it != this->Configs.end()) {
1739 return it->second.Objects;
1740 }
1741 return {};
1742 }
1743
EnsureDirectoryExists(const std::string & path) const1744 void cmNinjaTargetGenerator::EnsureDirectoryExists(
1745 const std::string& path) const
1746 {
1747 if (cmSystemTools::FileIsFullPath(path)) {
1748 cmSystemTools::MakeDirectory(path);
1749 } else {
1750 cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator();
1751 std::string fullPath = gg->GetCMakeInstance()->GetHomeOutputDirectory();
1752 // Also ensures there is a trailing slash.
1753 gg->StripNinjaOutputPathPrefixAsSuffix(fullPath);
1754 fullPath += path;
1755 cmSystemTools::MakeDirectory(fullPath);
1756 }
1757 }
1758
EnsureParentDirectoryExists(const std::string & path) const1759 void cmNinjaTargetGenerator::EnsureParentDirectoryExists(
1760 const std::string& path) const
1761 {
1762 this->EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path));
1763 }
1764
operator ()(cmSourceFile const & source,const char * pkgloc,const std::string & config)1765 void cmNinjaTargetGenerator::MacOSXContentGeneratorType::operator()(
1766 cmSourceFile const& source, const char* pkgloc, const std::string& config)
1767 {
1768 // Skip OS X content when not building a Framework or Bundle.
1769 if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) {
1770 return;
1771 }
1772
1773 std::string macdir =
1774 this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc,
1775 config);
1776
1777 // Reject files that collide with files from the Ninja file's native config.
1778 if (config != this->FileConfig) {
1779 std::string nativeMacdir =
1780 this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(
1781 pkgloc, this->FileConfig);
1782 if (macdir == nativeMacdir) {
1783 return;
1784 }
1785 }
1786
1787 // Get the input file location.
1788 std::string input = source.GetFullPath();
1789 input = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(input);
1790
1791 // Get the output file location.
1792 std::string output =
1793 cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input));
1794 output = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(output);
1795
1796 // Write a build statement to copy the content into the bundle.
1797 this->Generator->GetGlobalGenerator()->WriteMacOSXContentBuild(
1798 input, output, this->FileConfig);
1799
1800 // Add as a dependency to the target so that it gets called.
1801 this->Generator->Configs[config].ExtraFiles.push_back(std::move(output));
1802 }
1803
addPoolNinjaVariable(const std::string & pool_property,cmGeneratorTarget * target,cmNinjaVars & vars)1804 void cmNinjaTargetGenerator::addPoolNinjaVariable(
1805 const std::string& pool_property, cmGeneratorTarget* target,
1806 cmNinjaVars& vars)
1807 {
1808 cmValue pool = target->GetProperty(pool_property);
1809 if (pool) {
1810 vars["pool"] = *pool;
1811 }
1812 }
1813
ForceResponseFile()1814 bool cmNinjaTargetGenerator::ForceResponseFile()
1815 {
1816 static std::string const forceRspFile = "CMAKE_NINJA_FORCE_RESPONSE_FILE";
1817 return (this->GetMakefile()->IsDefinitionSet(forceRspFile) ||
1818 cmSystemTools::HasEnv(forceRspFile));
1819 }
1820