Coverage Report

Created: 2026-02-09 06:05

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