1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #pragma once
4 
5 #include "cmConfigure.h" // IWYU pragma: keep
6 
7 #include <iosfwd>
8 #include <map>
9 #include <memory>
10 #include <set>
11 #include <string>
12 #include <unordered_map>
13 #include <unordered_set>
14 #include <utility>
15 #include <vector>
16 
17 #include <cm/optional>
18 
19 #include "cm_codecvt.hxx"
20 
21 #include "cmGeneratedFileStream.h"
22 #include "cmGlobalCommonGenerator.h"
23 #include "cmGlobalGeneratorFactory.h"
24 #include "cmNinjaTypes.h"
25 #include "cmPolicies.h"
26 #include "cmStringAlgorithms.h"
27 #include "cmTransformDepfile.h"
28 
29 class cmCustomCommand;
30 class cmGeneratorTarget;
31 class cmLinkLineComputer;
32 class cmLocalGenerator;
33 class cmMakefile;
34 class cmOutputConverter;
35 class cmStateDirectory;
36 class cmake;
37 struct cmDocumentationEntry;
38 
39 /**
40  * \class cmGlobalNinjaGenerator
41  * \brief Write a build.ninja file.
42  *
43  * The main differences between this generator and the UnixMakefile
44  * generator family are:
45  * - We don't care about VERBOSE variable or RULE_MESSAGES property since
46  *   it is handle by Ninja's -v option.
47  * - We don't care about computing any progress status since Ninja manages
48  *   it itself.
49  * - We generate one build.ninja and one rules.ninja per project.
50  * - We try to minimize the number of generated rules: one per target and
51  *   language.
52  * - We use Ninja special variable $in and $out to produce nice output.
53  * - We extensively use Ninja variable overloading system to minimize the
54  *   number of generated rules.
55  */
56 class cmGlobalNinjaGenerator : public cmGlobalCommonGenerator
57 {
58 public:
59   /// The default name of Ninja's build file. Typically: build.ninja.
60   static const char* NINJA_BUILD_FILE;
61 
62   /// The default name of Ninja's rules file. Typically: rules.ninja.
63   /// It is included in the main build.ninja file.
64   static const char* NINJA_RULES_FILE;
65 
66   /// The indentation string used when generating Ninja's build file.
67   static const char* INDENT;
68 
69   /// The shell command used for a no-op.
70   static std::string const SHELL_NOOP;
71 
72   /// Write @a count times INDENT level to output stream @a os.
73   static void Indent(std::ostream& os, int count);
74 
75   /// Write a divider in the given output stream @a os.
76   static void WriteDivider(std::ostream& os);
77 
78   static std::string EncodeRuleName(std::string const& name);
79   std::string EncodeLiteral(const std::string& lit);
80   std::string EncodePath(const std::string& path);
81 
82   std::unique_ptr<cmLinkLineComputer> CreateLinkLineComputer(
83     cmOutputConverter* outputConverter,
84     cmStateDirectory const& stateDir) const override;
85 
86   /**
87    * Write the given @a comment to the output stream @a os. It
88    * handles new line character properly.
89    */
90   static void WriteComment(std::ostream& os, const std::string& comment);
91 
92   /**
93    * Utilized by the generator factory to determine if this generator
94    * supports toolsets.
95    */
SupportsToolset()96   static bool SupportsToolset() { return false; }
97 
98   /**
99    * Utilized by the generator factory to determine if this generator
100    * supports platforms.
101    */
SupportsPlatform()102   static bool SupportsPlatform() { return false; }
103 
IsIPOSupported()104   bool IsIPOSupported() const override { return true; }
105 
106   /**
107    * Write a build statement @a build to @a os.
108    * @warning no escaping of any kind is done here.
109    */
110   void WriteBuild(std::ostream& os, cmNinjaBuild const& build,
111                   int cmdLineLimit = 0, bool* usedResponseFile = nullptr);
112 
113   class CCOutputs
114   {
115     cmGlobalNinjaGenerator* GG;
116 
117   public:
CCOutputs(cmGlobalNinjaGenerator * gg)118     CCOutputs(cmGlobalNinjaGenerator* gg)
119       : GG(gg)
120     {
121     }
122     void Add(std::vector<std::string> const& outputs);
123     cmNinjaDeps ExplicitOuts;
124     cmNinjaDeps WorkDirOuts;
125   };
126 
127   void WriteCustomCommandBuild(std::string const& command,
128                                std::string const& description,
129                                std::string const& comment,
130                                std::string const& depfile,
131                                std::string const& pool, bool uses_terminal,
132                                bool restat, std::string const& config,
133                                CCOutputs outputs,
134                                cmNinjaDeps explicitDeps = cmNinjaDeps(),
135                                cmNinjaDeps orderOnlyDeps = cmNinjaDeps());
136 
137   void WriteMacOSXContentBuild(std::string input, std::string output,
138                                const std::string& config);
139 
140   /**
141    * Write a rule statement to @a os.
142    * @warning no escaping of any kind is done here.
143    */
144   static void WriteRule(std::ostream& os, cmNinjaRule const& rule);
145 
146   /**
147    * Write a variable named @a name to @a os with value @a value and an
148    * optional @a comment. An @a indent level can be specified.
149    * @warning no escaping of any kind is done here.
150    */
151   static void WriteVariable(std::ostream& os, const std::string& name,
152                             const std::string& value,
153                             const std::string& comment = "", int indent = 0);
154 
155   /**
156    * Write an include statement including @a filename with an optional
157    * @a comment to the @a os stream.
158    */
159   static void WriteInclude(std::ostream& os, const std::string& filename,
160                            const std::string& comment = "");
161 
162   /**
163    * Write a default target statement specifying @a targets as
164    * the default targets.
165    */
166   static void WriteDefault(std::ostream& os, const cmNinjaDeps& targets,
167                            const std::string& comment = "");
168 
IsGCCOnWindows()169   bool IsGCCOnWindows() const { return this->UsingGCCOnWindows; }
170 
171   cmGlobalNinjaGenerator(cmake* cm);
172 
NewFactory()173   static std::unique_ptr<cmGlobalGeneratorFactory> NewFactory()
174   {
175     return std::unique_ptr<cmGlobalGeneratorFactory>(
176       new cmGlobalGeneratorSimpleFactory<cmGlobalNinjaGenerator>());
177   }
178 
179   std::unique_ptr<cmLocalGenerator> CreateLocalGenerator(
180     cmMakefile* mf) override;
181 
GetName()182   std::string GetName() const override
183   {
184     return cmGlobalNinjaGenerator::GetActualName();
185   }
186 
GetActualName()187   static std::string GetActualName() { return "Ninja"; }
188 
IsNinja()189   bool IsNinja() const override { return true; }
190 
191   /** Get encoding used by generator for ninja files */
192   codecvt::Encoding GetMakefileEncoding() const override;
193 
194   static void GetDocumentation(cmDocumentationEntry& entry);
195 
196   void EnableLanguage(std::vector<std::string> const& languages,
197                       cmMakefile* mf, bool optional) override;
198 
199   std::vector<GeneratedMakeCommand> GenerateBuildCommand(
200     const std::string& makeProgram, const std::string& projectName,
201     const std::string& projectDir, std::vector<std::string> const& targetNames,
202     const std::string& config, bool fast, int jobs, bool verbose,
203     std::vector<std::string> const& makeOptions =
204       std::vector<std::string>()) override;
205 
206   // Setup target names
GetAllTargetName()207   const char* GetAllTargetName() const override { return "all"; }
GetInstallTargetName()208   const char* GetInstallTargetName() const override { return "install"; }
GetInstallLocalTargetName()209   const char* GetInstallLocalTargetName() const override
210   {
211     return "install/local";
212   }
GetInstallStripTargetName()213   const char* GetInstallStripTargetName() const override
214   {
215     return "install/strip";
216   }
GetTestTargetName()217   const char* GetTestTargetName() const override { return "test"; }
GetPackageTargetName()218   const char* GetPackageTargetName() const override { return "package"; }
GetPackageSourceTargetName()219   const char* GetPackageSourceTargetName() const override
220   {
221     return "package_source";
222   }
GetRebuildCacheTargetName()223   const char* GetRebuildCacheTargetName() const override
224   {
225     return "rebuild_cache";
226   }
GetCleanTargetName()227   const char* GetCleanTargetName() const override { return "clean"; }
228 
SupportsCustomCommandDepfile()229   bool SupportsCustomCommandDepfile() const override { return true; }
DepfileFormat()230   cm::optional<cmDepfileFormat> DepfileFormat() const override
231   {
232     return cmDepfileFormat::GccDepfile;
233   }
234 
GetImplFileStream(const std::string &)235   virtual cmGeneratedFileStream* GetImplFileStream(
236     const std::string& /*config*/) const
237   {
238     return this->BuildFileStream.get();
239   }
240 
GetConfigFileStream(const std::string &)241   virtual cmGeneratedFileStream* GetConfigFileStream(
242     const std::string& /*config*/) const
243   {
244     return this->BuildFileStream.get();
245   }
246 
GetDefaultFileStream()247   virtual cmGeneratedFileStream* GetDefaultFileStream() const
248   {
249     return this->BuildFileStream.get();
250   }
251 
GetCommonFileStream()252   virtual cmGeneratedFileStream* GetCommonFileStream() const
253   {
254     return this->BuildFileStream.get();
255   }
256 
GetRulesFileStream()257   cmGeneratedFileStream* GetRulesFileStream() const
258   {
259     return this->RulesFileStream.get();
260   }
261 
262   std::string const& ConvertToNinjaPath(const std::string& path) const;
263   std::string ConvertToNinjaAbsPath(std::string path) const;
264 
265   struct MapToNinjaPathImpl
266   {
267     cmGlobalNinjaGenerator* GG;
MapToNinjaPathImplMapToNinjaPathImpl268     MapToNinjaPathImpl(cmGlobalNinjaGenerator* gg)
269       : GG(gg)
270     {
271     }
operatorMapToNinjaPathImpl272     std::string operator()(std::string const& path) const
273     {
274       return this->GG->ConvertToNinjaPath(path);
275     }
276   };
MapToNinjaPath()277   MapToNinjaPathImpl MapToNinjaPath() { return { this }; }
278 
279   // -- Additional clean files
280   void AddAdditionalCleanFile(std::string fileName, const std::string& config);
GetAdditionalCleanTargetName()281   const char* GetAdditionalCleanTargetName() const
282   {
283     return "CMakeFiles/clean.additional";
284   }
285 
GetByproductsForCleanTargetName()286   static const char* GetByproductsForCleanTargetName()
287   {
288     return "CMakeFiles/cmake_byproducts_for_clean_target";
289   }
290 
291   void AddCXXCompileCommand(const std::string& commandLine,
292                             const std::string& sourceFile);
293 
294   /**
295    * Add a rule to the generated build system.
296    * Call WriteRule() behind the scene but perform some check before like:
297    * - Do not add twice the same rule.
298    */
299   void AddRule(cmNinjaRule const& rule);
300 
301   bool HasRule(const std::string& name);
302 
303   void AddCustomCommandRule();
304   void AddMacOSXContentRule();
305 
HasCustomCommandOutput(const std::string & output)306   bool HasCustomCommandOutput(const std::string& output)
307   {
308     return this->CustomCommandOutputs.find(output) !=
309       this->CustomCommandOutputs.end();
310   }
311 
312   /// Called when we have seen the given custom command.  Returns true
313   /// if we has seen it before.
SeenCustomCommand(cmCustomCommand const * cc,const std::string & config)314   bool SeenCustomCommand(cmCustomCommand const* cc, const std::string& config)
315   {
316     return !this->Configs[config].CustomCommands.insert(cc).second;
317   }
318 
319   /// Called when we have seen the given custom command output.
SeenCustomCommandOutput(const std::string & output)320   void SeenCustomCommandOutput(const std::string& output)
321   {
322     this->CustomCommandOutputs.insert(output);
323     // We don't need the assumed dependencies anymore, because we have
324     // an output.
325     this->AssumedSourceDependencies.erase(output);
326   }
327 
AddAssumedSourceDependencies(const std::string & source,const cmNinjaDeps & deps)328   void AddAssumedSourceDependencies(const std::string& source,
329                                     const cmNinjaDeps& deps)
330   {
331     std::set<std::string>& ASD = this->AssumedSourceDependencies[source];
332     // Because we may see the same source file multiple times (same source
333     // specified in multiple targets), compute the union of any assumed
334     // dependencies.
335     ASD.insert(deps.begin(), deps.end());
336   }
337 
338   virtual std::string OrderDependsTargetForTarget(
339     cmGeneratorTarget const* target, const std::string& config) const;
340 
341   void AppendTargetOutputs(cmGeneratorTarget const* target,
342                            cmNinjaDeps& outputs, const std::string& config,
343                            cmNinjaTargetDepends depends) const;
344   void AppendTargetDepends(cmGeneratorTarget const* target,
345                            cmNinjaDeps& outputs, const std::string& config,
346                            const std::string& fileConfig,
347                            cmNinjaTargetDepends depends);
348   void AppendTargetDependsClosure(cmGeneratorTarget const* target,
349                                   cmNinjaDeps& outputs,
350                                   const std::string& config,
351                                   const std::string& fileConfig,
352                                   bool genexOutput);
353   void AppendTargetDependsClosure(cmGeneratorTarget const* target,
354                                   cmNinjaOuts& outputs,
355                                   const std::string& config,
356                                   const std::string& fileConfig,
357                                   bool genexOutput, bool omit_self);
358 
359   void AppendDirectoryForConfig(const std::string& prefix,
360                                 const std::string& config,
361                                 const std::string& suffix,
362                                 std::string& dir) override;
363 
AppendNinjaFileArgument(GeneratedMakeCommand &,const std::string &)364   virtual void AppendNinjaFileArgument(GeneratedMakeCommand& /*command*/,
365                                        const std::string& /*config*/) const
366   {
367   }
368 
AddRebuildManifestOutputs(cmNinjaDeps & outputs)369   virtual void AddRebuildManifestOutputs(cmNinjaDeps& outputs) const
370   {
371     outputs.push_back(this->NinjaOutputPath(NINJA_BUILD_FILE));
372   }
373 
GetRuleCmdLength(const std::string & name)374   int GetRuleCmdLength(const std::string& name)
375   {
376     return this->RuleCmdLength[name];
377   }
378 
379   void AddTargetAlias(const std::string& alias, cmGeneratorTarget* target,
380                       const std::string& config);
381 
382   void ComputeTargetObjectDirectory(cmGeneratorTarget* gt) const override;
383 
384   // Ninja generator uses 'deps' and 'msvc_deps_prefix' introduced in 1.3
RequiredNinjaVersion()385   static std::string RequiredNinjaVersion() { return "1.3"; }
RequiredNinjaVersionForConsolePool()386   static std::string RequiredNinjaVersionForConsolePool() { return "1.5"; }
RequiredNinjaVersionForImplicitOuts()387   static std::string RequiredNinjaVersionForImplicitOuts() { return "1.7"; }
RequiredNinjaVersionForManifestRestat()388   static std::string RequiredNinjaVersionForManifestRestat() { return "1.8"; }
RequiredNinjaVersionForMultilineDepfile()389   static std::string RequiredNinjaVersionForMultilineDepfile()
390   {
391     return "1.9";
392   }
RequiredNinjaVersionForDyndeps()393   static std::string RequiredNinjaVersionForDyndeps() { return "1.10"; }
RequiredNinjaVersionForRestatTool()394   static std::string RequiredNinjaVersionForRestatTool() { return "1.10"; }
RequiredNinjaVersionForUnconditionalRecompactTool()395   static std::string RequiredNinjaVersionForUnconditionalRecompactTool()
396   {
397     return "1.10";
398   }
RequiredNinjaVersionForMultipleOutputs()399   static std::string RequiredNinjaVersionForMultipleOutputs()
400   {
401     return "1.10";
402   }
RequiredNinjaVersionForMetadataOnRegeneration()403   static std::string RequiredNinjaVersionForMetadataOnRegeneration()
404   {
405     return "1.10.2";
406   }
RequiredNinjaVersionForCodePage()407   static std::string RequiredNinjaVersionForCodePage() { return "1.11"; }
408   bool SupportsDirectConsole() const override;
409   bool SupportsImplicitOuts() const;
410   bool SupportsManifestRestat() const;
411   bool SupportsMultilineDepfile() const;
412 
413   std::string NinjaOutputPath(std::string const& path) const;
HasOutputPathPrefix()414   bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); }
415   void StripNinjaOutputPathPrefixAsSuffix(std::string& path);
416 
417   bool WriteDyndepFile(
418     std::string const& dir_top_src, std::string const& dir_top_bld,
419     std::string const& dir_cur_src, std::string const& dir_cur_bld,
420     std::string const& arg_dd, std::vector<std::string> const& arg_ddis,
421     std::string const& module_dir,
422     std::vector<std::string> const& linked_target_dirs,
423     std::string const& arg_lang, std::string const& arg_modmapfmt);
424 
BuildAlias(const std::string & alias,const std::string &)425   virtual std::string BuildAlias(const std::string& alias,
426                                  const std::string& /*config*/) const
427   {
428     return alias;
429   }
430 
ConfigDirectory(const std::string &)431   virtual std::string ConfigDirectory(const std::string& /*config*/) const
432   {
433     return "";
434   }
435 
GetByproductsForCleanTarget()436   cmNinjaDeps& GetByproductsForCleanTarget()
437   {
438     return this->ByproductsForCleanTarget;
439   }
440 
GetByproductsForCleanTarget(const std::string & config)441   cmNinjaDeps& GetByproductsForCleanTarget(const std::string& config)
442   {
443     return this->Configs[config].ByproductsForCleanTarget;
444   }
445 
446   bool EnableCrossConfigBuild() const;
447 
448   std::set<std::string> GetCrossConfigs(const std::string& config) const;
449 
GetDefaultConfigs()450   const std::set<std::string>& GetDefaultConfigs() const
451   {
452     return this->DefaultConfigs;
453   }
454 
GetPerConfigUtilityTargets()455   const std::set<std::string>& GetPerConfigUtilityTargets() const
456   {
457     return this->PerConfigUtilityTargets;
458   }
459 
AddPerConfigUtilityTarget(const std::string & name)460   void AddPerConfigUtilityTarget(const std::string& name)
461   {
462     this->PerConfigUtilityTargets.insert(name);
463   }
464 
465   bool IsSingleConfigUtility(cmGeneratorTarget const* target) const;
466 
467   bool CheckCxxModuleSupport();
468 
469 protected:
470   void Generate() override;
471 
CheckALLOW_DUPLICATE_CUSTOM_TARGETS()472   bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const override { return true; }
473 
474   virtual bool OpenBuildFileStreams();
475   virtual void CloseBuildFileStreams();
476 
477   bool OpenFileStream(std::unique_ptr<cmGeneratedFileStream>& stream,
478                       const std::string& name);
479 
480   static cm::optional<std::set<std::string>> ListSubsetWithAll(
481     const std::set<std::string>& all, const std::set<std::string>& defaults,
482     const std::vector<std::string>& items);
483 
484   std::set<std::string> CrossConfigs;
485   std::set<std::string> DefaultConfigs;
486   std::string DefaultFileConfig;
487 
488 private:
489   bool FindMakeProgram(cmMakefile* mf) override;
490   void CheckNinjaFeatures();
491   void CheckNinjaCodePage();
492   bool CheckLanguages(std::vector<std::string> const& languages,
493                       cmMakefile* mf) const override;
494   bool CheckFortran(cmMakefile* mf) const;
495   bool CheckISPC(cmMakefile* mf) const;
496 
497   void CloseCompileCommandsStream();
498 
499   bool OpenRulesFileStream();
500   void CloseRulesFileStream();
501   void CleanMetaData();
502 
503   /// Write the common disclaimer text at the top of each build file.
504   void WriteDisclaimer(std::ostream& os) const;
505 
506   void WriteAssumedSourceDependencies();
507 
508   void WriteTargetAliases(std::ostream& os);
509   void WriteFolderTargets(std::ostream& os);
510   void WriteUnknownExplicitDependencies(std::ostream& os);
511 
512   void WriteBuiltinTargets(std::ostream& os);
513   void WriteTargetDefault(std::ostream& os);
514   void WriteTargetRebuildManifest(std::ostream& os);
515   bool WriteTargetCleanAdditional(std::ostream& os);
516   void WriteTargetClean(std::ostream& os);
517   void WriteTargetHelp(std::ostream& os);
518 
519   void ComputeTargetDependsClosure(
520     cmGeneratorTarget const* target,
521     std::set<cmGeneratorTarget const*>& depends);
522 
523   std::string CMakeCmd() const;
524   std::string NinjaCmd() const;
525 
526   /// The file containing the build statement. (the relationship of the
527   /// compilation DAG).
528   std::unique_ptr<cmGeneratedFileStream> BuildFileStream;
529   /// The file containing the rule statements. (The action attached to each
530   /// edge of the compilation DAG).
531   std::unique_ptr<cmGeneratedFileStream> RulesFileStream;
532   std::unique_ptr<cmGeneratedFileStream> CompileCommandsStream;
533 
534   /// The set of rules added to the generated build system.
535   std::unordered_set<std::string> Rules;
536 
537   /// Length of rule command, used by rsp file evaluation
538   std::unordered_map<std::string, int> RuleCmdLength;
539 
540   bool UsingGCCOnWindows = false;
541 
542   /// The set of custom command outputs we have seen.
543   std::set<std::string> CustomCommandOutputs;
544 
545   /// Whether we are collecting known build outputs and needed
546   /// dependencies to determine unknown dependencies.
547   bool ComputingUnknownDependencies = false;
548   cmPolicies::PolicyStatus PolicyCMP0058 = cmPolicies::WARN;
549 
550   /// The combined explicit dependencies of custom build commands
551   std::set<std::string> CombinedCustomCommandExplicitDependencies;
552 
553   /// When combined with CombinedCustomCommandExplicitDependencies it allows
554   /// us to detect the set of explicit dependencies that have
555   std::set<std::string> CombinedBuildOutputs;
556 
557   /// The mapping from source file to assumed dependencies.
558   std::map<std::string, std::set<std::string>> AssumedSourceDependencies;
559 
560   /// Utility targets which have per-config outputs
561   std::set<std::string> PerConfigUtilityTargets;
562 
563   struct TargetAlias
564   {
565     cmGeneratorTarget* GeneratorTarget;
566     std::string Config;
567   };
568   using TargetAliasMap = std::map<std::string, TargetAlias>;
569   TargetAliasMap TargetAliases;
570   TargetAliasMap DefaultTargetAliases;
571 
572   /// the local cache for calls to ConvertToNinjaPath
573   mutable std::unordered_map<std::string, std::string> ConvertToNinjaPathCache;
574 
575   std::string NinjaCommand;
576   std::string NinjaVersion;
577   bool NinjaSupportsConsolePool = false;
578   bool NinjaSupportsImplicitOuts = false;
579   bool NinjaSupportsManifestRestat = false;
580   bool NinjaSupportsMultilineDepfile = false;
581   bool NinjaSupportsDyndeps = false;
582   bool NinjaSupportsRestatTool = false;
583   bool NinjaSupportsUnconditionalRecompactTool = false;
584   bool NinjaSupportsMultipleOutputs = false;
585   bool NinjaSupportsMetadataOnRegeneration = false;
586   bool NinjaSupportsCodePage = false;
587 
588   codecvt::Encoding NinjaExpectedEncoding = codecvt::None;
589 
590   bool DiagnosedCxxModuleSupport = false;
591 
592   void InitOutputPathPrefix();
593 
594   std::string OutputPathPrefix;
595   std::string TargetAll;
596   std::string CMakeCacheFile;
597   bool DisableCleandead = false;
598 
599   struct ByConfig
600   {
601     std::set<std::string> AdditionalCleanFiles;
602 
603     /// The set of custom commands we have seen.
604     std::set<cmCustomCommand const*> CustomCommands;
605 
606     struct TargetDependsClosureKey
607     {
608       cmGeneratorTarget const* Target;
609       std::string Config;
610       bool GenexOutput;
611     };
612 
613     std::map<TargetDependsClosureKey, cmNinjaOuts> TargetDependsClosures;
614 
615     TargetAliasMap TargetAliases;
616 
617     cmNinjaDeps ByproductsForCleanTarget;
618   };
619   std::map<std::string, ByConfig> Configs;
620 
621   cmNinjaDeps ByproductsForCleanTarget;
622 
623   friend bool operator==(const ByConfig::TargetDependsClosureKey& lhs,
624                          const ByConfig::TargetDependsClosureKey& rhs);
625   friend bool operator!=(const ByConfig::TargetDependsClosureKey& lhs,
626                          const ByConfig::TargetDependsClosureKey& rhs);
627   friend bool operator<(const ByConfig::TargetDependsClosureKey& lhs,
628                         const ByConfig::TargetDependsClosureKey& rhs);
629   friend bool operator>(const ByConfig::TargetDependsClosureKey& lhs,
630                         const ByConfig::TargetDependsClosureKey& rhs);
631   friend bool operator<=(const ByConfig::TargetDependsClosureKey& lhs,
632                          const ByConfig::TargetDependsClosureKey& rhs);
633   friend bool operator>=(const ByConfig::TargetDependsClosureKey& lhs,
634                          const ByConfig::TargetDependsClosureKey& rhs);
635 };
636 
637 class cmGlobalNinjaMultiGenerator : public cmGlobalNinjaGenerator
638 {
639 public:
640   /// The default name of Ninja's common file. Typically: common.ninja.
641   static const char* NINJA_COMMON_FILE;
642   /// The default file extension to use for per-config Ninja files.
643   static const char* NINJA_FILE_EXTENSION;
644 
645   cmGlobalNinjaMultiGenerator(cmake* cm);
IsMultiConfig()646   bool IsMultiConfig() const override { return true; }
NewFactory()647   static std::unique_ptr<cmGlobalGeneratorFactory> NewFactory()
648   {
649     return std::unique_ptr<cmGlobalGeneratorFactory>(
650       new cmGlobalGeneratorSimpleFactory<cmGlobalNinjaMultiGenerator>());
651   }
652 
653   static void GetDocumentation(cmDocumentationEntry& entry);
654 
GetName()655   std::string GetName() const override
656   {
657     return cmGlobalNinjaMultiGenerator::GetActualName();
658   }
659 
GetActualName()660   static std::string GetActualName() { return "Ninja Multi-Config"; }
661 
BuildAlias(const std::string & alias,const std::string & config)662   std::string BuildAlias(const std::string& alias,
663                          const std::string& config) const override
664   {
665     if (config.empty()) {
666       return alias;
667     }
668     return cmStrCat(alias, ":", config);
669   }
670 
ConfigDirectory(const std::string & config)671   std::string ConfigDirectory(const std::string& config) const override
672   {
673     if (!config.empty()) {
674       return cmStrCat('/', config);
675     }
676     return "";
677   }
678 
GetCMakeCFGIntDir()679   const char* GetCMakeCFGIntDir() const override { return "${CONFIGURATION}"; }
680 
681   std::string ExpandCFGIntDir(const std::string& str,
682                               const std::string& config) const override;
683 
GetImplFileStream(const std::string & config)684   cmGeneratedFileStream* GetImplFileStream(
685     const std::string& config) const override
686   {
687     return this->ImplFileStreams.at(config).get();
688   }
689 
GetConfigFileStream(const std::string & config)690   cmGeneratedFileStream* GetConfigFileStream(
691     const std::string& config) const override
692   {
693     return this->ConfigFileStreams.at(config).get();
694   }
695 
GetDefaultFileStream()696   cmGeneratedFileStream* GetDefaultFileStream() const override
697   {
698     return this->DefaultFileStream.get();
699   }
700 
GetCommonFileStream()701   cmGeneratedFileStream* GetCommonFileStream() const override
702   {
703     return this->CommonFileStream.get();
704   }
705 
706   void AppendNinjaFileArgument(GeneratedMakeCommand& command,
707                                const std::string& config) const override;
708 
709   static std::string GetNinjaImplFilename(const std::string& config);
710   static std::string GetNinjaConfigFilename(const std::string& config);
711 
712   void AddRebuildManifestOutputs(cmNinjaDeps& outputs) const override;
713 
714   void GetQtAutoGenConfigs(std::vector<std::string>& configs) const override;
715 
716   bool InspectConfigTypeVariables() override;
717 
718   std::string GetDefaultBuildConfig() const override;
719 
SupportsDefaultBuildType()720   bool SupportsDefaultBuildType() const override { return true; }
SupportsCrossConfigs()721   bool SupportsCrossConfigs() const override { return true; }
SupportsDefaultConfigs()722   bool SupportsDefaultConfigs() const override { return true; }
723 
724   std::string OrderDependsTargetForTarget(
725     cmGeneratorTarget const* target, const std::string& config) const override;
726 
727 protected:
728   bool OpenBuildFileStreams() override;
729   void CloseBuildFileStreams() override;
730 
731 private:
732   std::map<std::string, std::unique_ptr<cmGeneratedFileStream>>
733     ImplFileStreams;
734   std::map<std::string, std::unique_ptr<cmGeneratedFileStream>>
735     ConfigFileStreams;
736   std::unique_ptr<cmGeneratedFileStream> CommonFileStream;
737   std::unique_ptr<cmGeneratedFileStream> DefaultFileStream;
738 };
739