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