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