6248f09a5f1cc28a7bd5ebde2ab929b8857bf476
[platform/upstream/cmake.git] / Source / cmGlobalNinjaGenerator.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 "cmGlobalNinjaGenerator.h"
4
5 #include <algorithm>
6 #include <cctype>
7 #include <cstdio>
8 #include <sstream>
9 #include <utility>
10
11 #include <cm/iterator>
12 #include <cm/memory>
13 #include <cm/optional>
14 #include <cm/string_view>
15 #include <cmext/algorithm>
16 #include <cmext/memory>
17
18 #include <cm3p/json/reader.h>
19 #include <cm3p/json/value.h>
20 #include <cm3p/json/writer.h>
21
22 #include "cmsys/FStream.hxx"
23
24 #include "cmDocumentationEntry.h"
25 #include "cmFortranParser.h"
26 #include "cmGeneratedFileStream.h"
27 #include "cmGeneratorExpressionEvaluationFile.h"
28 #include "cmGeneratorTarget.h"
29 #include "cmGlobalGenerator.h"
30 #include "cmLinkLineComputer.h"
31 #include "cmListFileCache.h"
32 #include "cmLocalGenerator.h"
33 #include "cmLocalNinjaGenerator.h"
34 #include "cmMakefile.h"
35 #include "cmMessageType.h"
36 #include "cmNinjaLinkLineComputer.h"
37 #include "cmOutputConverter.h"
38 #include "cmRange.h"
39 #include "cmScanDepFormat.h"
40 #include "cmState.h"
41 #include "cmStateDirectory.h"
42 #include "cmStateSnapshot.h"
43 #include "cmStateTypes.h"
44 #include "cmStringAlgorithms.h"
45 #include "cmSystemTools.h"
46 #include "cmTarget.h"
47 #include "cmTargetDepend.h"
48 #include "cmValue.h"
49 #include "cmVersion.h"
50 #include "cmake.h"
51
52 const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja";
53 const char* cmGlobalNinjaGenerator::NINJA_RULES_FILE =
54   "CMakeFiles/rules.ninja";
55 const char* cmGlobalNinjaGenerator::INDENT = "  ";
56 #ifdef _WIN32
57 std::string const cmGlobalNinjaGenerator::SHELL_NOOP = "cd .";
58 #else
59 std::string const cmGlobalNinjaGenerator::SHELL_NOOP = ":";
60 #endif
61
62 bool operator==(
63   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
64   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
65 {
66   return lhs.Target == rhs.Target && lhs.Config == rhs.Config &&
67     lhs.GenexOutput == rhs.GenexOutput;
68 }
69
70 bool operator!=(
71   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
72   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
73 {
74   return !(lhs == rhs);
75 }
76
77 bool operator<(
78   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
79   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
80 {
81   return lhs.Target < rhs.Target ||
82     (lhs.Target == rhs.Target &&
83      (lhs.Config < rhs.Config ||
84       (lhs.Config == rhs.Config && lhs.GenexOutput < rhs.GenexOutput)));
85 }
86
87 bool operator>(
88   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
89   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
90 {
91   return rhs < lhs;
92 }
93
94 bool operator<=(
95   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
96   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
97 {
98   return !(lhs > rhs);
99 }
100
101 bool operator>=(
102   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
103   const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
104 {
105   return rhs <= lhs;
106 }
107
108 void cmGlobalNinjaGenerator::Indent(std::ostream& os, int count)
109 {
110   for (int i = 0; i < count; ++i) {
111     os << cmGlobalNinjaGenerator::INDENT;
112   }
113 }
114
115 void cmGlobalNinjaGenerator::WriteDivider(std::ostream& os)
116 {
117   os << "# ======================================"
118         "=======================================\n";
119 }
120
121 void cmGlobalNinjaGenerator::WriteComment(std::ostream& os,
122                                           const std::string& comment)
123 {
124   if (comment.empty()) {
125     return;
126   }
127
128   std::string::size_type lpos = 0;
129   std::string::size_type rpos;
130   os << "\n#############################################\n";
131   while ((rpos = comment.find('\n', lpos)) != std::string::npos) {
132     os << "# " << comment.substr(lpos, rpos - lpos) << "\n";
133     lpos = rpos + 1;
134   }
135   os << "# " << comment.substr(lpos) << "\n\n";
136 }
137
138 std::unique_ptr<cmLinkLineComputer>
139 cmGlobalNinjaGenerator::CreateLinkLineComputer(
140   cmOutputConverter* outputConverter,
141   cmStateDirectory const& /* stateDir */) const
142 {
143   return std::unique_ptr<cmLinkLineComputer>(
144     cm::make_unique<cmNinjaLinkLineComputer>(
145       outputConverter,
146       this->LocalGenerators[0]->GetStateSnapshot().GetDirectory(), this));
147 }
148
149 std::string cmGlobalNinjaGenerator::EncodeRuleName(std::string const& name)
150 {
151   // Ninja rule names must match "[a-zA-Z0-9_.-]+".  Use ".xx" to encode
152   // "." and all invalid characters as hexadecimal.
153   std::string encoded;
154   for (char i : name) {
155     if (isalnum(i) || i == '_' || i == '-') {
156       encoded += i;
157     } else {
158       char buf[16];
159       snprintf(buf, sizeof(buf), ".%02x", static_cast<unsigned int>(i));
160       encoded += buf;
161     }
162   }
163   return encoded;
164 }
165
166 std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string& lit)
167 {
168   std::string result = lit;
169   EncodeLiteralInplace(result);
170   return result;
171 }
172
173 void cmGlobalNinjaGenerator::EncodeLiteralInplace(std::string& lit)
174 {
175   cmSystemTools::ReplaceString(lit, "$", "$$");
176   cmSystemTools::ReplaceString(lit, "\n", "$\n");
177   if (this->IsMultiConfig()) {
178     cmSystemTools::ReplaceString(lit, cmStrCat('$', this->GetCMakeCFGIntDir()),
179                                  this->GetCMakeCFGIntDir());
180   }
181 }
182
183 std::string cmGlobalNinjaGenerator::EncodePath(const std::string& path)
184 {
185   std::string result = path;
186 #ifdef _WIN32
187   if (this->IsGCCOnWindows())
188     std::replace(result.begin(), result.end(), '\\', '/');
189   else
190     std::replace(result.begin(), result.end(), '/', '\\');
191 #endif
192   this->EncodeLiteralInplace(result);
193   cmSystemTools::ReplaceString(result, " ", "$ ");
194   cmSystemTools::ReplaceString(result, ":", "$:");
195   return result;
196 }
197
198 void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os,
199                                         cmNinjaBuild const& build,
200                                         int cmdLineLimit,
201                                         bool* usedResponseFile)
202 {
203   // Make sure there is a rule.
204   if (build.Rule.empty()) {
205     cmSystemTools::Error(cmStrCat(
206       "No rule for WriteBuild! called with comment: ", build.Comment));
207     return;
208   }
209
210   // Make sure there is at least one output file.
211   if (build.Outputs.empty()) {
212     cmSystemTools::Error(cmStrCat(
213       "No output files for WriteBuild! called with comment: ", build.Comment));
214     return;
215   }
216
217   cmGlobalNinjaGenerator::WriteComment(os, build.Comment);
218
219   // Write output files.
220   std::string buildStr("build");
221   {
222     // Write explicit outputs
223     for (std::string const& output : build.Outputs) {
224       buildStr = cmStrCat(buildStr, ' ', this->EncodePath(output));
225       if (this->ComputingUnknownDependencies) {
226         this->CombinedBuildOutputs.insert(output);
227       }
228     }
229     // Write implicit outputs
230     if (!build.ImplicitOuts.empty()) {
231       // Assume Ninja is new enough to support implicit outputs.
232       // Callers should not populate this field otherwise.
233       buildStr = cmStrCat(buildStr, " |");
234       for (std::string const& implicitOut : build.ImplicitOuts) {
235         buildStr = cmStrCat(buildStr, ' ', this->EncodePath(implicitOut));
236         if (this->ComputingUnknownDependencies) {
237           this->CombinedBuildOutputs.insert(implicitOut);
238         }
239       }
240     }
241
242     // Repeat some outputs, but expressed as absolute paths.
243     // This helps Ninja handle absolute paths found in a depfile.
244     // FIXME: Unfortunately this causes Ninja to stat the file twice.
245     // We could avoid this if Ninja Issue 1251 were fixed.
246     if (!build.WorkDirOuts.empty()) {
247       if (this->SupportsImplicitOuts() && build.ImplicitOuts.empty()) {
248         // Make them implicit outputs if supported by this version of Ninja.
249         buildStr = cmStrCat(buildStr, " |");
250       }
251       for (std::string const& workdirOut : build.WorkDirOuts) {
252         buildStr = cmStrCat(buildStr, " ${cmake_ninja_workdir}",
253                             this->EncodePath(workdirOut));
254       }
255     }
256
257     // Write the rule.
258     buildStr = cmStrCat(buildStr, ": ", build.Rule);
259   }
260
261   std::string arguments;
262   {
263     // TODO: Better formatting for when there are multiple input/output files.
264
265     // Write explicit dependencies.
266     for (std::string const& explicitDep : build.ExplicitDeps) {
267       arguments += cmStrCat(' ', this->EncodePath(explicitDep));
268     }
269
270     // Write implicit dependencies.
271     if (!build.ImplicitDeps.empty()) {
272       arguments += " |";
273       for (std::string const& implicitDep : build.ImplicitDeps) {
274         arguments += cmStrCat(' ', this->EncodePath(implicitDep));
275       }
276     }
277
278     // Write order-only dependencies.
279     if (!build.OrderOnlyDeps.empty()) {
280       arguments += " ||";
281       for (std::string const& orderOnlyDep : build.OrderOnlyDeps) {
282         arguments += cmStrCat(' ', this->EncodePath(orderOnlyDep));
283       }
284     }
285
286     arguments += '\n';
287   }
288
289   // Write the variables bound to this build statement.
290   std::string assignments;
291   {
292     std::ostringstream variable_assignments;
293     for (auto const& variable : build.Variables) {
294       cmGlobalNinjaGenerator::WriteVariable(
295         variable_assignments, variable.first, variable.second, "", 1);
296     }
297
298     // check if a response file rule should be used
299     assignments = variable_assignments.str();
300     bool useResponseFile = false;
301     if (cmdLineLimit < 0 ||
302         (cmdLineLimit > 0 &&
303          (arguments.size() + buildStr.size() + assignments.size() + 1000) >
304            static_cast<size_t>(cmdLineLimit))) {
305       variable_assignments.str(std::string());
306       cmGlobalNinjaGenerator::WriteVariable(variable_assignments, "RSP_FILE",
307                                             build.RspFile, "", 1);
308       assignments += variable_assignments.str();
309       useResponseFile = true;
310     }
311     if (usedResponseFile) {
312       *usedResponseFile = useResponseFile;
313     }
314   }
315
316   if (build.Variables.count("dyndep") > 0) {
317     // The ninja 'cleandead' operation does not account for outputs
318     // discovered by 'dyndep' bindings.  Avoid removing them.
319     this->DisableCleandead = true;
320   }
321
322   os << buildStr << arguments << assignments << "\n";
323 }
324
325 void cmGlobalNinjaGenerator::AddCustomCommandRule()
326 {
327   cmNinjaRule rule("CUSTOM_COMMAND");
328   rule.Command = "$COMMAND";
329   rule.Description = "$DESC";
330   rule.Comment = "Rule for running custom commands.";
331   this->AddRule(rule);
332 }
333
334 void cmGlobalNinjaGenerator::CCOutputs::Add(
335   std::vector<std::string> const& paths)
336 {
337   for (std::string const& path : paths) {
338     std::string out = this->GG->ConvertToNinjaPath(path);
339     if (!cmSystemTools::FileIsFullPath(out)) {
340       // This output is expressed as a relative path.  Repeat it,
341       // but expressed as an absolute path for Ninja Issue 1251.
342       this->WorkDirOuts.emplace_back(out);
343       this->GG->SeenCustomCommandOutput(this->GG->ConvertToNinjaAbsPath(path));
344     }
345     this->GG->SeenCustomCommandOutput(out);
346     this->ExplicitOuts.emplace_back(std::move(out));
347   }
348 }
349
350 void cmGlobalNinjaGenerator::WriteCustomCommandBuild(
351   std::string const& command, std::string const& description,
352   std::string const& comment, std::string const& depfile,
353   std::string const& job_pool, bool uses_terminal, bool restat,
354   std::string const& config, CCOutputs outputs, cmNinjaDeps explicitDeps,
355   cmNinjaDeps orderOnlyDeps)
356 {
357   this->AddCustomCommandRule();
358
359   if (this->ComputingUnknownDependencies) {
360     // we need to track every dependency that comes in, since we are trying
361     // to find dependencies that are side effects of build commands
362     for (std::string const& dep : explicitDeps) {
363       this->CombinedCustomCommandExplicitDependencies.insert(dep);
364     }
365   }
366
367   {
368     cmNinjaBuild build("CUSTOM_COMMAND");
369     build.Comment = comment;
370     build.Outputs = std::move(outputs.ExplicitOuts);
371     build.WorkDirOuts = std::move(outputs.WorkDirOuts);
372     build.ExplicitDeps = std::move(explicitDeps);
373     build.OrderOnlyDeps = std::move(orderOnlyDeps);
374
375     cmNinjaVars& vars = build.Variables;
376     {
377       std::string cmd = command; // NOLINT(*)
378 #ifdef _WIN32
379       if (cmd.empty())
380         // TODO Shouldn't an empty command be handled by ninja?
381         cmd = "cmd.exe /c";
382 #endif
383       vars["COMMAND"] = std::move(cmd);
384     }
385     vars["DESC"] = this->EncodeLiteral(description);
386     if (restat) {
387       vars["restat"] = "1";
388     }
389     if (uses_terminal && this->SupportsDirectConsole()) {
390       vars["pool"] = "console";
391     } else if (!job_pool.empty()) {
392       vars["pool"] = job_pool;
393     }
394     if (!depfile.empty()) {
395       vars["depfile"] = depfile;
396     }
397     if (config.empty()) {
398       this->WriteBuild(*this->GetCommonFileStream(), build);
399     } else {
400       this->WriteBuild(*this->GetImplFileStream(config), build);
401     }
402   }
403 }
404
405 void cmGlobalNinjaGenerator::AddMacOSXContentRule()
406 {
407   cmNinjaRule rule("COPY_OSX_CONTENT");
408   rule.Command = cmStrCat(this->CMakeCmd(), " -E copy $in $out");
409   rule.Description = "Copying OS X Content $out";
410   rule.Comment = "Rule for copying OS X bundle content file.";
411   this->AddRule(rule);
412 }
413
414 void cmGlobalNinjaGenerator::WriteMacOSXContentBuild(std::string input,
415                                                      std::string output,
416                                                      const std::string& config)
417 {
418   this->AddMacOSXContentRule();
419   {
420     cmNinjaBuild build("COPY_OSX_CONTENT");
421     build.Outputs.push_back(std::move(output));
422     build.ExplicitDeps.push_back(std::move(input));
423     this->WriteBuild(*this->GetImplFileStream(config), build);
424   }
425 }
426
427 void cmGlobalNinjaGenerator::WriteRule(std::ostream& os,
428                                        cmNinjaRule const& rule)
429 {
430   // -- Parameter checks
431   // Make sure the rule has a name.
432   if (rule.Name.empty()) {
433     cmSystemTools::Error(cmStrCat(
434       "No name given for WriteRule! called with comment: ", rule.Comment));
435     return;
436   }
437
438   // Make sure a command is given.
439   if (rule.Command.empty()) {
440     cmSystemTools::Error(cmStrCat(
441       "No command given for WriteRule! called with comment: ", rule.Comment));
442     return;
443   }
444
445   // Make sure response file content is given
446   if (!rule.RspFile.empty() && rule.RspContent.empty()) {
447     cmSystemTools::Error(
448       cmStrCat("rspfile but no rspfile_content given for WriteRule! "
449                "called with comment: ",
450                rule.Comment));
451     return;
452   }
453
454   // -- Write rule
455   // Write rule intro
456   cmGlobalNinjaGenerator::WriteComment(os, rule.Comment);
457   os << "rule " << rule.Name << '\n';
458
459   // Write rule key/value pairs
460   auto writeKV = [&os](const char* key, std::string const& value) {
461     if (!value.empty()) {
462       cmGlobalNinjaGenerator::Indent(os, 1);
463       os << key << " = " << value << '\n';
464     }
465   };
466
467   writeKV("depfile", rule.DepFile);
468   writeKV("deps", rule.DepType);
469   writeKV("command", rule.Command);
470   writeKV("description", rule.Description);
471   if (!rule.RspFile.empty()) {
472     writeKV("rspfile", rule.RspFile);
473     writeKV("rspfile_content", rule.RspContent);
474   }
475   writeKV("restat", rule.Restat);
476   if (rule.Generator) {
477     writeKV("generator", "1");
478   }
479
480   // Finish rule
481   os << '\n';
482 }
483
484 void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os,
485                                            const std::string& name,
486                                            const std::string& value,
487                                            const std::string& comment,
488                                            int indent)
489 {
490   // Make sure we have a name.
491   if (name.empty()) {
492     cmSystemTools::Error(cmStrCat("No name given for WriteVariable! called "
493                                   "with comment: ",
494                                   comment));
495     return;
496   }
497
498   // Do not add a variable if the value is empty.
499   std::string val = cmTrimWhitespace(value);
500   if (val.empty()) {
501     return;
502   }
503
504   cmGlobalNinjaGenerator::WriteComment(os, comment);
505   cmGlobalNinjaGenerator::Indent(os, indent);
506   os << name << " = " << val << "\n";
507 }
508
509 void cmGlobalNinjaGenerator::WriteInclude(std::ostream& os,
510                                           const std::string& filename,
511                                           const std::string& comment)
512 {
513   cmGlobalNinjaGenerator::WriteComment(os, comment);
514   os << "include " << filename << "\n";
515 }
516
517 void cmGlobalNinjaGenerator::WriteDefault(std::ostream& os,
518                                           const cmNinjaDeps& targets,
519                                           const std::string& comment)
520 {
521   cmGlobalNinjaGenerator::WriteComment(os, comment);
522   os << "default";
523   for (std::string const& target : targets) {
524     os << " " << target;
525   }
526   os << "\n";
527 }
528
529 cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm)
530   : cmGlobalCommonGenerator(cm)
531 {
532 #ifdef _WIN32
533   cm->GetState()->SetWindowsShell(true);
534 #endif
535   this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
536 }
537
538 // Virtual public methods.
539
540 std::unique_ptr<cmLocalGenerator> cmGlobalNinjaGenerator::CreateLocalGenerator(
541   cmMakefile* mf)
542 {
543   return std::unique_ptr<cmLocalGenerator>(
544     cm::make_unique<cmLocalNinjaGenerator>(this, mf));
545 }
546
547 codecvt::Encoding cmGlobalNinjaGenerator::GetMakefileEncoding() const
548 {
549   return this->NinjaExpectedEncoding;
550 }
551
552 void cmGlobalNinjaGenerator::GetDocumentation(cmDocumentationEntry& entry)
553 {
554   entry.Name = cmGlobalNinjaGenerator::GetActualName();
555   entry.Brief = "Generates build.ninja files.";
556 }
557
558 // Implemented in all cmGlobaleGenerator sub-classes.
559 // Used in:
560 //   Source/cmLocalGenerator.cxx
561 //   Source/cmake.cxx
562 void cmGlobalNinjaGenerator::Generate()
563 {
564   // Check minimum Ninja version.
565   if (cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
566                                     RequiredNinjaVersion())) {
567     std::ostringstream msg;
568     msg << "The detected version of Ninja (" << this->NinjaVersion;
569     msg << ") is less than the version of Ninja required by CMake (";
570     msg << cmGlobalNinjaGenerator::RequiredNinjaVersion() << ").";
571     this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
572                                            msg.str());
573     return;
574   }
575   if (!this->OpenBuildFileStreams()) {
576     return;
577   }
578   if (!this->OpenRulesFileStream()) {
579     return;
580   }
581
582   for (auto& it : this->Configs) {
583     it.second.TargetDependsClosures.clear();
584   }
585
586   this->InitOutputPathPrefix();
587   this->TargetAll = this->NinjaOutputPath("all");
588   this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt");
589   this->DisableCleandead = false;
590   this->DiagnosedCxxModuleSupport = false;
591
592   this->PolicyCMP0058 =
593     this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus(
594       cmPolicies::CMP0058);
595   this->ComputingUnknownDependencies =
596     (this->PolicyCMP0058 == cmPolicies::OLD ||
597      this->PolicyCMP0058 == cmPolicies::WARN);
598
599   this->cmGlobalGenerator::Generate();
600
601   this->WriteAssumedSourceDependencies();
602   this->WriteTargetAliases(*this->GetCommonFileStream());
603   this->WriteFolderTargets(*this->GetCommonFileStream());
604   this->WriteUnknownExplicitDependencies(*this->GetCommonFileStream());
605   this->WriteBuiltinTargets(*this->GetCommonFileStream());
606
607   if (cmSystemTools::GetErrorOccurredFlag()) {
608     this->RulesFileStream->setstate(std::ios::failbit);
609     for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs(
610            cmMakefile::IncludeEmptyConfig)) {
611       this->GetImplFileStream(config)->setstate(std::ios::failbit);
612       this->GetConfigFileStream(config)->setstate(std::ios::failbit);
613     }
614     this->GetCommonFileStream()->setstate(std::ios::failbit);
615   }
616
617   this->CloseCompileCommandsStream();
618   this->CloseRulesFileStream();
619   this->CloseBuildFileStreams();
620
621 #ifdef _WIN32
622   // Older ninja tools will not be able to update metadata on Windows
623   // when we are re-generating inside an existing 'ninja' invocation
624   // because the outer tool has the files open for write.
625   if (this->NinjaSupportsMetadataOnRegeneration ||
626       !this->GetCMakeInstance()->GetRegenerateDuringBuild())
627 #endif
628   {
629     this->CleanMetaData();
630   }
631 }
632
633 void cmGlobalNinjaGenerator::CleanMetaData()
634 {
635   auto run_ninja_tool = [this](std::vector<char const*> const& args) {
636     std::vector<std::string> command;
637     command.push_back(this->NinjaCommand);
638     command.emplace_back("-C");
639     command.emplace_back(this->GetCMakeInstance()->GetHomeOutputDirectory());
640     command.emplace_back("-t");
641     for (auto const& arg : args) {
642       command.emplace_back(arg);
643     }
644     std::string error;
645     if (!cmSystemTools::RunSingleCommand(command, nullptr, &error, nullptr,
646                                          nullptr,
647                                          cmSystemTools::OUTPUT_NONE)) {
648       this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
649                                              cmStrCat("Running\n '",
650                                                       cmJoin(command, "' '"),
651                                                       "'\n"
652                                                       "failed with:\n ",
653                                                       error));
654       cmSystemTools::SetFatalErrorOccurred();
655     }
656   };
657
658   // Can the tools below expect 'build.ninja' to be loadable?
659   bool const expectBuildManifest =
660     !this->IsMultiConfig() && this->OutputPathPrefix.empty();
661
662   // Skip some ninja tools if they need 'build.ninja' but it is missing.
663   bool const missingBuildManifest = expectBuildManifest &&
664     this->NinjaSupportsUnconditionalRecompactTool &&
665     !cmSystemTools::FileExists("build.ninja");
666
667   // The `recompact` tool loads the manifest. As above, we don't have a single
668   // `build.ninja` to load for this in Ninja-Multi. This may be relaxed in the
669   // future pending further investigation into how Ninja works upstream
670   // (ninja#1721).
671   if (this->NinjaSupportsUnconditionalRecompactTool &&
672       !this->GetCMakeInstance()->GetRegenerateDuringBuild() &&
673       expectBuildManifest && !missingBuildManifest) {
674     run_ninja_tool({ "recompact" });
675   }
676   if (this->NinjaSupportsRestatTool && this->OutputPathPrefix.empty()) {
677     // XXX(ninja): We only list `build.ninja` entry files here because CMake
678     // *always* rewrites these files on a reconfigure. If CMake ever gets
679     // smarter about this, all CMake-time created/edited files listed as
680     // outputs for the reconfigure build statement will need to be listed here.
681     cmNinjaDeps outputs;
682     this->AddRebuildManifestOutputs(outputs);
683     std::vector<const char*> args;
684     args.reserve(outputs.size() + 1);
685     args.push_back("restat");
686     for (auto const& output : outputs) {
687       args.push_back(output.c_str());
688     }
689     run_ninja_tool(args);
690   }
691 }
692
693 bool cmGlobalNinjaGenerator::FindMakeProgram(cmMakefile* mf)
694 {
695   if (!this->cmGlobalGenerator::FindMakeProgram(mf)) {
696     return false;
697   }
698   if (cmValue ninjaCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) {
699     this->NinjaCommand = *ninjaCommand;
700     std::vector<std::string> command;
701     command.push_back(this->NinjaCommand);
702     command.emplace_back("--version");
703     std::string version;
704     std::string error;
705     if (!cmSystemTools::RunSingleCommand(command, &version, &error, nullptr,
706                                          nullptr,
707                                          cmSystemTools::OUTPUT_NONE)) {
708       mf->IssueMessage(MessageType::FATAL_ERROR,
709                        cmStrCat("Running\n '", cmJoin(command, "' '"),
710                                 "'\n"
711                                 "failed with:\n ",
712                                 error));
713       cmSystemTools::SetFatalErrorOccurred();
714       return false;
715     }
716     this->NinjaVersion = cmTrimWhitespace(version);
717     this->CheckNinjaFeatures();
718   }
719   return true;
720 }
721
722 void cmGlobalNinjaGenerator::CheckNinjaFeatures()
723 {
724   this->NinjaSupportsConsolePool =
725     !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
726                                    RequiredNinjaVersionForConsolePool());
727   this->NinjaSupportsImplicitOuts = !cmSystemTools::VersionCompare(
728     cmSystemTools::OP_LESS, this->NinjaVersion,
729     cmGlobalNinjaGenerator::RequiredNinjaVersionForImplicitOuts());
730   this->NinjaSupportsManifestRestat =
731     !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
732                                    RequiredNinjaVersionForManifestRestat());
733   this->NinjaSupportsMultilineDepfile =
734     !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
735                                    RequiredNinjaVersionForMultilineDepfile());
736   this->NinjaSupportsDyndeps =
737     !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
738                                    RequiredNinjaVersionForDyndeps());
739   if (!this->NinjaSupportsDyndeps) {
740     // The ninja version number is not new enough to have upstream support.
741     // Our ninja branch adds ".dyndep-#" to its version number,
742     // where '#' is a feature-specific version number.  Extract it.
743     static std::string const k_DYNDEP_ = ".dyndep-";
744     std::string::size_type pos = this->NinjaVersion.find(k_DYNDEP_);
745     if (pos != std::string::npos) {
746       const char* fv = &this->NinjaVersion[pos + k_DYNDEP_.size()];
747       unsigned long dyndep = 0;
748       cmStrToULong(fv, &dyndep);
749       if (dyndep == 1) {
750         this->NinjaSupportsDyndeps = true;
751       }
752     }
753   }
754   this->NinjaSupportsUnconditionalRecompactTool =
755     !cmSystemTools::VersionCompare(
756       cmSystemTools::OP_LESS, this->NinjaVersion,
757       RequiredNinjaVersionForUnconditionalRecompactTool());
758   this->NinjaSupportsRestatTool =
759     !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
760                                    RequiredNinjaVersionForRestatTool());
761   this->NinjaSupportsMultipleOutputs =
762     !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
763                                    RequiredNinjaVersionForMultipleOutputs());
764   this->NinjaSupportsMetadataOnRegeneration = !cmSystemTools::VersionCompare(
765     cmSystemTools::OP_LESS, this->NinjaVersion,
766     RequiredNinjaVersionForMetadataOnRegeneration());
767 #ifdef _WIN32
768   this->NinjaSupportsCodePage =
769     !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
770                                    RequiredNinjaVersionForCodePage());
771   if (this->NinjaSupportsCodePage) {
772     this->CheckNinjaCodePage();
773   } else {
774     this->NinjaExpectedEncoding = codecvt::ANSI;
775   }
776 #endif
777 }
778
779 void cmGlobalNinjaGenerator::CheckNinjaCodePage()
780 {
781   std::vector<std::string> command{ this->NinjaCommand, "-t", "wincodepage" };
782   std::string output;
783   std::string error;
784   int result;
785   if (!cmSystemTools::RunSingleCommand(command, &output, &error, &result,
786                                        nullptr, cmSystemTools::OUTPUT_NONE)) {
787     this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
788                                            cmStrCat("Running\n '",
789                                                     cmJoin(command, "' '"),
790                                                     "'\n"
791                                                     "failed with:\n ",
792                                                     error));
793     cmSystemTools::SetFatalErrorOccurred();
794   } else if (result == 0) {
795     std::istringstream outputStream(output);
796     std::string line;
797     bool found = false;
798     while (cmSystemTools::GetLineFromStream(outputStream, line)) {
799       if (cmHasLiteralPrefix(line, "Build file encoding: ")) {
800         cm::string_view lineView(line);
801         cm::string_view encoding =
802           lineView.substr(cmStrLen("Build file encoding: "));
803         if (encoding == "UTF-8") {
804           // Ninja expects UTF-8. We use that internally. No conversion needed.
805           this->NinjaExpectedEncoding = codecvt::None;
806         } else {
807           this->NinjaExpectedEncoding = codecvt::ANSI;
808         }
809         found = true;
810         break;
811       }
812     }
813     if (!found) {
814       this->GetCMakeInstance()->IssueMessage(
815         MessageType::WARNING,
816         "Could not determine Ninja's code page, defaulting to UTF-8");
817       this->NinjaExpectedEncoding = codecvt::None;
818     }
819   } else {
820     this->NinjaExpectedEncoding = codecvt::ANSI;
821   }
822 }
823
824 bool cmGlobalNinjaGenerator::CheckLanguages(
825   std::vector<std::string> const& languages, cmMakefile* mf) const
826 {
827   if (cm::contains(languages, "Fortran")) {
828     return this->CheckFortran(mf);
829   }
830   if (cm::contains(languages, "ISPC")) {
831     return this->CheckISPC(mf);
832   }
833   if (cm::contains(languages, "Swift")) {
834     const std::string architectures =
835       mf->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES");
836     if (architectures.find_first_of(';') != std::string::npos) {
837       mf->IssueMessage(MessageType::FATAL_ERROR,
838                        "multiple values for CMAKE_OSX_ARCHITECTURES not "
839                        "supported with Swift");
840       cmSystemTools::SetFatalErrorOccurred();
841       return false;
842     }
843   }
844   return true;
845 }
846
847 bool cmGlobalNinjaGenerator::CheckCxxModuleSupport()
848 {
849   bool const diagnose = !this->DiagnosedCxxModuleSupport &&
850     !this->CMakeInstance->GetIsInTryCompile();
851   if (diagnose) {
852     this->DiagnosedCxxModuleSupport = true;
853     this->GetCMakeInstance()->IssueMessage(
854       MessageType::AUTHOR_WARNING,
855       "C++20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP "
856       "is experimental.  It is meant only for compiler developers to try.");
857   }
858   if (this->NinjaSupportsDyndeps) {
859     return true;
860   }
861   if (diagnose) {
862     std::ostringstream e;
863     /* clang-format off */
864     e <<
865       "The Ninja generator does not support C++20 modules "
866       "using Ninja version \n"
867       "  " << this->NinjaVersion << "\n"
868       "due to lack of required features.  "
869       "Ninja " << RequiredNinjaVersionForDyndeps() << " or higher is required."
870       ;
871     /* clang-format on */
872     this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str());
873     cmSystemTools::SetFatalErrorOccurred();
874   }
875   return false;
876 }
877
878 bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const
879 {
880   if (this->NinjaSupportsDyndeps) {
881     return true;
882   }
883
884   std::ostringstream e;
885   /* clang-format off */
886   e <<
887     "The Ninja generator does not support Fortran using Ninja version\n"
888     "  " << this->NinjaVersion << "\n"
889     "due to lack of required features.  "
890     "Ninja " << RequiredNinjaVersionForDyndeps() << " or higher is required."
891     ;
892   /* clang-format on */
893   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
894   cmSystemTools::SetFatalErrorOccurred();
895   return false;
896 }
897
898 bool cmGlobalNinjaGenerator::CheckISPC(cmMakefile* mf) const
899 {
900   if (this->NinjaSupportsMultipleOutputs) {
901     return true;
902   }
903
904   std::ostringstream e;
905   /* clang-format off */
906   e <<
907     "The Ninja generator does not support ISPC using Ninja version\n"
908     "  " << this->NinjaVersion << "\n"
909     "due to lack of required features.  "
910     "Ninja " << RequiredNinjaVersionForMultipleOutputs() <<
911     " or higher is required."
912     ;
913   /* clang-format on */
914   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
915   cmSystemTools::SetFatalErrorOccurred();
916   return false;
917 }
918
919 void cmGlobalNinjaGenerator::EnableLanguage(
920   std::vector<std::string> const& langs, cmMakefile* mf, bool optional)
921 {
922   if (this->IsMultiConfig()) {
923     mf->InitCMAKE_CONFIGURATION_TYPES("Debug;Release;RelWithDebInfo");
924   }
925
926   this->cmGlobalGenerator::EnableLanguage(langs, mf, optional);
927   for (std::string const& l : langs) {
928     if (l == "NONE") {
929       continue;
930     }
931     this->ResolveLanguageCompiler(l, mf, optional);
932 #ifdef _WIN32
933     std::string const& compilerId =
934       mf->GetSafeDefinition(cmStrCat("CMAKE_", l, "_COMPILER_ID"));
935     std::string const& simulateId =
936       mf->GetSafeDefinition(cmStrCat("CMAKE_", l, "_SIMULATE_ID"));
937     std::string const& compilerFrontendVariant = mf->GetSafeDefinition(
938       cmStrCat("CMAKE_", l, "_COMPILER_FRONTEND_VARIANT"));
939     if ((compilerId == "Clang" && compilerFrontendVariant == "GNU") ||
940         (simulateId != "MSVC" &&
941          (compilerId == "GNU" || compilerId == "QCC" ||
942           cmHasLiteralSuffix(compilerId, "Clang")))) {
943       this->UsingGCCOnWindows = true;
944     }
945 #endif
946   }
947 }
948
949 // Implemented by:
950 //   cmGlobalUnixMakefileGenerator3
951 //   cmGlobalGhsMultiGenerator
952 //   cmGlobalVisualStudio10Generator
953 //   cmGlobalVisualStudio7Generator
954 //   cmGlobalXCodeGenerator
955 // Called by:
956 //   cmGlobalGenerator::Build()
957 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
958 cmGlobalNinjaGenerator::GenerateBuildCommand(
959   const std::string& makeProgram, const std::string& /*projectName*/,
960   const std::string& /*projectDir*/,
961   std::vector<std::string> const& targetNames, const std::string& config,
962   int jobs, bool verbose, const cmBuildOptions& /*buildOptions*/,
963   std::vector<std::string> const& makeOptions)
964 {
965   GeneratedMakeCommand makeCommand;
966   makeCommand.Add(this->SelectMakeProgram(makeProgram));
967
968   if (verbose) {
969     makeCommand.Add("-v");
970   }
971
972   if ((jobs != cmake::NO_BUILD_PARALLEL_LEVEL) &&
973       (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL)) {
974     makeCommand.Add("-j", std::to_string(jobs));
975   }
976
977   this->AppendNinjaFileArgument(makeCommand, config);
978
979   makeCommand.Add(makeOptions.begin(), makeOptions.end());
980   for (const auto& tname : targetNames) {
981     if (!tname.empty()) {
982       makeCommand.Add(tname);
983     }
984   }
985   return { std::move(makeCommand) };
986 }
987
988 // Non-virtual public methods.
989
990 void cmGlobalNinjaGenerator::AddRule(cmNinjaRule const& rule)
991 {
992   // Do not add the same rule twice.
993   if (!this->Rules.insert(rule.Name).second) {
994     return;
995   }
996   // Store command length
997   this->RuleCmdLength[rule.Name] = static_cast<int>(rule.Command.size());
998   // Write rule
999   cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream, rule);
1000 }
1001
1002 bool cmGlobalNinjaGenerator::HasRule(const std::string& name)
1003 {
1004   return (this->Rules.find(name) != this->Rules.end());
1005 }
1006
1007 // Private virtual overrides
1008
1009 void cmGlobalNinjaGenerator::ComputeTargetObjectDirectory(
1010   cmGeneratorTarget* gt) const
1011 {
1012   // Compute full path to object file directory for this target.
1013   std::string dir = cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(),
1014                              '/', gt->LocalGenerator->GetTargetDirectory(gt),
1015                              '/', this->GetCMakeCFGIntDir(), '/');
1016   gt->ObjectDirectory = dir;
1017 }
1018
1019 // Private methods
1020
1021 bool cmGlobalNinjaGenerator::OpenBuildFileStreams()
1022 {
1023   if (!this->OpenFileStream(this->BuildFileStream,
1024                             cmGlobalNinjaGenerator::NINJA_BUILD_FILE)) {
1025     return false;
1026   }
1027
1028   // Write a comment about this file.
1029   *this->BuildFileStream
1030     << "# This file contains all the build statements describing the\n"
1031     << "# compilation DAG.\n\n";
1032
1033   return true;
1034 }
1035
1036 bool cmGlobalNinjaGenerator::OpenFileStream(
1037   std::unique_ptr<cmGeneratedFileStream>& stream, const std::string& name)
1038 {
1039   // Get a stream where to generate things.
1040   if (!stream) {
1041     // Compute Ninja's build file path.
1042     std::string path =
1043       cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), '/', name);
1044     stream = cm::make_unique<cmGeneratedFileStream>(
1045       path, false, this->GetMakefileEncoding());
1046     if (!(*stream)) {
1047       // An error message is generated by the constructor if it cannot
1048       // open the file.
1049       return false;
1050     }
1051
1052     // Write the do not edit header.
1053     this->WriteDisclaimer(*stream);
1054   }
1055
1056   return true;
1057 }
1058
1059 cm::optional<std::set<std::string>> cmGlobalNinjaGenerator::ListSubsetWithAll(
1060   const std::set<std::string>& all, const std::set<std::string>& defaults,
1061   const std::vector<std::string>& items)
1062 {
1063   std::set<std::string> result;
1064
1065   for (auto const& item : items) {
1066     if (item == "all") {
1067       if (items.size() == 1) {
1068         result = defaults;
1069       } else {
1070         return cm::nullopt;
1071       }
1072     } else if (all.count(item)) {
1073       result.insert(item);
1074     } else {
1075       return cm::nullopt;
1076     }
1077   }
1078
1079   return cm::make_optional(result);
1080 }
1081
1082 void cmGlobalNinjaGenerator::CloseBuildFileStreams()
1083 {
1084   if (this->BuildFileStream) {
1085     this->BuildFileStream.reset();
1086   } else {
1087     cmSystemTools::Error("Build file stream was not open.");
1088   }
1089 }
1090
1091 bool cmGlobalNinjaGenerator::OpenRulesFileStream()
1092 {
1093   if (!this->OpenFileStream(this->RulesFileStream,
1094                             cmGlobalNinjaGenerator::NINJA_RULES_FILE)) {
1095     return false;
1096   }
1097
1098   // Write comment about this file.
1099   /* clang-format off */
1100   *this->RulesFileStream
1101     << "# This file contains all the rules used to get the outputs files\n"
1102     << "# built from the input files.\n"
1103     << "# It is included in the main '" << NINJA_BUILD_FILE << "'.\n\n"
1104     ;
1105   /* clang-format on */
1106   return true;
1107 }
1108
1109 void cmGlobalNinjaGenerator::CloseRulesFileStream()
1110 {
1111   if (this->RulesFileStream) {
1112     this->RulesFileStream.reset();
1113   } else {
1114     cmSystemTools::Error("Rules file stream was not open.");
1115   }
1116 }
1117
1118 static void EnsureTrailingSlash(std::string& path)
1119 {
1120   if (path.empty()) {
1121     return;
1122   }
1123   std::string::value_type last = path.back();
1124 #ifdef _WIN32
1125   if (last != '\\') {
1126     path += '\\';
1127   }
1128 #else
1129   if (last != '/') {
1130     path += '/';
1131   }
1132 #endif
1133 }
1134
1135 std::string const& cmGlobalNinjaGenerator::ConvertToNinjaPath(
1136   const std::string& path) const
1137 {
1138   auto const f = this->ConvertToNinjaPathCache.find(path);
1139   if (f != this->ConvertToNinjaPathCache.end()) {
1140     return f->second;
1141   }
1142
1143   std::string convPath =
1144     this->LocalGenerators[0]->MaybeRelativeToTopBinDir(path);
1145   convPath = this->NinjaOutputPath(convPath);
1146 #ifdef _WIN32
1147   std::replace(convPath.begin(), convPath.end(), '/', '\\');
1148 #endif
1149   return this->ConvertToNinjaPathCache.emplace(path, std::move(convPath))
1150     .first->second;
1151 }
1152
1153 std::string cmGlobalNinjaGenerator::ConvertToNinjaAbsPath(
1154   std::string path) const
1155 {
1156 #ifdef _WIN32
1157   std::replace(path.begin(), path.end(), '/', '\\');
1158 #endif
1159   return path;
1160 }
1161
1162 void cmGlobalNinjaGenerator::AddAdditionalCleanFile(std::string fileName,
1163                                                     const std::string& config)
1164 {
1165   this->Configs[config].AdditionalCleanFiles.emplace(std::move(fileName));
1166 }
1167
1168 void cmGlobalNinjaGenerator::AddCXXCompileCommand(
1169   const std::string& commandLine, const std::string& sourceFile)
1170 {
1171   // Compute Ninja's build file path.
1172   std::string buildFileDir =
1173     this->GetCMakeInstance()->GetHomeOutputDirectory();
1174   if (!this->CompileCommandsStream) {
1175     std::string buildFilePath =
1176       cmStrCat(buildFileDir, "/compile_commands.json");
1177     if (this->ComputingUnknownDependencies) {
1178       this->CombinedBuildOutputs.insert(
1179         this->NinjaOutputPath("compile_commands.json"));
1180     }
1181
1182     // Get a stream where to generate things.
1183     this->CompileCommandsStream =
1184       cm::make_unique<cmGeneratedFileStream>(buildFilePath);
1185     *this->CompileCommandsStream << "[\n";
1186   } else {
1187     *this->CompileCommandsStream << ",\n";
1188   }
1189
1190   std::string sourceFileName = sourceFile;
1191   if (!cmSystemTools::FileIsFullPath(sourceFileName)) {
1192     sourceFileName = cmSystemTools::CollapseFullPath(
1193       sourceFileName, this->GetCMakeInstance()->GetHomeOutputDirectory());
1194   }
1195
1196   /* clang-format off */
1197   *this->CompileCommandsStream << "{\n"
1198      << R"(  "directory": ")"
1199      << cmGlobalGenerator::EscapeJSON(buildFileDir) << "\",\n"
1200      << R"(  "command": ")"
1201      << cmGlobalGenerator::EscapeJSON(commandLine) << "\",\n"
1202      << R"(  "file": ")"
1203      << cmGlobalGenerator::EscapeJSON(sourceFileName) << "\"\n"
1204      << "}";
1205   /* clang-format on */
1206 }
1207
1208 void cmGlobalNinjaGenerator::CloseCompileCommandsStream()
1209 {
1210   if (this->CompileCommandsStream) {
1211     *this->CompileCommandsStream << "\n]";
1212     this->CompileCommandsStream.reset();
1213   }
1214 }
1215
1216 void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os) const
1217 {
1218   os << "# CMAKE generated file: DO NOT EDIT!\n"
1219      << "# Generated by \"" << this->GetName() << "\""
1220      << " Generator, CMake Version " << cmVersion::GetMajorVersion() << "."
1221      << cmVersion::GetMinorVersion() << "\n\n";
1222 }
1223
1224 void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
1225 {
1226   for (auto const& asd : this->AssumedSourceDependencies) {
1227     CCOutputs outputs(this);
1228     outputs.ExplicitOuts.emplace_back(asd.first);
1229     cmNinjaDeps orderOnlyDeps;
1230     std::copy(asd.second.begin(), asd.second.end(),
1231               std::back_inserter(orderOnlyDeps));
1232     this->WriteCustomCommandBuild(
1233       /*command=*/"", /*description=*/"",
1234       "Assume dependencies for generated source file.",
1235       /*depfile*/ "", /*job_pool*/ "",
1236       /*uses_terminal*/ false,
1237       /*restat*/ true, std::string(), outputs, cmNinjaDeps(),
1238       std::move(orderOnlyDeps));
1239   }
1240 }
1241
1242 std::string cmGlobalNinjaGenerator::OrderDependsTargetForTarget(
1243   cmGeneratorTarget const* target, const std::string& /*config*/) const
1244 {
1245   return cmStrCat("cmake_object_order_depends_target_", target->GetName());
1246 }
1247
1248 void cmGlobalNinjaGenerator::AppendTargetOutputs(
1249   cmGeneratorTarget const* target, cmNinjaDeps& outputs,
1250   const std::string& config, cmNinjaTargetDepends depends) const
1251 {
1252   // for frameworks, we want the real name, not smple name
1253   // frameworks always appear versioned, and the build.ninja
1254   // will always attempt to manage symbolic links instead
1255   // of letting cmOSXBundleGenerator do it.
1256   bool realname = target->IsFrameworkOnApple();
1257
1258   switch (target->GetType()) {
1259     case cmStateEnums::SHARED_LIBRARY:
1260     case cmStateEnums::STATIC_LIBRARY:
1261     case cmStateEnums::MODULE_LIBRARY: {
1262       if (depends == DependOnTargetOrdering) {
1263         outputs.push_back(this->OrderDependsTargetForTarget(target, config));
1264         break;
1265       }
1266     }
1267       CM_FALLTHROUGH;
1268     case cmStateEnums::EXECUTABLE: {
1269       outputs.push_back(this->ConvertToNinjaPath(target->GetFullPath(
1270         config, cmStateEnums::RuntimeBinaryArtifact, realname)));
1271       break;
1272     }
1273     case cmStateEnums::OBJECT_LIBRARY: {
1274       if (depends == DependOnTargetOrdering) {
1275         outputs.push_back(this->OrderDependsTargetForTarget(target, config));
1276         break;
1277       }
1278     }
1279       CM_FALLTHROUGH;
1280     case cmStateEnums::GLOBAL_TARGET:
1281     case cmStateEnums::INTERFACE_LIBRARY:
1282     case cmStateEnums::UTILITY: {
1283       std::string path =
1284         cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/',
1285                  target->GetName());
1286       std::string output = this->ConvertToNinjaPath(path);
1287       if (target->Target->IsPerConfig()) {
1288         output = this->BuildAlias(output, config);
1289       }
1290       outputs.push_back(output);
1291       break;
1292     }
1293
1294     case cmStateEnums::UNKNOWN_LIBRARY:
1295       break;
1296   }
1297 }
1298
1299 void cmGlobalNinjaGenerator::AppendTargetDepends(
1300   cmGeneratorTarget const* target, cmNinjaDeps& outputs,
1301   const std::string& config, const std::string& fileConfig,
1302   cmNinjaTargetDepends depends)
1303 {
1304   if (target->GetType() == cmStateEnums::GLOBAL_TARGET) {
1305     // These depend only on other CMake-provided targets, e.g. "all".
1306     for (BT<std::pair<std::string, bool>> const& util :
1307          target->GetUtilities()) {
1308       std::string d =
1309         cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/',
1310                  util.Value.first);
1311       outputs.push_back(this->BuildAlias(this->ConvertToNinjaPath(d), config));
1312     }
1313   } else {
1314     cmNinjaDeps outs;
1315
1316     auto computeISPCOuputs = [](cmGlobalNinjaGenerator* gg,
1317                                 cmGeneratorTarget const* depTarget,
1318                                 cmNinjaDeps& outputDeps,
1319                                 const std::string& targetConfig) {
1320       if (depTarget->CanCompileSources()) {
1321         auto headers = depTarget->GetGeneratedISPCHeaders(targetConfig);
1322         if (!headers.empty()) {
1323           std::transform(headers.begin(), headers.end(), headers.begin(),
1324                          gg->MapToNinjaPath());
1325           outputDeps.insert(outputDeps.end(), headers.begin(), headers.end());
1326         }
1327         auto objs = depTarget->GetGeneratedISPCObjects(targetConfig);
1328         if (!objs.empty()) {
1329           std::transform(objs.begin(), objs.end(), objs.begin(),
1330                          gg->MapToNinjaPath());
1331           outputDeps.insert(outputDeps.end(), objs.begin(), objs.end());
1332         }
1333       }
1334     };
1335
1336     for (cmTargetDepend const& targetDep :
1337          this->GetTargetDirectDepends(target)) {
1338       if (!targetDep->IsInBuildSystem()) {
1339         continue;
1340       }
1341       if (targetDep.IsCross()) {
1342         this->AppendTargetOutputs(targetDep, outs, fileConfig, depends);
1343         computeISPCOuputs(this, targetDep, outs, fileConfig);
1344       } else {
1345         this->AppendTargetOutputs(targetDep, outs, config, depends);
1346         computeISPCOuputs(this, targetDep, outs, config);
1347       }
1348     }
1349     std::sort(outs.begin(), outs.end());
1350     cm::append(outputs, outs);
1351   }
1352 }
1353
1354 void cmGlobalNinjaGenerator::AppendTargetDependsClosure(
1355   cmGeneratorTarget const* target, cmNinjaDeps& outputs,
1356   const std::string& config, const std::string& fileConfig, bool genexOutput)
1357 {
1358   cmNinjaOuts outs;
1359   this->AppendTargetDependsClosure(target, outs, config, fileConfig,
1360                                    genexOutput, true);
1361   cm::append(outputs, outs);
1362 }
1363
1364 void cmGlobalNinjaGenerator::AppendTargetDependsClosure(
1365   cmGeneratorTarget const* target, cmNinjaOuts& outputs,
1366   const std::string& config, const std::string& fileConfig, bool genexOutput,
1367   bool omit_self)
1368 {
1369
1370   // try to locate the target in the cache
1371   ByConfig::TargetDependsClosureKey key{
1372     target,
1373     config,
1374     genexOutput,
1375   };
1376   auto find = this->Configs[fileConfig].TargetDependsClosures.lower_bound(key);
1377
1378   if (find == this->Configs[fileConfig].TargetDependsClosures.end() ||
1379       find->first != key) {
1380     // We now calculate the closure outputs by inspecting the dependent
1381     // targets recursively.
1382     // For that we have to distinguish between a local result set that is only
1383     // relevant for filling the cache entries properly isolated and a global
1384     // result set that is relevant for the result of the top level call to
1385     // AppendTargetDependsClosure.
1386     cmNinjaOuts this_outs; // this will be the new cache entry
1387
1388     for (auto const& dep_target : this->GetTargetDirectDepends(target)) {
1389       if (!dep_target->IsInBuildSystem()) {
1390         continue;
1391       }
1392
1393       if (!this->IsSingleConfigUtility(target) &&
1394           !this->IsSingleConfigUtility(dep_target) &&
1395           this->EnableCrossConfigBuild() && !dep_target.IsCross() &&
1396           !genexOutput) {
1397         continue;
1398       }
1399
1400       if (dep_target.IsCross()) {
1401         this->AppendTargetDependsClosure(dep_target, this_outs, fileConfig,
1402                                          fileConfig, genexOutput, false);
1403       } else {
1404         this->AppendTargetDependsClosure(dep_target, this_outs, config,
1405                                          fileConfig, genexOutput, false);
1406       }
1407     }
1408     find = this->Configs[fileConfig].TargetDependsClosures.emplace_hint(
1409       find, key, std::move(this_outs));
1410   }
1411
1412   // now fill the outputs of the final result from the newly generated cache
1413   // entry
1414   outputs.insert(find->second.begin(), find->second.end());
1415
1416   // finally generate the outputs of the target itself, if applicable
1417   cmNinjaDeps outs;
1418   if (!omit_self) {
1419     this->AppendTargetOutputs(target, outs, config, DependOnTargetArtifact);
1420   }
1421   outputs.insert(outs.begin(), outs.end());
1422 }
1423
1424 void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias,
1425                                             cmGeneratorTarget* target,
1426                                             const std::string& config)
1427 {
1428   std::string outputPath = this->NinjaOutputPath(alias);
1429   std::string buildAlias = this->BuildAlias(outputPath, config);
1430   cmNinjaDeps outputs;
1431   if (config != "all") {
1432     this->AppendTargetOutputs(target, outputs, config, DependOnTargetArtifact);
1433   }
1434   // Mark the target's outputs as ambiguous to ensure that no other target
1435   // uses the output as an alias.
1436   for (std::string const& output : outputs) {
1437     this->TargetAliases[output].GeneratorTarget = nullptr;
1438     this->DefaultTargetAliases[output].GeneratorTarget = nullptr;
1439     for (const std::string& config2 :
1440          this->Makefiles.front()->GetGeneratorConfigs(
1441            cmMakefile::IncludeEmptyConfig)) {
1442       this->Configs[config2].TargetAliases[output].GeneratorTarget = nullptr;
1443     }
1444   }
1445
1446   // Insert the alias into the map.  If the alias was already present in the
1447   // map and referred to another target, mark it as ambiguous.
1448   TargetAlias ta;
1449   ta.GeneratorTarget = target;
1450   ta.Config = config;
1451
1452   auto newAliasGlobal =
1453     this->TargetAliases.insert(std::make_pair(buildAlias, ta));
1454   if (newAliasGlobal.second &&
1455       newAliasGlobal.first->second.GeneratorTarget != target) {
1456     newAliasGlobal.first->second.GeneratorTarget = nullptr;
1457   }
1458
1459   auto newAliasConfig =
1460     this->Configs[config].TargetAliases.insert(std::make_pair(outputPath, ta));
1461   if (newAliasConfig.second &&
1462       newAliasConfig.first->second.GeneratorTarget != target) {
1463     newAliasConfig.first->second.GeneratorTarget = nullptr;
1464   }
1465   if (this->DefaultConfigs.count(config)) {
1466     auto newAliasDefaultGlobal =
1467       this->DefaultTargetAliases.insert(std::make_pair(outputPath, ta));
1468     if (newAliasDefaultGlobal.second &&
1469         newAliasDefaultGlobal.first->second.GeneratorTarget != target) {
1470       newAliasDefaultGlobal.first->second.GeneratorTarget = nullptr;
1471     }
1472   }
1473 }
1474
1475 void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os)
1476 {
1477   cmGlobalNinjaGenerator::WriteDivider(os);
1478   os << "# Target aliases.\n\n";
1479
1480   cmNinjaBuild build("phony");
1481   build.Outputs.emplace_back();
1482   for (auto const& ta : this->TargetAliases) {
1483     // Don't write ambiguous aliases.
1484     if (!ta.second.GeneratorTarget) {
1485       continue;
1486     }
1487
1488     // Don't write alias if there is a already a custom command with
1489     // matching output
1490     if (this->HasCustomCommandOutput(ta.first)) {
1491       continue;
1492     }
1493
1494     build.Outputs.front() = ta.first;
1495     build.ExplicitDeps.clear();
1496     if (ta.second.Config == "all") {
1497       for (auto const& config : this->CrossConfigs) {
1498         this->AppendTargetOutputs(ta.second.GeneratorTarget,
1499                                   build.ExplicitDeps, config,
1500                                   DependOnTargetArtifact);
1501       }
1502     } else {
1503       this->AppendTargetOutputs(ta.second.GeneratorTarget, build.ExplicitDeps,
1504                                 ta.second.Config, DependOnTargetArtifact);
1505     }
1506     this->WriteBuild(this->EnableCrossConfigBuild() &&
1507                          (ta.second.Config == "all" ||
1508                           this->CrossConfigs.count(ta.second.Config))
1509                        ? os
1510                        : *this->GetImplFileStream(ta.second.Config),
1511                      build);
1512   }
1513
1514   if (this->IsMultiConfig()) {
1515     for (auto const& config : this->Makefiles.front()->GetGeneratorConfigs(
1516            cmMakefile::IncludeEmptyConfig)) {
1517       for (auto const& ta : this->Configs[config].TargetAliases) {
1518         // Don't write ambiguous aliases.
1519         if (!ta.second.GeneratorTarget) {
1520           continue;
1521         }
1522
1523         // Don't write alias if there is a already a custom command with
1524         // matching output
1525         if (this->HasCustomCommandOutput(ta.first)) {
1526           continue;
1527         }
1528
1529         build.Outputs.front() = ta.first;
1530         build.ExplicitDeps.clear();
1531         this->AppendTargetOutputs(ta.second.GeneratorTarget,
1532                                   build.ExplicitDeps, config,
1533                                   DependOnTargetArtifact);
1534         this->WriteBuild(*this->GetConfigFileStream(config), build);
1535       }
1536     }
1537
1538     if (!this->DefaultConfigs.empty()) {
1539       for (auto const& ta : this->DefaultTargetAliases) {
1540         // Don't write ambiguous aliases.
1541         if (!ta.second.GeneratorTarget) {
1542           continue;
1543         }
1544
1545         // Don't write alias if there is a already a custom command with
1546         // matching output
1547         if (this->HasCustomCommandOutput(ta.first)) {
1548           continue;
1549         }
1550
1551         build.Outputs.front() = ta.first;
1552         build.ExplicitDeps.clear();
1553         for (auto const& config : this->DefaultConfigs) {
1554           this->AppendTargetOutputs(ta.second.GeneratorTarget,
1555                                     build.ExplicitDeps, config,
1556                                     DependOnTargetArtifact);
1557         }
1558         this->WriteBuild(*this->GetDefaultFileStream(), build);
1559       }
1560     }
1561   }
1562 }
1563
1564 void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os)
1565 {
1566   cmGlobalNinjaGenerator::WriteDivider(os);
1567   os << "# Folder targets.\n\n";
1568
1569   std::map<std::string, DirectoryTarget> dirTargets =
1570     this->ComputeDirectoryTargets();
1571
1572   for (auto const& it : dirTargets) {
1573     cmNinjaBuild build("phony");
1574     cmGlobalNinjaGenerator::WriteDivider(os);
1575     std::string const& currentBinaryDir = it.first;
1576     DirectoryTarget const& dt = it.second;
1577     std::vector<std::string> configs =
1578       dt.LG->GetMakefile()->GetGeneratorConfigs(
1579         cmMakefile::IncludeEmptyConfig);
1580
1581     // Setup target
1582     cmNinjaDeps configDeps;
1583     build.Comment = cmStrCat("Folder: ", currentBinaryDir);
1584     build.Outputs.emplace_back();
1585     std::string const buildDirAllTarget =
1586       this->ConvertToNinjaPath(cmStrCat(currentBinaryDir, "/all"));
1587     for (auto const& config : configs) {
1588       build.ExplicitDeps.clear();
1589       build.Outputs.front() = this->BuildAlias(buildDirAllTarget, config);
1590       configDeps.emplace_back(build.Outputs.front());
1591       for (DirectoryTarget::Target const& t : dt.Targets) {
1592         if (!this->IsExcludedFromAllInConfig(t, config)) {
1593           this->AppendTargetOutputs(t.GT, build.ExplicitDeps, config,
1594                                     DependOnTargetArtifact);
1595         }
1596       }
1597       for (DirectoryTarget::Dir const& d : dt.Children) {
1598         if (!d.ExcludeFromAll) {
1599           build.ExplicitDeps.emplace_back(this->BuildAlias(
1600             this->ConvertToNinjaPath(cmStrCat(d.Path, "/all")), config));
1601         }
1602       }
1603       // Write target
1604       this->WriteBuild(this->EnableCrossConfigBuild() &&
1605                            this->CrossConfigs.count(config)
1606                          ? os
1607                          : *this->GetImplFileStream(config),
1608                        build);
1609     }
1610
1611     // Add shortcut target
1612     if (this->IsMultiConfig()) {
1613       for (auto const& config : configs) {
1614         build.ExplicitDeps = { this->BuildAlias(buildDirAllTarget, config) };
1615         build.Outputs.front() = buildDirAllTarget;
1616         this->WriteBuild(*this->GetConfigFileStream(config), build);
1617       }
1618
1619       if (!this->DefaultFileConfig.empty()) {
1620         build.ExplicitDeps.clear();
1621         for (auto const& config : this->DefaultConfigs) {
1622           build.ExplicitDeps.push_back(
1623             this->BuildAlias(buildDirAllTarget, config));
1624         }
1625         build.Outputs.front() = buildDirAllTarget;
1626         this->WriteBuild(*this->GetDefaultFileStream(), build);
1627       }
1628     }
1629
1630     // Add target for all configs
1631     if (this->EnableCrossConfigBuild()) {
1632       build.ExplicitDeps.clear();
1633       for (auto const& config : this->CrossConfigs) {
1634         build.ExplicitDeps.push_back(
1635           this->BuildAlias(buildDirAllTarget, config));
1636       }
1637       build.Outputs.front() = this->BuildAlias(buildDirAllTarget, "all");
1638       this->WriteBuild(os, build);
1639     }
1640   }
1641 }
1642
1643 void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os)
1644 {
1645   if (!this->ComputingUnknownDependencies) {
1646     return;
1647   }
1648
1649   // We need to collect the set of known build outputs.
1650   // Start with those generated by WriteBuild calls.
1651   // No other method needs this so we can take ownership
1652   // of the set locally and throw it out when we are done.
1653   std::set<std::string> knownDependencies;
1654   knownDependencies.swap(this->CombinedBuildOutputs);
1655
1656   // now write out the unknown explicit dependencies.
1657
1658   // union the configured files, evaluations files and the
1659   // CombinedBuildOutputs,
1660   // and then difference with CombinedExplicitDependencies to find the explicit
1661   // dependencies that we have no rule for
1662
1663   cmGlobalNinjaGenerator::WriteDivider(os);
1664   /* clang-format off */
1665   os << "# Unknown Build Time Dependencies.\n"
1666      << "# Tell Ninja that they may appear as side effects of build rules\n"
1667      << "# otherwise ordered by order-only dependencies.\n\n";
1668   /* clang-format on */
1669
1670   // get the list of files that cmake itself has generated as a
1671   // product of configuration.
1672
1673   for (const auto& lg : this->LocalGenerators) {
1674     // get the vector of files created by this makefile and convert them
1675     // to ninja paths, which are all relative in respect to the build directory
1676     for (std::string const& file : lg->GetMakefile()->GetOutputFiles()) {
1677       knownDependencies.insert(this->ConvertToNinjaPath(file));
1678     }
1679     if (!this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
1680       // get list files which are implicit dependencies as well and will be
1681       // phony for rebuild manifest
1682       for (std::string const& j : lg->GetMakefile()->GetListFiles()) {
1683         knownDependencies.insert(this->ConvertToNinjaPath(j));
1684       }
1685     }
1686     for (const auto& li : lg->GetMakefile()->GetEvaluationFiles()) {
1687       // get all the files created by generator expressions and convert them
1688       // to ninja paths
1689       for (std::string const& evaluationFile : li->GetFiles()) {
1690         knownDependencies.insert(this->ConvertToNinjaPath(evaluationFile));
1691       }
1692     }
1693   }
1694   knownDependencies.insert(this->CMakeCacheFile);
1695
1696   for (auto const& ta : this->TargetAliases) {
1697     knownDependencies.insert(this->ConvertToNinjaPath(ta.first));
1698   }
1699
1700   // remove all source files we know will exist.
1701   for (auto const& i : this->AssumedSourceDependencies) {
1702     knownDependencies.insert(this->ConvertToNinjaPath(i.first));
1703   }
1704
1705   // now we difference with CombinedCustomCommandExplicitDependencies to find
1706   // the list of items we know nothing about.
1707   // We have encoded all the paths in CombinedCustomCommandExplicitDependencies
1708   // and knownDependencies so no matter if unix or windows paths they
1709   // should all match now.
1710
1711   std::vector<std::string> unknownExplicitDepends;
1712   this->CombinedCustomCommandExplicitDependencies.erase(this->TargetAll);
1713
1714   std::set_difference(this->CombinedCustomCommandExplicitDependencies.begin(),
1715                       this->CombinedCustomCommandExplicitDependencies.end(),
1716                       knownDependencies.begin(), knownDependencies.end(),
1717                       std::back_inserter(unknownExplicitDepends));
1718
1719   std::vector<std::string> warnExplicitDepends;
1720   if (!unknownExplicitDepends.empty()) {
1721     cmake* cmk = this->GetCMakeInstance();
1722     std::string const& buildRoot = cmk->GetHomeOutputDirectory();
1723     bool const inSource = (buildRoot == cmk->GetHomeDirectory());
1724     bool const warn = (!inSource && (this->PolicyCMP0058 == cmPolicies::WARN));
1725     cmNinjaBuild build("phony");
1726     build.Outputs.emplace_back("");
1727     for (std::string const& ued : unknownExplicitDepends) {
1728       // verify the file is in the build directory
1729       std::string const absDepPath =
1730         cmSystemTools::CollapseFullPath(ued, buildRoot);
1731       if (cmSystemTools::IsSubDirectory(absDepPath, buildRoot)) {
1732         // Generate phony build statement
1733         build.Outputs[0] = ued;
1734         this->WriteBuild(os, build);
1735         // Add to warning on demand
1736         if (warn && warnExplicitDepends.size() < 10) {
1737           warnExplicitDepends.push_back(ued);
1738         }
1739       }
1740     }
1741   }
1742
1743   if (!warnExplicitDepends.empty()) {
1744     std::ostringstream w;
1745     /* clang-format off */
1746     w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0058) << "\n"
1747       "This project specifies custom command DEPENDS on files "
1748       "in the build tree that are not specified as the OUTPUT or "
1749       "BYPRODUCTS of any add_custom_command or add_custom_target:\n"
1750       " " << cmJoin(warnExplicitDepends, "\n ") <<
1751       "\n"
1752       "For compatibility with versions of CMake that did not have "
1753       "the BYPRODUCTS option, CMake is generating phony rules for "
1754       "such files to convince 'ninja' to build."
1755       "\n"
1756       "Project authors should add the missing BYPRODUCTS or OUTPUT "
1757       "options to the custom commands that produce these files."
1758       ;
1759     /* clang-format on */
1760     this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
1761                                            w.str());
1762   }
1763 }
1764
1765 void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
1766 {
1767   // Write headers.
1768   cmGlobalNinjaGenerator::WriteDivider(os);
1769   os << "# Built-in targets\n\n";
1770
1771   this->WriteTargetRebuildManifest(os);
1772   this->WriteTargetClean(os);
1773   this->WriteTargetHelp(os);
1774
1775   for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs(
1776          cmMakefile::IncludeEmptyConfig)) {
1777     this->WriteTargetDefault(*this->GetConfigFileStream(config));
1778   }
1779
1780   if (!this->DefaultFileConfig.empty()) {
1781     this->WriteTargetDefault(*this->GetDefaultFileStream());
1782   }
1783 }
1784
1785 void cmGlobalNinjaGenerator::WriteTargetDefault(std::ostream& os)
1786 {
1787   if (!this->HasOutputPathPrefix()) {
1788     cmNinjaDeps all;
1789     all.push_back(this->TargetAll);
1790     cmGlobalNinjaGenerator::WriteDefault(os, all,
1791                                          "Make the all target the default.");
1792   }
1793 }
1794
1795 void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
1796 {
1797   if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
1798     return;
1799   }
1800   const auto& lg = this->LocalGenerators[0];
1801
1802   {
1803     cmNinjaRule rule("RERUN_CMAKE");
1804     rule.Command =
1805       cmStrCat(this->CMakeCmd(), " --regenerate-during-build -S",
1806                lg->ConvertToOutputFormat(lg->GetSourceDirectory(),
1807                                          cmOutputConverter::SHELL),
1808                " -B",
1809                lg->ConvertToOutputFormat(lg->GetBinaryDirectory(),
1810                                          cmOutputConverter::SHELL));
1811     rule.Description = "Re-running CMake...";
1812     rule.Comment = "Rule for re-running cmake.";
1813     rule.Generator = true;
1814     WriteRule(*this->RulesFileStream, rule);
1815   }
1816
1817   cmNinjaBuild reBuild("RERUN_CMAKE");
1818   reBuild.Comment = "Re-run CMake if any of its inputs changed.";
1819   this->AddRebuildManifestOutputs(reBuild.Outputs);
1820
1821   for (const auto& localGen : this->LocalGenerators) {
1822     for (std::string const& fi : localGen->GetMakefile()->GetListFiles()) {
1823       reBuild.ImplicitDeps.push_back(this->ConvertToNinjaPath(fi));
1824     }
1825   }
1826   reBuild.ImplicitDeps.push_back(this->CMakeCacheFile);
1827
1828   // Use 'console' pool to get non buffered output of the CMake re-run call
1829   // Available since Ninja 1.5
1830   if (this->SupportsDirectConsole()) {
1831     reBuild.Variables["pool"] = "console";
1832   }
1833
1834   cmake* cm = this->GetCMakeInstance();
1835   if (this->SupportsManifestRestat() && cm->DoWriteGlobVerifyTarget()) {
1836     {
1837       cmNinjaRule rule("VERIFY_GLOBS");
1838       rule.Command =
1839         cmStrCat(this->CMakeCmd(), " -P ",
1840                  lg->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
1841                                            cmOutputConverter::SHELL));
1842       rule.Description = "Re-checking globbed directories...";
1843       rule.Comment = "Rule for re-checking globbed directories.";
1844       rule.Generator = true;
1845       this->WriteRule(*this->RulesFileStream, rule);
1846     }
1847
1848     cmNinjaBuild phonyBuild("phony");
1849     phonyBuild.Comment = "Phony target to force glob verification run.";
1850     phonyBuild.Outputs.push_back(
1851       cmStrCat(cm->GetGlobVerifyScript(), "_force"));
1852     this->WriteBuild(os, phonyBuild);
1853
1854     reBuild.Variables["restat"] = "1";
1855     std::string const verifyScriptFile =
1856       this->NinjaOutputPath(cm->GetGlobVerifyScript());
1857     std::string const verifyStampFile =
1858       this->NinjaOutputPath(cm->GetGlobVerifyStamp());
1859     {
1860       cmNinjaBuild vgBuild("VERIFY_GLOBS");
1861       vgBuild.Comment =
1862         "Re-run CMake to check if globbed directories changed.";
1863       vgBuild.Outputs.push_back(verifyStampFile);
1864       vgBuild.ImplicitDeps = phonyBuild.Outputs;
1865       vgBuild.Variables = reBuild.Variables;
1866       this->WriteBuild(os, vgBuild);
1867     }
1868     reBuild.Variables.erase("restat");
1869     reBuild.ImplicitDeps.push_back(verifyScriptFile);
1870     reBuild.ExplicitDeps.push_back(verifyStampFile);
1871   } else if (!this->SupportsManifestRestat() &&
1872              cm->DoWriteGlobVerifyTarget()) {
1873     std::ostringstream msg;
1874     msg << "The detected version of Ninja:\n"
1875         << "  " << this->NinjaVersion << "\n"
1876         << "is less than the version of Ninja required by CMake for adding "
1877            "restat dependencies to the build.ninja manifest regeneration "
1878            "target:\n"
1879         << "  "
1880         << cmGlobalNinjaGenerator::RequiredNinjaVersionForManifestRestat()
1881         << "\n";
1882     msg << "Any pre-check scripts, such as those generated for file(GLOB "
1883            "CONFIGURE_DEPENDS), will not be run by Ninja.";
1884     this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
1885                                            msg.str());
1886   }
1887
1888   std::sort(reBuild.ImplicitDeps.begin(), reBuild.ImplicitDeps.end());
1889   reBuild.ImplicitDeps.erase(
1890     std::unique(reBuild.ImplicitDeps.begin(), reBuild.ImplicitDeps.end()),
1891     reBuild.ImplicitDeps.end());
1892
1893   this->WriteBuild(os, reBuild);
1894
1895   {
1896     cmNinjaBuild build("phony");
1897     build.Comment = "A missing CMake input file is not an error.";
1898     std::set_difference(std::make_move_iterator(reBuild.ImplicitDeps.begin()),
1899                         std::make_move_iterator(reBuild.ImplicitDeps.end()),
1900                         this->CustomCommandOutputs.begin(),
1901                         this->CustomCommandOutputs.end(),
1902                         std::back_inserter(build.Outputs));
1903     this->WriteBuild(os, build);
1904   }
1905 }
1906
1907 std::string cmGlobalNinjaGenerator::CMakeCmd() const
1908 {
1909   const auto& lgen = this->LocalGenerators.at(0);
1910   return lgen->ConvertToOutputFormat(cmSystemTools::GetCMakeCommand(),
1911                                      cmOutputConverter::SHELL);
1912 }
1913
1914 std::string cmGlobalNinjaGenerator::NinjaCmd() const
1915 {
1916   const auto& lgen = this->LocalGenerators[0];
1917   if (lgen != nullptr) {
1918     return lgen->ConvertToOutputFormat(this->NinjaCommand,
1919                                        cmOutputConverter::SHELL);
1920   }
1921   return "ninja";
1922 }
1923
1924 bool cmGlobalNinjaGenerator::SupportsDirectConsole() const
1925 {
1926   return this->NinjaSupportsConsolePool;
1927 }
1928
1929 bool cmGlobalNinjaGenerator::SupportsImplicitOuts() const
1930 {
1931   return this->NinjaSupportsImplicitOuts;
1932 }
1933
1934 bool cmGlobalNinjaGenerator::SupportsManifestRestat() const
1935 {
1936   return this->NinjaSupportsManifestRestat;
1937 }
1938
1939 bool cmGlobalNinjaGenerator::SupportsMultilineDepfile() const
1940 {
1941   return this->NinjaSupportsMultilineDepfile;
1942 }
1943
1944 bool cmGlobalNinjaGenerator::WriteTargetCleanAdditional(std::ostream& os)
1945 {
1946   const auto& lgr = this->LocalGenerators.at(0);
1947   std::string cleanScriptRel = "CMakeFiles/clean_additional.cmake";
1948   std::string cleanScriptAbs =
1949     cmStrCat(lgr->GetBinaryDirectory(), '/', cleanScriptRel);
1950   std::vector<std::string> configs =
1951     this->Makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
1952
1953   // Check if there are additional files to clean
1954   bool empty = true;
1955   for (auto const& config : configs) {
1956     auto const it = this->Configs.find(config);
1957     if (it != this->Configs.end() &&
1958         !it->second.AdditionalCleanFiles.empty()) {
1959       empty = false;
1960       break;
1961     }
1962   }
1963   if (empty) {
1964     // Remove cmake clean script file if it exists
1965     cmSystemTools::RemoveFile(cleanScriptAbs);
1966     return false;
1967   }
1968
1969   // Write cmake clean script file
1970   {
1971     cmGeneratedFileStream fout(cleanScriptAbs);
1972     if (!fout) {
1973       return false;
1974     }
1975     fout << "# Additional clean files\ncmake_minimum_required(VERSION 3.16)\n";
1976     for (auto const& config : configs) {
1977       auto const it = this->Configs.find(config);
1978       if (it != this->Configs.end() &&
1979           !it->second.AdditionalCleanFiles.empty()) {
1980         fout << "\nif(\"${CONFIG}\" STREQUAL \"\" OR \"${CONFIG}\" STREQUAL \""
1981              << config << "\")\n";
1982         fout << "  file(REMOVE_RECURSE\n";
1983         for (std::string const& acf : it->second.AdditionalCleanFiles) {
1984           fout << "  "
1985                << cmOutputConverter::EscapeForCMake(
1986                     this->ConvertToNinjaPath(acf))
1987                << '\n';
1988         }
1989         fout << "  )\n";
1990         fout << "endif()\n";
1991       }
1992     }
1993   }
1994   // Register clean script file
1995   lgr->GetMakefile()->AddCMakeOutputFile(cleanScriptAbs);
1996
1997   // Write rule
1998   {
1999     cmNinjaRule rule("CLEAN_ADDITIONAL");
2000     rule.Command = cmStrCat(
2001       this->CMakeCmd(), " -DCONFIG=$CONFIG -P ",
2002       lgr->ConvertToOutputFormat(this->NinjaOutputPath(cleanScriptRel),
2003                                  cmOutputConverter::SHELL));
2004     rule.Description = "Cleaning additional files...";
2005     rule.Comment = "Rule for cleaning additional files.";
2006     WriteRule(*this->RulesFileStream, rule);
2007   }
2008
2009   // Write build
2010   {
2011     cmNinjaBuild build("CLEAN_ADDITIONAL");
2012     build.Comment = "Clean additional files.";
2013     build.Outputs.emplace_back();
2014     for (auto const& config : configs) {
2015       build.Outputs.front() = this->BuildAlias(
2016         this->NinjaOutputPath(this->GetAdditionalCleanTargetName()), config);
2017       build.Variables["CONFIG"] = config;
2018       this->WriteBuild(os, build);
2019     }
2020     if (this->IsMultiConfig()) {
2021       build.Outputs.front() =
2022         this->NinjaOutputPath(this->GetAdditionalCleanTargetName());
2023       build.Variables["CONFIG"] = "";
2024       this->WriteBuild(os, build);
2025     }
2026   }
2027   // Return success
2028   return true;
2029 }
2030
2031 void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
2032 {
2033   // -- Additional clean target
2034   bool additionalFiles = this->WriteTargetCleanAdditional(os);
2035
2036   // -- Default clean target
2037   // Write rule
2038   {
2039     cmNinjaRule rule("CLEAN");
2040     rule.Command = cmStrCat(this->NinjaCmd(), " $FILE_ARG -t clean $TARGETS");
2041     rule.Description = "Cleaning all built files...";
2042     rule.Comment = "Rule for cleaning all built files.";
2043     WriteRule(*this->RulesFileStream, rule);
2044   }
2045
2046   auto const configs = this->Makefiles.front()->GetGeneratorConfigs(
2047     cmMakefile::IncludeEmptyConfig);
2048
2049   // Write build
2050   {
2051     cmNinjaBuild build("CLEAN");
2052     build.Comment = "Clean all the built files.";
2053     build.Outputs.emplace_back();
2054
2055     for (auto const& config : configs) {
2056       build.Outputs.front() = this->BuildAlias(
2057         this->NinjaOutputPath(this->GetCleanTargetName()), config);
2058       if (this->IsMultiConfig()) {
2059         build.Variables["TARGETS"] =
2060           cmStrCat(this->BuildAlias(GetByproductsForCleanTargetName(), config),
2061                    " ", GetByproductsForCleanTargetName());
2062       }
2063       build.ExplicitDeps.clear();
2064       if (additionalFiles) {
2065         build.ExplicitDeps.push_back(this->BuildAlias(
2066           this->NinjaOutputPath(this->GetAdditionalCleanTargetName()),
2067           config));
2068       }
2069       for (auto const& fileConfig : configs) {
2070         if (fileConfig != config && !this->EnableCrossConfigBuild()) {
2071           continue;
2072         }
2073         if (this->IsMultiConfig()) {
2074           build.Variables["FILE_ARG"] = cmStrCat(
2075             "-f ",
2076             cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig));
2077         }
2078         this->WriteBuild(*this->GetImplFileStream(fileConfig), build);
2079       }
2080     }
2081
2082     if (this->EnableCrossConfigBuild()) {
2083       build.Outputs.front() = this->BuildAlias(
2084         this->NinjaOutputPath(this->GetCleanTargetName()), "all");
2085       build.ExplicitDeps.clear();
2086
2087       if (additionalFiles) {
2088         for (auto const& config : this->CrossConfigs) {
2089           build.ExplicitDeps.push_back(this->BuildAlias(
2090             this->NinjaOutputPath(this->GetAdditionalCleanTargetName()),
2091             config));
2092         }
2093       }
2094
2095       std::vector<std::string> byproducts;
2096       for (auto const& config : this->CrossConfigs) {
2097         byproducts.push_back(
2098           this->BuildAlias(GetByproductsForCleanTargetName(), config));
2099       }
2100       byproducts.emplace_back(GetByproductsForCleanTargetName());
2101       build.Variables["TARGETS"] = cmJoin(byproducts, " ");
2102
2103       for (auto const& fileConfig : configs) {
2104         build.Variables["FILE_ARG"] = cmStrCat(
2105           "-f ",
2106           cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig));
2107         this->WriteBuild(*this->GetImplFileStream(fileConfig), build);
2108       }
2109     }
2110   }
2111
2112   if (this->IsMultiConfig()) {
2113     cmNinjaBuild build("phony");
2114     build.Outputs.emplace_back(
2115       this->NinjaOutputPath(this->GetCleanTargetName()));
2116     build.ExplicitDeps.emplace_back();
2117
2118     for (auto const& config : configs) {
2119       build.ExplicitDeps.front() = this->BuildAlias(
2120         this->NinjaOutputPath(this->GetCleanTargetName()), config);
2121       this->WriteBuild(*this->GetConfigFileStream(config), build);
2122     }
2123
2124     if (!this->DefaultConfigs.empty()) {
2125       build.ExplicitDeps.clear();
2126       for (auto const& config : this->DefaultConfigs) {
2127         build.ExplicitDeps.push_back(this->BuildAlias(
2128           this->NinjaOutputPath(this->GetCleanTargetName()), config));
2129       }
2130       this->WriteBuild(*this->GetDefaultFileStream(), build);
2131     }
2132   }
2133
2134   // Write byproducts
2135   if (this->IsMultiConfig()) {
2136     cmNinjaBuild build("phony");
2137     build.Comment = "Clean byproducts.";
2138     build.Outputs.emplace_back(
2139       this->ConvertToNinjaPath(GetByproductsForCleanTargetName()));
2140     build.ExplicitDeps = this->ByproductsForCleanTarget;
2141     this->WriteBuild(os, build);
2142
2143     for (auto const& config : configs) {
2144       build.Outputs.front() = this->BuildAlias(
2145         this->ConvertToNinjaPath(GetByproductsForCleanTargetName()), config);
2146       build.ExplicitDeps = this->Configs[config].ByproductsForCleanTarget;
2147       this->WriteBuild(os, build);
2148     }
2149   }
2150 }
2151
2152 void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os)
2153 {
2154   {
2155     cmNinjaRule rule("HELP");
2156     rule.Command = cmStrCat(this->NinjaCmd(), " -t targets");
2157     rule.Description = "All primary targets available:";
2158     rule.Comment = "Rule for printing all primary targets available.";
2159     WriteRule(*this->RulesFileStream, rule);
2160   }
2161   {
2162     cmNinjaBuild build("HELP");
2163     build.Comment = "Print all primary targets available.";
2164     build.Outputs.push_back(this->NinjaOutputPath("help"));
2165     this->WriteBuild(os, build);
2166   }
2167 }
2168
2169 void cmGlobalNinjaGenerator::InitOutputPathPrefix()
2170 {
2171   this->OutputPathPrefix =
2172     this->LocalGenerators[0]->GetMakefile()->GetSafeDefinition(
2173       "CMAKE_NINJA_OUTPUT_PATH_PREFIX");
2174   EnsureTrailingSlash(this->OutputPathPrefix);
2175 }
2176
2177 std::string cmGlobalNinjaGenerator::NinjaOutputPath(
2178   std::string const& path) const
2179 {
2180   if (!this->HasOutputPathPrefix() || cmSystemTools::FileIsFullPath(path)) {
2181     return path;
2182   }
2183   return cmStrCat(this->OutputPathPrefix, path);
2184 }
2185
2186 void cmGlobalNinjaGenerator::StripNinjaOutputPathPrefixAsSuffix(
2187   std::string& path)
2188 {
2189   if (path.empty()) {
2190     return;
2191   }
2192   EnsureTrailingSlash(path);
2193   cmStripSuffixIfExists(path, this->OutputPathPrefix);
2194 }
2195
2196 #if !defined(CMAKE_BOOTSTRAP)
2197
2198 /*
2199
2200 We use the following approach to support Fortran.  Each target already
2201 has a <target>.dir/ directory used to hold intermediate files for CMake.
2202 For each target, a FortranDependInfo.json file is generated by CMake with
2203 information about include directories, module directories, and the locations
2204 the per-target directories for target dependencies.
2205
2206 Compilation of source files within a target is split into the following steps:
2207
2208 1. Preprocess all sources, scan preprocessed output for module dependencies.
2209    This step is done with independent build statements for each source,
2210    and can therefore be done in parallel.
2211
2212     rule Fortran_PREPROCESS
2213       depfile = $DEP_FILE
2214       command = gfortran -cpp $DEFINES $INCLUDES $FLAGS -E $in -o $out &&
2215                 cmake -E cmake_ninja_depends \
2216                   --tdi=FortranDependInfo.json --pp=$out --dep=$DEP_FILE \
2217                   --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE \
2218                   --lang=Fortran
2219
2220     build src.f90-pp.f90 | src.f90.o.ddi: Fortran_PREPROCESS src.f90
2221       OBJ_FILE = src.f90.o
2222       DEP_FILE = src.f90.o.d
2223       DYNDEP_INTERMEDIATE_FILE = src.f90.o.ddi
2224
2225    The ``cmake -E cmake_ninja_depends`` tool reads the preprocessed output
2226    and generates the ninja depfile for preprocessor dependencies.  It also
2227    generates a "ddi" file (in a format private to CMake) that lists the
2228    object file that compilation will produce along with the module names
2229    it provides and/or requires.  The "ddi" file is an implicit output
2230    because it should not appear in "$out" but is generated by the rule.
2231
2232 2. Consolidate the per-source module dependencies saved in the "ddi"
2233    files from all sources to produce a ninja "dyndep" file, ``Fortran.dd``.
2234
2235     rule Fortran_DYNDEP
2236       command = cmake -E cmake_ninja_dyndep \
2237                   --tdi=FortranDependInfo.json --lang=Fortran --dd=$out $in
2238
2239     build Fortran.dd: Fortran_DYNDEP src1.f90.o.ddi src2.f90.o.ddi
2240
2241    The ``cmake -E cmake_ninja_dyndep`` tool reads the "ddi" files from all
2242    sources in the target and the ``FortranModules.json`` files from targets
2243    on which the target depends.  It computes dependency edges on compilations
2244    that require modules to those that provide the modules.  This information
2245    is placed in the ``Fortran.dd`` file for ninja to load later.  It also
2246    writes the expected location of modules provided by this target into
2247    ``FortranModules.json`` for use by dependent targets.
2248
2249 3. Compile all sources after loading dynamically discovered dependencies
2250    of the compilation build statements from their ``dyndep`` bindings.
2251
2252     rule Fortran_COMPILE
2253       command = gfortran $INCLUDES $FLAGS -c $in -o $out
2254
2255     build src1.f90.o: Fortran_COMPILE src1.f90-pp.f90 || Fortran.dd
2256       dyndep = Fortran.dd
2257
2258    The "dyndep" binding tells ninja to load dynamically discovered
2259    dependency information from ``Fortran.dd``.  This adds information
2260    such as:
2261
2262     build src1.f90.o | mod1.mod: dyndep
2263       restat = 1
2264
2265    This tells ninja that ``mod1.mod`` is an implicit output of compiling
2266    the object file ``src1.f90.o``.  The ``restat`` binding tells it that
2267    the timestamp of the output may not always change.  Additionally:
2268
2269     build src2.f90.o: dyndep | mod1.mod
2270
2271    This tells ninja that ``mod1.mod`` is a dependency of compiling the
2272    object file ``src2.f90.o``.  This ensures that ``src1.f90.o`` and
2273    ``mod1.mod`` will always be up to date before ``src2.f90.o`` is built
2274    (because the latter consumes the module).
2275 */
2276
2277 namespace {
2278
2279 struct cmSourceInfo
2280 {
2281   cmScanDepInfo ScanDep;
2282   std::vector<std::string> Includes;
2283 };
2284
2285 cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
2286   std::string const& arg_tdi, std::string const& arg_pp);
2287 }
2288
2289 int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
2290                               std::vector<std::string>::const_iterator argEnd)
2291 {
2292   std::string arg_tdi;
2293   std::string arg_pp;
2294   std::string arg_dep;
2295   std::string arg_obj;
2296   std::string arg_ddi;
2297   std::string arg_lang;
2298   for (std::string const& arg : cmMakeRange(argBeg, argEnd)) {
2299     if (cmHasLiteralPrefix(arg, "--tdi=")) {
2300       arg_tdi = arg.substr(6);
2301     } else if (cmHasLiteralPrefix(arg, "--pp=")) {
2302       arg_pp = arg.substr(5);
2303     } else if (cmHasLiteralPrefix(arg, "--dep=")) {
2304       arg_dep = arg.substr(6);
2305     } else if (cmHasLiteralPrefix(arg, "--obj=")) {
2306       arg_obj = arg.substr(6);
2307     } else if (cmHasLiteralPrefix(arg, "--ddi=")) {
2308       arg_ddi = arg.substr(6);
2309     } else if (cmHasLiteralPrefix(arg, "--lang=")) {
2310       arg_lang = arg.substr(7);
2311     } else {
2312       cmSystemTools::Error(
2313         cmStrCat("-E cmake_ninja_depends unknown argument: ", arg));
2314       return 1;
2315     }
2316   }
2317   if (arg_tdi.empty()) {
2318     cmSystemTools::Error("-E cmake_ninja_depends requires value for --tdi=");
2319     return 1;
2320   }
2321   if (arg_pp.empty()) {
2322     cmSystemTools::Error("-E cmake_ninja_depends requires value for --pp=");
2323     return 1;
2324   }
2325   if (arg_dep.empty()) {
2326     cmSystemTools::Error("-E cmake_ninja_depends requires value for --dep=");
2327     return 1;
2328   }
2329   if (arg_obj.empty()) {
2330     cmSystemTools::Error("-E cmake_ninja_depends requires value for --obj=");
2331     return 1;
2332   }
2333   if (arg_ddi.empty()) {
2334     cmSystemTools::Error("-E cmake_ninja_depends requires value for --ddi=");
2335     return 1;
2336   }
2337   if (arg_lang.empty()) {
2338     cmSystemTools::Error("-E cmake_ninja_depends requires value for --lang=");
2339     return 1;
2340   }
2341
2342   cm::optional<cmSourceInfo> info;
2343   if (arg_lang == "Fortran") {
2344     info = cmcmd_cmake_ninja_depends_fortran(arg_tdi, arg_pp);
2345   } else {
2346     cmSystemTools::Error(
2347       cmStrCat("-E cmake_ninja_depends does not understand the ", arg_lang,
2348                " language"));
2349     return 1;
2350   }
2351
2352   if (!info) {
2353     // The error message is already expected to have been output.
2354     return 1;
2355   }
2356
2357   info->ScanDep.PrimaryOutput = arg_obj;
2358
2359   {
2360     cmGeneratedFileStream depfile(arg_dep);
2361     depfile << cmSystemTools::ConvertToUnixOutputPath(arg_pp) << ":";
2362     for (std::string const& include : info->Includes) {
2363       depfile << " \\\n " << cmSystemTools::ConvertToUnixOutputPath(include);
2364     }
2365     depfile << "\n";
2366   }
2367
2368   if (!cmScanDepFormat_P1689_Write(arg_ddi, info->ScanDep)) {
2369     cmSystemTools::Error(
2370       cmStrCat("-E cmake_ninja_depends failed to write ", arg_ddi));
2371     return 1;
2372   }
2373   return 0;
2374 }
2375
2376 namespace {
2377
2378 cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
2379   std::string const& arg_tdi, std::string const& arg_pp)
2380 {
2381   cm::optional<cmSourceInfo> info;
2382   cmFortranCompiler fc;
2383   std::vector<std::string> includes;
2384   std::string dir_top_bld;
2385   std::string module_dir;
2386   {
2387     Json::Value tdio;
2388     Json::Value const& tdi = tdio;
2389     {
2390       cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary);
2391       Json::Reader reader;
2392       if (!reader.parse(tdif, tdio, false)) {
2393         cmSystemTools::Error(
2394           cmStrCat("-E cmake_ninja_depends failed to parse ", arg_tdi,
2395                    reader.getFormattedErrorMessages()));
2396         return info;
2397       }
2398     }
2399
2400     dir_top_bld = tdi["dir-top-bld"].asString();
2401     if (!dir_top_bld.empty() && !cmHasLiteralSuffix(dir_top_bld, "/")) {
2402       dir_top_bld += '/';
2403     }
2404
2405     Json::Value const& tdi_include_dirs = tdi["include-dirs"];
2406     if (tdi_include_dirs.isArray()) {
2407       for (auto const& tdi_include_dir : tdi_include_dirs) {
2408         includes.push_back(tdi_include_dir.asString());
2409       }
2410     }
2411
2412     Json::Value const& tdi_module_dir = tdi["module-dir"];
2413     module_dir = tdi_module_dir.asString();
2414     if (!module_dir.empty() && !cmHasLiteralSuffix(module_dir, "/")) {
2415       module_dir += '/';
2416     }
2417
2418     Json::Value const& tdi_compiler_id = tdi["compiler-id"];
2419     fc.Id = tdi_compiler_id.asString();
2420
2421     Json::Value const& tdi_submodule_sep = tdi["submodule-sep"];
2422     fc.SModSep = tdi_submodule_sep.asString();
2423
2424     Json::Value const& tdi_submodule_ext = tdi["submodule-ext"];
2425     fc.SModExt = tdi_submodule_ext.asString();
2426   }
2427
2428   cmFortranSourceInfo finfo;
2429   std::set<std::string> defines;
2430   cmFortranParser parser(fc, includes, defines, finfo);
2431   if (!cmFortranParser_FilePush(&parser, arg_pp.c_str())) {
2432     cmSystemTools::Error(
2433       cmStrCat("-E cmake_ninja_depends failed to open ", arg_pp));
2434     return info;
2435   }
2436   if (cmFortran_yyparse(parser.Scanner) != 0) {
2437     // Failed to parse the file.
2438     return info;
2439   }
2440
2441   info = cmSourceInfo();
2442   for (std::string const& provide : finfo.Provides) {
2443     cmSourceReqInfo src_info;
2444     src_info.LogicalName = provide;
2445     if (!module_dir.empty()) {
2446       std::string mod = cmStrCat(module_dir, provide);
2447       if (!dir_top_bld.empty() && cmHasPrefix(mod, dir_top_bld)) {
2448         mod = mod.substr(dir_top_bld.size());
2449       }
2450       src_info.CompiledModulePath = std::move(mod);
2451     }
2452     info->ScanDep.Provides.emplace_back(src_info);
2453   }
2454   for (std::string const& require : finfo.Requires) {
2455     // Require modules not provided in the same source.
2456     if (finfo.Provides.count(require)) {
2457       continue;
2458     }
2459     cmSourceReqInfo src_info;
2460     src_info.LogicalName = require;
2461     info->ScanDep.Requires.emplace_back(src_info);
2462   }
2463   for (std::string const& include : finfo.Includes) {
2464     info->Includes.push_back(include);
2465   }
2466   return info;
2467 }
2468 }
2469
2470 bool cmGlobalNinjaGenerator::WriteDyndepFile(
2471   std::string const& dir_top_src, std::string const& dir_top_bld,
2472   std::string const& dir_cur_src, std::string const& dir_cur_bld,
2473   std::string const& arg_dd, std::vector<std::string> const& arg_ddis,
2474   std::string const& module_dir,
2475   std::vector<std::string> const& linked_target_dirs,
2476   std::string const& arg_lang, std::string const& arg_modmapfmt)
2477 {
2478   // Setup path conversions.
2479   {
2480     cmStateSnapshot snapshot = this->GetCMakeInstance()->GetCurrentSnapshot();
2481     snapshot.GetDirectory().SetCurrentSource(dir_cur_src);
2482     snapshot.GetDirectory().SetCurrentBinary(dir_cur_bld);
2483     auto mfd = cm::make_unique<cmMakefile>(this, snapshot);
2484     auto lgd = this->CreateLocalGenerator(mfd.get());
2485     lgd->SetRelativePathTop(dir_top_src, dir_top_bld);
2486     this->Makefiles.push_back(std::move(mfd));
2487     this->LocalGenerators.push_back(std::move(lgd));
2488   }
2489
2490   std::vector<cmScanDepInfo> objects;
2491   for (std::string const& arg_ddi : arg_ddis) {
2492     cmScanDepInfo info;
2493     if (!cmScanDepFormat_P1689_Parse(arg_ddi, &info)) {
2494       cmSystemTools::Error(
2495         cmStrCat("-E cmake_ninja_dyndep failed to parse ddi file ", arg_ddi));
2496       return false;
2497     }
2498     objects.push_back(std::move(info));
2499   }
2500
2501   // Map from module name to module file path, if known.
2502   std::map<std::string, std::string> mod_files;
2503
2504   // Populate the module map with those provided by linked targets first.
2505   for (std::string const& linked_target_dir : linked_target_dirs) {
2506     std::string const ltmn =
2507       cmStrCat(linked_target_dir, "/", arg_lang, "Modules.json");
2508     Json::Value ltm;
2509     cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary);
2510     Json::Reader reader;
2511     if (ltmf && !reader.parse(ltmf, ltm, false)) {
2512       cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
2513                                     linked_target_dir,
2514                                     reader.getFormattedErrorMessages()));
2515       return false;
2516     }
2517     if (ltm.isObject()) {
2518       for (Json::Value::iterator i = ltm.begin(); i != ltm.end(); ++i) {
2519         mod_files[i.key().asString()] = i->asString();
2520       }
2521     }
2522   }
2523
2524   const char* module_ext = "";
2525   if (arg_modmapfmt == "gcc") {
2526     module_ext = ".gcm";
2527   }
2528
2529   // Extend the module map with those provided by this target.
2530   // We do this after loading the modules provided by linked targets
2531   // in case we have one of the same name that must be preferred.
2532   Json::Value tm = Json::objectValue;
2533   for (cmScanDepInfo const& object : objects) {
2534     for (auto const& p : object.Provides) {
2535       std::string mod;
2536       if (!p.CompiledModulePath.empty()) {
2537         // The scanner provided the path to the module file.
2538         mod = p.CompiledModulePath;
2539         if (!cmSystemTools::FileIsFullPath(mod)) {
2540           // Treat relative to work directory (top of build tree).
2541           mod = cmSystemTools::CollapseFullPath(mod, dir_top_bld);
2542         }
2543       } else {
2544         // Assume the module file path matches the logical module name.
2545         std::string safe_logical_name = p.LogicalName;
2546         cmSystemTools::ReplaceString(safe_logical_name, ":", "-");
2547         mod = cmStrCat(module_dir, safe_logical_name, module_ext);
2548       }
2549       mod_files[p.LogicalName] = mod;
2550       tm[p.LogicalName] = mod;
2551     }
2552   }
2553
2554   cmGeneratedFileStream ddf(arg_dd);
2555   ddf << "ninja_dyndep_version = 1.0\n";
2556
2557   {
2558     cmNinjaBuild build("dyndep");
2559     build.Outputs.emplace_back("");
2560     for (cmScanDepInfo const& object : objects) {
2561       build.Outputs[0] = this->ConvertToNinjaPath(object.PrimaryOutput);
2562       build.ImplicitOuts.clear();
2563       for (auto const& p : object.Provides) {
2564         build.ImplicitOuts.push_back(
2565           this->ConvertToNinjaPath(mod_files[p.LogicalName]));
2566       }
2567       build.ImplicitDeps.clear();
2568       for (auto const& r : object.Requires) {
2569         auto mit = mod_files.find(r.LogicalName);
2570         if (mit != mod_files.end()) {
2571           build.ImplicitDeps.push_back(this->ConvertToNinjaPath(mit->second));
2572         }
2573       }
2574       build.Variables.clear();
2575       if (!object.Provides.empty()) {
2576         build.Variables.emplace("restat", "1");
2577       }
2578
2579       if (arg_modmapfmt.empty()) {
2580         // nothing to do.
2581       } else {
2582         std::stringstream mm;
2583         if (arg_modmapfmt == "gcc") {
2584           // Documented in GCC's documentation. The format is a series of lines
2585           // with a module name and the associated filename separated by
2586           // spaces. The first line may use `$root` as the module name to
2587           // specify a "repository root". That is used to anchor any relative
2588           // paths present in the file (CMake should never generate any).
2589
2590           // Write the root directory to use for module paths.
2591           mm << "$root .\n";
2592
2593           for (auto const& l : object.Provides) {
2594             auto m = mod_files.find(l.LogicalName);
2595             if (m != mod_files.end()) {
2596               mm << l.LogicalName << " " << this->ConvertToNinjaPath(m->second)
2597                  << "\n";
2598             }
2599           }
2600           for (auto const& r : object.Requires) {
2601             auto m = mod_files.find(r.LogicalName);
2602             if (m != mod_files.end()) {
2603               mm << r.LogicalName << " " << this->ConvertToNinjaPath(m->second)
2604                  << "\n";
2605             }
2606           }
2607         } else {
2608           cmSystemTools::Error(
2609             cmStrCat("-E cmake_ninja_dyndep does not understand the ",
2610                      arg_modmapfmt, " module map format"));
2611           return false;
2612         }
2613
2614         // XXX(modmap): If changing this path construction, change
2615         // `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the
2616         // corresponding file path.
2617         cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap"));
2618         mmf << mm.str();
2619       }
2620
2621       this->WriteBuild(ddf, build);
2622     }
2623   }
2624
2625   // Store the map of modules provided by this target in a file for
2626   // use by dependents that reference this target in linked-target-dirs.
2627   std::string const target_mods_file = cmStrCat(
2628     cmSystemTools::GetFilenamePath(arg_dd), '/', arg_lang, "Modules.json");
2629   cmGeneratedFileStream tmf(target_mods_file);
2630   tmf << tm;
2631
2632   return true;
2633 }
2634
2635 int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
2636                              std::vector<std::string>::const_iterator argEnd)
2637 {
2638   std::vector<std::string> arg_full =
2639     cmSystemTools::HandleResponseFile(argBeg, argEnd);
2640
2641   std::string arg_dd;
2642   std::string arg_lang;
2643   std::string arg_tdi;
2644   std::string arg_modmapfmt;
2645   std::vector<std::string> arg_ddis;
2646   for (std::string const& arg : arg_full) {
2647     if (cmHasLiteralPrefix(arg, "--tdi=")) {
2648       arg_tdi = arg.substr(6);
2649     } else if (cmHasLiteralPrefix(arg, "--lang=")) {
2650       arg_lang = arg.substr(7);
2651     } else if (cmHasLiteralPrefix(arg, "--dd=")) {
2652       arg_dd = arg.substr(5);
2653     } else if (cmHasLiteralPrefix(arg, "--modmapfmt=")) {
2654       arg_modmapfmt = arg.substr(12);
2655     } else if (!cmHasLiteralPrefix(arg, "--") &&
2656                cmHasLiteralSuffix(arg, ".ddi")) {
2657       arg_ddis.push_back(arg);
2658     } else {
2659       cmSystemTools::Error(
2660         cmStrCat("-E cmake_ninja_dyndep unknown argument: ", arg));
2661       return 1;
2662     }
2663   }
2664   if (arg_tdi.empty()) {
2665     cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --tdi=");
2666     return 1;
2667   }
2668   if (arg_lang.empty()) {
2669     cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --lang=");
2670     return 1;
2671   }
2672   if (arg_dd.empty()) {
2673     cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --dd=");
2674     return 1;
2675   }
2676
2677   Json::Value tdio;
2678   Json::Value const& tdi = tdio;
2679   {
2680     cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary);
2681     Json::Reader reader;
2682     if (!reader.parse(tdif, tdio, false)) {
2683       cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
2684                                     arg_tdi,
2685                                     reader.getFormattedErrorMessages()));
2686       return 1;
2687     }
2688   }
2689
2690   std::string const dir_cur_bld = tdi["dir-cur-bld"].asString();
2691   std::string const dir_cur_src = tdi["dir-cur-src"].asString();
2692   std::string const dir_top_bld = tdi["dir-top-bld"].asString();
2693   std::string const dir_top_src = tdi["dir-top-src"].asString();
2694   std::string module_dir = tdi["module-dir"].asString();
2695   if (!module_dir.empty() && !cmHasLiteralSuffix(module_dir, "/")) {
2696     module_dir += '/';
2697   }
2698   std::vector<std::string> linked_target_dirs;
2699   Json::Value const& tdi_linked_target_dirs = tdi["linked-target-dirs"];
2700   if (tdi_linked_target_dirs.isArray()) {
2701     for (auto const& tdi_linked_target_dir : tdi_linked_target_dirs) {
2702       linked_target_dirs.push_back(tdi_linked_target_dir.asString());
2703     }
2704   }
2705
2706   cmake cm(cmake::RoleInternal, cmState::Unknown);
2707   cm.SetHomeDirectory(dir_top_src);
2708   cm.SetHomeOutputDirectory(dir_top_bld);
2709   auto ggd = cm.CreateGlobalGenerator("Ninja");
2710   if (!ggd ||
2711       !cm::static_reference_cast<cmGlobalNinjaGenerator>(ggd).WriteDyndepFile(
2712         dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis,
2713         module_dir, linked_target_dirs, arg_lang, arg_modmapfmt)) {
2714     return 1;
2715   }
2716   return 0;
2717 }
2718
2719 #endif
2720
2721 bool cmGlobalNinjaGenerator::EnableCrossConfigBuild() const
2722 {
2723   return !this->CrossConfigs.empty();
2724 }
2725
2726 void cmGlobalNinjaGenerator::AppendDirectoryForConfig(
2727   const std::string& prefix, const std::string& config,
2728   const std::string& suffix, std::string& dir)
2729 {
2730   if (!config.empty() && this->IsMultiConfig()) {
2731     dir += cmStrCat(prefix, config, suffix);
2732   }
2733 }
2734
2735 std::set<std::string> cmGlobalNinjaGenerator::GetCrossConfigs(
2736   const std::string& fileConfig) const
2737 {
2738   auto result = this->CrossConfigs;
2739   result.insert(fileConfig);
2740   return result;
2741 }
2742
2743 bool cmGlobalNinjaGenerator::IsSingleConfigUtility(
2744   cmGeneratorTarget const* target) const
2745 {
2746   return target->GetType() == cmStateEnums::UTILITY &&
2747     !this->PerConfigUtilityTargets.count(target->GetName());
2748 }
2749
2750 const char* cmGlobalNinjaMultiGenerator::NINJA_COMMON_FILE =
2751   "CMakeFiles/common.ninja";
2752 const char* cmGlobalNinjaMultiGenerator::NINJA_FILE_EXTENSION = ".ninja";
2753
2754 cmGlobalNinjaMultiGenerator::cmGlobalNinjaMultiGenerator(cmake* cm)
2755   : cmGlobalNinjaGenerator(cm)
2756 {
2757   cm->GetState()->SetIsGeneratorMultiConfig(true);
2758   cm->GetState()->SetNinjaMulti(true);
2759 }
2760
2761 void cmGlobalNinjaMultiGenerator::GetDocumentation(cmDocumentationEntry& entry)
2762 {
2763   entry.Name = cmGlobalNinjaMultiGenerator::GetActualName();
2764   entry.Brief = "Generates build-<Config>.ninja files.";
2765 }
2766
2767 std::string cmGlobalNinjaMultiGenerator::ExpandCFGIntDir(
2768   const std::string& str, const std::string& config) const
2769 {
2770   std::string result = str;
2771   cmSystemTools::ReplaceString(result, this->GetCMakeCFGIntDir(), config);
2772   return result;
2773 }
2774
2775 bool cmGlobalNinjaMultiGenerator::OpenBuildFileStreams()
2776 {
2777   if (!this->OpenFileStream(this->CommonFileStream,
2778                             cmGlobalNinjaMultiGenerator::NINJA_COMMON_FILE)) {
2779     return false;
2780   }
2781
2782   if (!this->OpenFileStream(this->DefaultFileStream, NINJA_BUILD_FILE)) {
2783     return false;
2784   }
2785   *this->DefaultFileStream << "# Build using rules for '"
2786                            << this->DefaultFileConfig << "'.\n\n"
2787                            << "include "
2788                            << GetNinjaImplFilename(this->DefaultFileConfig)
2789                            << "\n\n";
2790
2791   // Write a comment about this file.
2792   *this->CommonFileStream
2793     << "# This file contains build statements common to all "
2794        "configurations.\n\n";
2795
2796   auto const& configs =
2797     this->Makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
2798   return std::all_of(
2799     configs.begin(), configs.end(), [this](std::string const& config) -> bool {
2800       // Open impl file.
2801       if (!this->OpenFileStream(this->ImplFileStreams[config],
2802                                 GetNinjaImplFilename(config))) {
2803         return false;
2804       }
2805
2806       // Write a comment about this file.
2807       *this->ImplFileStreams[config]
2808         << "# This file contains build statements specific to the \"" << config
2809         << "\"\n# configuration.\n\n";
2810
2811       // Open config file.
2812       if (!this->OpenFileStream(this->ConfigFileStreams[config],
2813                                 GetNinjaConfigFilename(config))) {
2814         return false;
2815       }
2816
2817       // Write a comment about this file.
2818       *this->ConfigFileStreams[config]
2819         << "# This file contains aliases specific to the \"" << config
2820         << "\"\n# configuration.\n\n"
2821         << "include " << GetNinjaImplFilename(config) << "\n\n";
2822
2823       return true;
2824     });
2825 }
2826
2827 void cmGlobalNinjaMultiGenerator::CloseBuildFileStreams()
2828 {
2829   if (this->CommonFileStream) {
2830     this->CommonFileStream.reset();
2831   } else {
2832     cmSystemTools::Error("Common file stream was not open.");
2833   }
2834
2835   if (this->DefaultFileStream) {
2836     this->DefaultFileStream.reset();
2837   } // No error if it wasn't open
2838
2839   for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs(
2840          cmMakefile::IncludeEmptyConfig)) {
2841     if (this->ImplFileStreams[config]) {
2842       this->ImplFileStreams[config].reset();
2843     } else {
2844       cmSystemTools::Error(
2845         cmStrCat("Impl file stream for \"", config, "\" was not open."));
2846     }
2847     if (this->ConfigFileStreams[config]) {
2848       this->ConfigFileStreams[config].reset();
2849     } else {
2850       cmSystemTools::Error(
2851         cmStrCat("Config file stream for \"", config, "\" was not open."));
2852     }
2853   }
2854 }
2855
2856 void cmGlobalNinjaMultiGenerator::AppendNinjaFileArgument(
2857   GeneratedMakeCommand& command, const std::string& config) const
2858 {
2859   if (!config.empty()) {
2860     command.Add("-f");
2861     command.Add(GetNinjaConfigFilename(config));
2862   }
2863 }
2864
2865 std::string cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(
2866   const std::string& config)
2867 {
2868   return cmStrCat("CMakeFiles/impl-", config,
2869                   cmGlobalNinjaMultiGenerator::NINJA_FILE_EXTENSION);
2870 }
2871
2872 std::string cmGlobalNinjaMultiGenerator::GetNinjaConfigFilename(
2873   const std::string& config)
2874 {
2875   return cmStrCat("build-", config,
2876                   cmGlobalNinjaMultiGenerator::NINJA_FILE_EXTENSION);
2877 }
2878
2879 void cmGlobalNinjaMultiGenerator::AddRebuildManifestOutputs(
2880   cmNinjaDeps& outputs) const
2881 {
2882   for (auto const& config : this->Makefiles.front()->GetGeneratorConfigs(
2883          cmMakefile::IncludeEmptyConfig)) {
2884     outputs.push_back(this->NinjaOutputPath(GetNinjaImplFilename(config)));
2885     outputs.push_back(this->NinjaOutputPath(GetNinjaConfigFilename(config)));
2886   }
2887   if (!this->DefaultFileConfig.empty()) {
2888     outputs.push_back(this->NinjaOutputPath(NINJA_BUILD_FILE));
2889   }
2890 }
2891
2892 void cmGlobalNinjaMultiGenerator::GetQtAutoGenConfigs(
2893   std::vector<std::string>& configs) const
2894 {
2895   auto allConfigs =
2896     this->Makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
2897   configs.insert(configs.end(), cm::cbegin(allConfigs), cm::cend(allConfigs));
2898 }
2899
2900 bool cmGlobalNinjaMultiGenerator::InspectConfigTypeVariables()
2901 {
2902   std::vector<std::string> configsVec;
2903   cmExpandList(
2904     this->Makefiles.front()->GetSafeDefinition("CMAKE_CONFIGURATION_TYPES"),
2905     configsVec);
2906   if (configsVec.empty()) {
2907     configsVec.emplace_back();
2908   }
2909   std::set<std::string> configs(configsVec.cbegin(), configsVec.cend());
2910
2911   this->DefaultFileConfig =
2912     this->Makefiles.front()->GetSafeDefinition("CMAKE_DEFAULT_BUILD_TYPE");
2913   if (this->DefaultFileConfig.empty()) {
2914     this->DefaultFileConfig = configsVec.front();
2915   }
2916   if (!configs.count(this->DefaultFileConfig)) {
2917     std::ostringstream msg;
2918     msg << "The configuration specified by "
2919         << "CMAKE_DEFAULT_BUILD_TYPE (" << this->DefaultFileConfig
2920         << ") is not present in CMAKE_CONFIGURATION_TYPES";
2921     this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
2922                                            msg.str());
2923     return false;
2924   }
2925
2926   std::vector<std::string> crossConfigsVec;
2927   cmExpandList(
2928     this->Makefiles.front()->GetSafeDefinition("CMAKE_CROSS_CONFIGS"),
2929     crossConfigsVec);
2930   auto crossConfigs = ListSubsetWithAll(configs, configs, crossConfigsVec);
2931   if (!crossConfigs) {
2932     std::ostringstream msg;
2933     msg << "CMAKE_CROSS_CONFIGS is not a subset of "
2934         << "CMAKE_CONFIGURATION_TYPES";
2935     this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
2936                                            msg.str());
2937     return false;
2938   }
2939   this->CrossConfigs = *crossConfigs;
2940
2941   auto defaultConfigsString =
2942     this->Makefiles.front()->GetSafeDefinition("CMAKE_DEFAULT_CONFIGS");
2943   if (defaultConfigsString.empty()) {
2944     defaultConfigsString = this->DefaultFileConfig;
2945   }
2946   if (!defaultConfigsString.empty() &&
2947       defaultConfigsString != this->DefaultFileConfig &&
2948       (this->DefaultFileConfig.empty() || this->CrossConfigs.empty())) {
2949     std::ostringstream msg;
2950     msg << "CMAKE_DEFAULT_CONFIGS cannot be used without "
2951         << "CMAKE_DEFAULT_BUILD_TYPE or CMAKE_CROSS_CONFIGS";
2952     this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
2953                                            msg.str());
2954     return false;
2955   }
2956
2957   std::vector<std::string> defaultConfigsVec;
2958   cmExpandList(defaultConfigsString, defaultConfigsVec);
2959   if (!this->DefaultFileConfig.empty()) {
2960     auto defaultConfigs =
2961       ListSubsetWithAll(this->GetCrossConfigs(this->DefaultFileConfig),
2962                         this->CrossConfigs, defaultConfigsVec);
2963     if (!defaultConfigs) {
2964       std::ostringstream msg;
2965       msg << "CMAKE_DEFAULT_CONFIGS is not a subset of CMAKE_CROSS_CONFIGS";
2966       this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
2967                                              msg.str());
2968       return false;
2969     }
2970     this->DefaultConfigs = *defaultConfigs;
2971   }
2972
2973   return true;
2974 }
2975
2976 std::string cmGlobalNinjaMultiGenerator::GetDefaultBuildConfig() const
2977 {
2978   return "";
2979 }
2980
2981 std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTarget(
2982   cmGeneratorTarget const* target, const std::string& config) const
2983 {
2984   return cmStrCat("cmake_object_order_depends_target_", target->GetName(), '_',
2985                   cmSystemTools::UpperCase(config));
2986 }