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