Coverage Report

Created: 2026-10-28 06:38

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