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