Coverage Report

Created: 2026-03-12 06:35

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