Coverage Report

Created: 2026-04-29 07:01

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