Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmNinjaTargetGenerator.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 "cmNinjaTargetGenerator.h"
4
5
#include <algorithm>
6
#include <array>
7
#include <cassert>
8
#include <functional>
9
#include <iterator>
10
#include <map>
11
#include <ostream>
12
#include <unordered_map>
13
#include <unordered_set>
14
#include <utility>
15
16
#include <cm/memory>
17
#include <cm/optional>
18
#include <cm/string_view>
19
#include <cmext/algorithm>
20
#include <cmext/string_view>
21
22
#include <cm3p/json/value.h>
23
#include <cm3p/json/writer.h>
24
25
#include "cmBuildDatabase.h"
26
#include "cmComputeLinkInformation.h"
27
#include "cmCustomCommand.h"
28
#include "cmCustomCommandGenerator.h"
29
#include "cmDyndepCollation.h"
30
#include "cmFileSet.h"
31
#include "cmGeneratedFileStream.h"
32
#include "cmGeneratorExpression.h"
33
#include "cmGeneratorOptions.h"
34
#include "cmGeneratorTarget.h"
35
#include "cmGlobalCommonGenerator.h"
36
#include "cmGlobalNinjaGenerator.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 "cmNinjaNormalTargetGenerator.h"
44
#include "cmNinjaUtilityTargetGenerator.h"
45
#include "cmOutputConverter.h"
46
#include "cmPolicies.h"
47
#include "cmRange.h"
48
#include "cmRulePlaceholderExpander.h"
49
#include "cmSourceFile.h"
50
#include "cmState.h"
51
#include "cmStateTypes.h"
52
#include "cmStringAlgorithms.h"
53
#include "cmSystemTools.h"
54
#include "cmTarget.h"
55
#include "cmTargetDepend.h"
56
#include "cmValue.h"
57
#include "cmake.h"
58
59
std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New(
60
  cmGeneratorTarget* target)
61
0
{
62
0
  switch (target->GetType()) {
63
0
    case cmStateEnums::EXECUTABLE:
64
0
    case cmStateEnums::SHARED_LIBRARY:
65
0
    case cmStateEnums::STATIC_LIBRARY:
66
0
    case cmStateEnums::MODULE_LIBRARY:
67
0
    case cmStateEnums::OBJECT_LIBRARY:
68
0
      return cm::make_unique<cmNinjaNormalTargetGenerator>(target);
69
70
0
    case cmStateEnums::INTERFACE_LIBRARY:
71
0
      if (target->HaveCxx20ModuleSources()) {
72
0
        return cm::make_unique<cmNinjaNormalTargetGenerator>(target);
73
0
      }
74
0
      CM_FALLTHROUGH;
75
76
0
    case cmStateEnums::UTILITY:
77
0
    case cmStateEnums::GLOBAL_TARGET:
78
0
      return cm::make_unique<cmNinjaUtilityTargetGenerator>(target);
79
80
0
    default:
81
0
      return std::unique_ptr<cmNinjaTargetGenerator>();
82
0
  }
83
0
}
84
85
cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmGeneratorTarget* target)
86
0
  : cmCommonTargetGenerator(target)
87
0
  , OSXBundleGenerator(nullptr)
88
  , LocalGenerator(
89
0
      static_cast<cmLocalNinjaGenerator*>(target->GetLocalGenerator()))
90
0
{
91
0
  for (auto const& fileConfig : this->LocalGenerator->GetConfigNames()) {
92
0
    this->Configs[fileConfig].MacOSXContentGenerator =
93
0
      cm::make_unique<MacOSXContentGeneratorType>(this, fileConfig);
94
0
  }
95
0
}
96
97
0
cmNinjaTargetGenerator::~cmNinjaTargetGenerator() = default;
98
99
cmGeneratedFileStream& cmNinjaTargetGenerator::GetImplFileStream(
100
  std::string const& config) const
101
0
{
102
0
  return *this->GetGlobalGenerator()->GetImplFileStream(config);
103
0
}
104
105
cmGeneratedFileStream& cmNinjaTargetGenerator::GetCommonFileStream() const
106
0
{
107
0
  return *this->GetGlobalGenerator()->GetCommonFileStream();
108
0
}
109
110
cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const
111
0
{
112
0
  return *this->GetGlobalGenerator()->GetRulesFileStream();
113
0
}
114
115
cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const
116
0
{
117
0
  return this->LocalGenerator->GetGlobalNinjaGenerator();
118
0
}
119
120
std::string cmNinjaTargetGenerator::LanguageCompilerRule(
121
  std::string const& lang, std::string const& config,
122
  WithScanning withScanning) const
123
0
{
124
0
  return cmStrCat(
125
0
    lang, "_COMPILER__",
126
0
    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
127
0
    withScanning == WithScanning::Yes ? "_scanned_" : "_unscanned_", config);
128
0
}
129
130
std::string cmNinjaTargetGenerator::LanguagePreprocessAndScanRule(
131
  std::string const& lang, std::string const& config) const
132
0
{
133
0
  return cmStrCat(
134
0
    lang, "_PREPROCESS_SCAN__",
135
0
    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
136
0
    '_', config);
137
0
}
138
139
std::string cmNinjaTargetGenerator::LanguageScanRule(
140
  std::string const& lang, std::string const& config) const
141
0
{
142
0
  return cmStrCat(
143
0
    lang, "_SCAN__",
144
0
    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
145
0
    '_', config);
146
0
}
147
148
bool cmNinjaTargetGenerator::NeedExplicitPreprocessing(
149
  std::string const& lang) const
150
0
{
151
0
  return lang == "Fortran" || lang == "Swift";
152
0
}
153
154
bool cmNinjaTargetGenerator::CompileWithDefines(std::string const& lang) const
155
0
{
156
0
  return this->Makefile->IsOn(
157
0
    cmStrCat("CMAKE_", lang, "_COMPILE_WITH_DEFINES"));
158
0
}
159
160
std::string cmNinjaTargetGenerator::LanguageDyndepRule(
161
  std::string const& lang, std::string const& config) const
162
0
{
163
0
  return cmStrCat(
164
0
    lang, "_DYNDEP__",
165
0
    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
166
0
    '_', config);
167
0
}
168
169
std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget(
170
  std::string const& config)
171
0
{
172
0
  return this->GetGlobalGenerator()->OrderDependsTargetForTarget(
173
0
    this->GeneratorTarget, config);
174
0
}
175
176
std::string cmNinjaTargetGenerator::OrderDependsTargetForTargetPrivate(
177
  std::string const& config)
178
0
{
179
0
  return this->GetGlobalGenerator()->OrderDependsTargetForTargetPrivate(
180
0
    this->GeneratorTarget, config);
181
0
}
182
183
// TODO: Most of the code is picked up from
184
// void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink),
185
// void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
186
// Refactor it.
187
std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
188
  cmSourceFile const* source, std::string const& language,
189
  std::string const& config, std::string const& objectFileName)
190
0
{
191
0
  std::unordered_map<std::string, std::string> pchSources;
192
0
  std::vector<std::string> pchArchs =
193
0
    this->GeneratorTarget->GetPchArchs(config, language);
194
195
0
  std::string filterArch;
196
0
  for (std::string const& arch : pchArchs) {
197
0
    std::string const pchSource =
198
0
      this->GeneratorTarget->GetPchSource(config, language, arch);
199
0
    if (pchSource == source->GetFullPath()) {
200
0
      filterArch = arch;
201
0
    }
202
0
    if (!pchSource.empty()) {
203
0
      pchSources.insert(std::make_pair(pchSource, arch));
204
0
    }
205
0
  }
206
207
0
  std::string flags;
208
  // Explicitly add the explicit language flag before any other flag
209
  // so user flags can override it.
210
0
  this->GeneratorTarget->AddExplicitLanguageFlags(flags, *source);
211
212
0
  if (!flags.empty()) {
213
0
    flags += " ";
214
0
  }
215
0
  flags += this->GetFlags(language, config, filterArch);
216
217
  // Add Fortran format flags.
218
0
  if (language == "Fortran") {
219
0
    this->AppendFortranFormatFlags(flags, *source);
220
0
    this->AppendFortranPreprocessFlags(flags, *source,
221
0
                                       PreprocessFlagsRequired::NO);
222
0
  }
223
224
  // Add source file specific flags.
225
0
  cmGeneratorExpressionInterpreter genexInterpreter(
226
0
    this->LocalGenerator, config, this->GeneratorTarget, language);
227
228
0
  std::string const COMPILE_FLAGS("COMPILE_FLAGS");
229
0
  if (cmValue cflags = source->GetProperty(COMPILE_FLAGS)) {
230
0
    this->LocalGenerator->AppendFlags(
231
0
      flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS));
232
0
  }
233
234
0
  std::string const COMPILE_OPTIONS("COMPILE_OPTIONS");
235
0
  if (cmValue coptions = source->GetProperty(COMPILE_OPTIONS)) {
236
0
    this->LocalGenerator->AppendCompileOptions(
237
0
      flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
238
0
  }
239
240
  // Add precompile headers compile options.
241
0
  if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
242
0
    std::string pchOptions;
243
0
    auto pchIt = pchSources.find(source->GetFullPath());
244
0
    if (pchIt != pchSources.end()) {
245
0
      pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions(
246
0
        config, language, pchIt->second);
247
0
    } else {
248
0
      pchOptions =
249
0
        this->GeneratorTarget->GetPchUseCompileOptions(config, language);
250
0
    }
251
252
0
    this->LocalGenerator->AppendCompileOptions(
253
0
      flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
254
0
  }
255
256
0
  auto const* fs = this->GeneratorTarget->GetFileSetForSource(config, source);
257
0
  if (fs && fs->GetType() == "CXX_MODULES"_s) {
258
0
    if (source->GetLanguage() != "CXX"_s) {
259
0
      this->GetMakefile()->IssueMessage(
260
0
        MessageType::FATAL_ERROR,
261
0
        cmStrCat("Target \"", this->GeneratorTarget->Target->GetName(),
262
0
                 "\" contains the source\n  ", source->GetFullPath(),
263
0
                 "\nin a file set of type \"", fs->GetType(),
264
0
                 R"(" but the source is not classified as a "CXX" source.)"));
265
0
    }
266
267
0
    if (!this->GeneratorTarget->Target->IsNormal()) {
268
0
      auto flag = this->GetMakefile()->GetSafeDefinition(
269
0
        "CMAKE_CXX_MODULE_BMI_ONLY_FLAG");
270
0
      cmRulePlaceholderExpander::RuleVariables compileObjectVars;
271
0
      compileObjectVars.Object = objectFileName.c_str();
272
0
      auto rulePlaceholderExpander =
273
0
        this->GetLocalGenerator()->CreateRulePlaceholderExpander();
274
0
      rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
275
0
                                                   flag, compileObjectVars);
276
0
      this->LocalGenerator->AppendCompileOptions(flags, flag);
277
0
    }
278
0
  }
279
280
0
  return flags;
281
0
}
282
283
void cmNinjaTargetGenerator::AddIncludeFlags(std::string& languageFlags,
284
                                             std::string const& language,
285
                                             std::string const& config)
286
0
{
287
0
  std::vector<std::string> includes;
288
0
  this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
289
0
                                              language, config);
290
  // Add include directory flags.
291
0
  std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
292
0
    includes, this->GeneratorTarget, language, config, false);
293
0
  if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
294
0
    std::replace(includeFlags.begin(), includeFlags.end(), '\\', '/');
295
0
  }
296
297
0
  this->LocalGenerator->AppendFlags(languageFlags, includeFlags);
298
0
}
299
300
// TODO: Refactor with
301
// void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
302
std::string cmNinjaTargetGenerator::ComputeDefines(cmSourceFile const* source,
303
                                                   std::string const& language,
304
                                                   std::string const& config)
305
0
{
306
0
  std::set<std::string> defines;
307
0
  cmGeneratorExpressionInterpreter genexInterpreter(
308
0
    this->LocalGenerator, config, this->GeneratorTarget, language);
309
310
  // Seriously??
311
0
  if (this->GetGlobalGenerator()->IsMultiConfig()) {
312
0
    defines.insert(cmStrCat("CMAKE_INTDIR=\"", config, '"'));
313
0
  }
314
315
0
  std::string const COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
316
0
  if (cmValue compile_defs = source->GetProperty(COMPILE_DEFINITIONS)) {
317
0
    this->LocalGenerator->AppendDefines(
318
0
      defines, genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS));
319
0
  }
320
321
0
  std::string defPropName =
322
0
    cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config));
323
0
  if (cmValue config_compile_defs = source->GetProperty(defPropName)) {
324
0
    this->LocalGenerator->AppendDefines(
325
0
      defines,
326
0
      genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS));
327
0
  }
328
329
0
  std::string definesString = this->GetDefines(language, config);
330
0
  this->LocalGenerator->JoinDefines(defines, definesString, language);
331
332
0
  return definesString;
333
0
}
334
335
std::string cmNinjaTargetGenerator::ComputeIncludes(
336
  cmSourceFile const* source, std::string const& language,
337
  std::string const& config)
338
0
{
339
0
  std::vector<std::string> includes;
340
0
  cmGeneratorExpressionInterpreter genexInterpreter(
341
0
    this->LocalGenerator, config, this->GeneratorTarget, language);
342
343
0
  std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
344
0
  if (cmValue cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) {
345
0
    this->LocalGenerator->AppendIncludeDirectories(
346
0
      includes, genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES),
347
0
      *source);
348
0
  }
349
350
0
  std::string includesString = this->LocalGenerator->GetIncludeFlags(
351
0
    includes, this->GeneratorTarget, language, config, false);
352
0
  this->LocalGenerator->AppendFlags(includesString,
353
0
                                    this->GetIncludes(language, config));
354
355
0
  return includesString;
356
0
}
357
358
cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps(
359
  std::string const& linkLanguage, std::string const& config,
360
  bool ignoreType) const
361
0
{
362
  // Static libraries never depend on other targets for linking.
363
0
  if (!ignoreType &&
364
0
      (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
365
0
       this->GeneratorTarget->GetType() == cmStateEnums::OBJECT_LIBRARY)) {
366
0
    return cmNinjaDeps();
367
0
  }
368
369
0
  cmComputeLinkInformation* cli =
370
0
    this->GeneratorTarget->GetLinkInformation(config);
371
0
  if (!cli) {
372
0
    return cmNinjaDeps();
373
0
  }
374
375
0
  std::vector<std::string> const& deps = cli->GetDepends();
376
0
  cmNinjaDeps result(deps.size());
377
0
  std::transform(deps.begin(), deps.end(), result.begin(),
378
0
                 this->MapToNinjaPath());
379
380
  // Add a dependency on the link definitions file, if any.
381
0
  if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
382
0
        this->GeneratorTarget->GetModuleDefinitionInfo(config)) {
383
0
    for (cmSourceFile const* src : mdi->Sources) {
384
0
      result.push_back(this->ConvertToNinjaPath(src->GetFullPath()));
385
0
    }
386
0
  }
387
388
  // Add a dependency on user-specified manifest files, if any.
389
0
  std::vector<cmSourceFile const*> manifest_srcs;
390
0
  this->GeneratorTarget->GetManifests(manifest_srcs, config);
391
0
  for (cmSourceFile const* manifest_src : manifest_srcs) {
392
0
    result.push_back(this->ConvertToNinjaPath(manifest_src->GetFullPath()));
393
0
  }
394
395
  // Add user-specified dependencies.
396
0
  std::vector<std::string> linkDeps;
397
0
  this->GeneratorTarget->GetLinkDepends(linkDeps, config, linkLanguage);
398
0
  std::transform(linkDeps.begin(), linkDeps.end(), std::back_inserter(result),
399
0
                 this->MapToNinjaPath());
400
401
0
  return result;
402
0
}
403
404
std::string cmNinjaTargetGenerator::GetCompiledSourceNinjaPath(
405
  cmSourceFile const* source) const
406
0
{
407
  // Pass source files to the compiler by absolute path.
408
0
  return this->ConvertToNinjaAbsPath(source->GetFullPath());
409
0
}
410
411
std::string cmNinjaTargetGenerator::GetObjectFileDir(
412
  std::string const& config) const
413
0
{
414
0
  return this->LocalGenerator->MaybeRelativeToTopBinDir(
415
0
    cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
416
0
             this->GetGlobalGenerator()->GetConfigDirectory(config)));
417
0
}
418
419
std::string cmNinjaTargetGenerator::GetObjectFilePath(
420
  cmSourceFile const* source, std::string const& config) const
421
0
{
422
0
  std::string const& objectName = this->GeneratorTarget->GetObjectName(source);
423
0
  return cmStrCat(this->GetObjectFileDir(config), '/', objectName);
424
0
}
425
426
std::string cmNinjaTargetGenerator::GetBmiFilePath(
427
  cmSourceFile const* source, std::string const& config) const
428
0
{
429
0
  auto& importedConfigInfo = this->Configs.at(config).ImportedCxxModules;
430
0
  if (!importedConfigInfo.Initialized()) {
431
0
    std::string configUpper = cmSystemTools::UpperCase(config);
432
0
    std::string propName = cmStrCat("IMPORTED_CXX_MODULES_", configUpper);
433
0
    auto value = this->GeneratorTarget->GetSafeProperty(propName);
434
0
    importedConfigInfo.Initialize(value);
435
0
  }
436
437
0
  std::string bmiName =
438
0
    importedConfigInfo.BmiNameForSource(source->GetFullPath());
439
440
0
  return cmStrCat(this->GetObjectFileDir(config), '/', bmiName);
441
0
}
442
443
std::string cmNinjaTargetGenerator::GetClangTidyReplacementsFilePath(
444
  std::string const& directory, cmSourceFile const& source,
445
  std::string const& config) const
446
0
{
447
0
  auto const& objectName = this->GeneratorTarget->GetObjectName(&source);
448
0
  return cmStrCat(directory, '/',
449
0
                  this->LocalGenerator->CreateSafeObjectFileName(
450
0
                    this->GetObjectFileDir(config)),
451
0
                  '/', objectName, ".yaml");
452
0
}
453
454
std::string cmNinjaTargetGenerator::GetPreprocessedFilePath(
455
  cmSourceFile const* source, std::string const& config) const
456
0
{
457
  // Choose an extension to compile already-preprocessed source.
458
0
  std::string ppExt = source->GetExtension();
459
0
  if (cmHasPrefix(ppExt, 'F')) {
460
    // Some Fortran compilers automatically enable preprocessing for
461
    // upper-case extensions.  Since the source is already preprocessed,
462
    // use a lower-case extension.
463
0
    ppExt = cmSystemTools::LowerCase(ppExt);
464
0
  }
465
0
  if (ppExt == "fpp") {
466
    // Some Fortran compilers automatically enable preprocessing for
467
    // the ".fpp" extension.  Since the source is already preprocessed,
468
    // use the ".f" extension.
469
0
    ppExt = "f";
470
0
  }
471
472
  // Take the object file name and replace the extension.
473
0
  std::string const& objName = this->GeneratorTarget->GetObjectName(source);
474
0
  std::string const& objExt =
475
0
    this->GetGlobalGenerator()->GetLanguageOutputExtension(*source);
476
0
  assert(objName.size() >= objExt.size());
477
0
  std::string const ppName =
478
0
    cmStrCat(objName.substr(0, objName.size() - objExt.size()), "-pp.", ppExt);
479
480
0
  return cmStrCat(this->GetObjectFileDir(config), '/', ppName);
481
0
}
482
483
std::string cmNinjaTargetGenerator::GetDyndepFilePath(
484
  std::string const& lang, std::string const& config) const
485
0
{
486
0
  return cmStrCat(this->GetObjectFileDir(config), '/', lang, ".dd");
487
0
}
488
489
std::string cmNinjaTargetGenerator::GetTargetDependInfoPath(
490
  std::string const& lang, std::string const& config) const
491
0
{
492
0
  return cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
493
0
                  this->GetGlobalGenerator()->ConfigDirectory(config), '/',
494
0
                  lang, "DependInfo.json");
495
0
}
496
497
std::string cmNinjaTargetGenerator::GetTargetOutputDir(
498
  std::string const& config) const
499
0
{
500
0
  std::string dir = this->GeneratorTarget->GetDirectory(config);
501
0
  return this->ConvertToNinjaPath(dir);
502
0
}
503
504
std::string cmNinjaTargetGenerator::GetTargetFilePath(
505
  std::string const& name, std::string const& config) const
506
0
{
507
0
  std::string path = this->GetTargetOutputDir(config);
508
0
  if (path.empty() || path == ".") {
509
0
    return name;
510
0
  }
511
0
  path += cmStrCat('/', name);
512
0
  return path;
513
0
}
514
515
std::string cmNinjaTargetGenerator::GetTargetName() const
516
0
{
517
0
  return this->GeneratorTarget->GetName();
518
0
}
519
520
bool cmNinjaTargetGenerator::SetMsvcTargetPdbVariable(
521
  cmNinjaVars& vars, std::string const& config) const
522
0
{
523
0
  cmMakefile* mf = this->GetMakefile();
524
0
  if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") ||
525
0
      mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID") ||
526
0
      mf->GetDefinition("MSVC_CUDA_ARCHITECTURE_ID")) {
527
0
    std::string pdbPath;
528
0
    std::string compilePdbPath = this->ComputeTargetCompilePDB(config);
529
0
    if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE ||
530
0
        this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
531
0
        this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
532
0
        this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
533
0
      pdbPath = cmStrCat(this->GeneratorTarget->GetPDBDirectory(config), '/',
534
0
                         this->GeneratorTarget->GetPDBName(config));
535
0
    }
536
537
0
    vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
538
0
      this->ConvertToNinjaPath(pdbPath), cmOutputConverter::SHELL);
539
0
    vars["TARGET_COMPILE_PDB"] =
540
0
      this->GetLocalGenerator()->ConvertToOutputFormat(
541
0
        this->ConvertToNinjaPath(compilePdbPath), cmOutputConverter::SHELL);
542
543
0
    this->EnsureParentDirectoryExists(pdbPath);
544
0
    this->EnsureParentDirectoryExists(compilePdbPath);
545
0
    return true;
546
0
  }
547
0
  return false;
548
0
}
549
550
void cmNinjaTargetGenerator::WriteLanguageRules(std::string const& language,
551
                                                std::string const& config)
552
0
{
553
#ifdef NINJA_GEN_VERBOSE_FILES
554
  this->GetRulesFileStream() << "# Rules for language " << language << "\n\n";
555
#endif
556
0
  this->WriteCompileRule(language, config);
557
0
}
558
559
namespace {
560
// Create the command to run the dependency scanner
561
std::string GetScanCommand(
562
  cm::string_view cmakeCmd, cm::string_view tdi, cm::string_view lang,
563
  cm::string_view srcFile, cm::string_view ddiFile,
564
  cm::optional<cm::string_view> srcOrigFile = cm::nullopt)
565
0
{
566
0
  return cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi,
567
0
                  " --lang=", lang, " --src=", srcFile,
568
0
                  " --out=$out"
569
0
                  " --dep=$DEP_FILE --obj=$OBJ_FILE --ddi=",
570
0
                  ddiFile,
571
0
                  srcOrigFile ? cmStrCat(" --src-orig=", *srcOrigFile) : "");
572
0
}
573
574
// Helper function to create dependency scanning rule that may or may
575
// not perform explicit preprocessing too.
576
cmNinjaRule GetScanRule(
577
  std::string const& ruleName, std::string const& ppFileName,
578
  std::string const& deptype,
579
  cmRulePlaceholderExpander::RuleVariables const& vars,
580
  std::string const& responseFlag, std::string const& flags,
581
  cmRulePlaceholderExpander* const rulePlaceholderExpander,
582
  cmLocalNinjaGenerator* generator, std::vector<std::string> scanCmds,
583
  std::string const& outputConfig)
584
0
{
585
0
  cmNinjaRule rule(ruleName);
586
  // Scanning always uses a depfile for preprocessor dependencies.
587
0
  if (deptype == "msvc"_s) {
588
0
    rule.DepType = deptype;
589
0
    rule.DepFile.clear();
590
0
  } else {
591
0
    rule.DepType.clear(); // no deps= for multiple outputs
592
0
    rule.DepFile = "$DEP_FILE";
593
0
  }
594
595
0
  cmRulePlaceholderExpander::RuleVariables scanVars;
596
0
  scanVars.CMTargetName = vars.CMTargetName;
597
0
  scanVars.CMTargetType = vars.CMTargetType;
598
0
  scanVars.Language = vars.Language;
599
0
  scanVars.Object = "$OBJ_FILE";
600
0
  scanVars.PreprocessedSource = ppFileName.c_str();
601
0
  scanVars.DynDepFile = "$DYNDEP_INTERMEDIATE_FILE";
602
0
  scanVars.DependencyFile = rule.DepFile.c_str();
603
0
  scanVars.DependencyTarget = "$out";
604
0
  scanVars.Config = vars.Config;
605
606
  // Scanning needs the same preprocessor settings as direct compilation would.
607
0
  scanVars.Source = vars.Source;
608
0
  scanVars.Defines = vars.Defines;
609
0
  scanVars.Includes = vars.Includes;
610
611
  // Scanning needs the compilation flags too.
612
0
  std::string scanFlags = flags;
613
614
  // If using a response file, move defines, includes, and flags into it.
615
0
  if (!responseFlag.empty()) {
616
0
    rule.RspFile = "$RSP_FILE";
617
0
    rule.RspContent =
618
0
      cmStrCat(' ', scanVars.Defines, ' ', scanVars.Includes, ' ', scanFlags);
619
0
    scanFlags = cmStrCat(responseFlag, rule.RspFile);
620
0
    scanVars.Defines = "";
621
0
    scanVars.Includes = "";
622
0
  }
623
624
0
  scanVars.Flags = scanFlags.c_str();
625
626
  // Rule for scanning a source file.
627
0
  for (std::string& scanCmd : scanCmds) {
628
0
    rulePlaceholderExpander->ExpandRuleVariables(generator, scanCmd, scanVars);
629
0
  }
630
0
  rule.Command =
631
0
    generator->BuildCommandLine(scanCmds, outputConfig, outputConfig);
632
633
0
  return rule;
634
0
}
635
}
636
637
void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang,
638
                                              std::string const& config)
639
0
{
640
  // For some cases we scan to dynamically discover dependencies.
641
0
  bool const needDyndep = this->GetGeneratorTarget()->NeedDyndep(lang, config);
642
643
0
  if (needDyndep) {
644
0
    this->WriteCompileRule(lang, config, WithScanning::Yes);
645
0
  }
646
0
  this->WriteCompileRule(lang, config, WithScanning::No);
647
0
}
648
649
void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang,
650
                                              std::string const& config,
651
                                              WithScanning withScanning)
652
0
{
653
0
  cmRulePlaceholderExpander::RuleVariables vars;
654
0
  vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
655
0
  vars.CMTargetType =
656
0
    cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str();
657
0
  vars.Language = lang.c_str();
658
0
  vars.Source = "$in";
659
0
  vars.Object = "$out";
660
0
  vars.Defines = "$DEFINES";
661
0
  vars.Includes = "$INCLUDES";
662
0
  vars.TargetPDB = "$TARGET_PDB";
663
0
  vars.TargetCompilePDB = "$TARGET_COMPILE_PDB";
664
0
  vars.ObjectDir = "$OBJECT_DIR";
665
0
  vars.TargetSupportDir = "$TARGET_SUPPORT_DIR";
666
0
  vars.ObjectFileDir = "$OBJECT_FILE_DIR";
667
0
  vars.CudaCompileMode = "$CUDA_COMPILE_MODE";
668
0
  vars.ISPCHeader = "$ISPC_HEADER_FILE";
669
0
  vars.Config = "$CONFIG";
670
671
0
  cmMakefile* mf = this->GetMakefile();
672
673
  // For some cases we scan to dynamically discover dependencies.
674
0
  bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(lang);
675
676
0
  std::string flags = "$FLAGS";
677
678
0
  std::string responseFlag;
679
0
  bool const lang_supports_response = lang != "RC";
680
0
  if (lang_supports_response && this->ForceResponseFile()) {
681
0
    std::string const responseFlagVar =
682
0
      cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_FLAG");
683
0
    responseFlag = this->Makefile->GetSafeDefinition(responseFlagVar);
684
0
    if (responseFlag.empty() && lang != "CUDA") {
685
0
      responseFlag = "@";
686
0
    }
687
0
  }
688
0
  std::string const modmapFormatVar =
689
0
    cmStrCat("CMAKE_", lang, "_MODULE_MAP_FORMAT");
690
0
  std::string const modmapFormat =
691
0
    this->Makefile->GetSafeDefinition(modmapFormatVar);
692
693
0
  auto rulePlaceholderExpander =
694
0
    this->GetLocalGenerator()->CreateRulePlaceholderExpander();
695
696
0
  std::string const tdi = this->GetLocalGenerator()->ConvertToOutputFormat(
697
0
    this->ConvertToNinjaPath(this->GetTargetDependInfoPath(lang, config)),
698
0
    cmLocalGenerator::SHELL);
699
700
0
  std::string launcher;
701
0
  std::string val = this->GetLocalGenerator()->GetRuleLauncher(
702
0
    this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", config);
703
0
  if (cmNonempty(val)) {
704
0
    launcher = cmStrCat(val, ' ');
705
0
  }
706
707
0
  std::string const cmakeCmd =
708
0
    this->GetLocalGenerator()->ConvertToOutputFormat(
709
0
      cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
710
711
0
  if (withScanning == WithScanning::Yes) {
712
0
    auto const& scanDepType = this->GetMakefile()->GetSafeDefinition(
713
0
      cmStrCat("CMAKE_", lang, "_SCANDEP_DEPFILE_FORMAT"));
714
715
    // Rule to scan dependencies of sources that need preprocessing.
716
0
    {
717
0
      cmList scanCommands;
718
0
      std::string scanRuleName;
719
0
      std::string ppFileName;
720
0
      if (compilationPreprocesses) {
721
0
        scanRuleName = this->LanguageScanRule(lang, config);
722
0
        ppFileName = "$PREPROCESSED_OUTPUT_FILE";
723
0
        std::string const& scanCommand = mf->GetRequiredDefinition(
724
0
          cmStrCat("CMAKE_", lang, "_SCANDEP_SOURCE"));
725
0
        scanCommands.assign(scanCommand);
726
0
        for (auto& i : scanCommands) {
727
0
          i = cmStrCat(launcher, i);
728
0
        }
729
0
      } else {
730
0
        scanRuleName = this->LanguagePreprocessAndScanRule(lang, config);
731
0
        ppFileName = "$out";
732
0
        std::string const& ppCommand = mf->GetRequiredDefinition(
733
0
          cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE"));
734
0
        scanCommands.assign(ppCommand);
735
0
        for (auto& i : scanCommands) {
736
0
          i = cmStrCat(launcher, i);
737
0
        }
738
0
        scanCommands.emplace_back(GetScanCommand(
739
0
          cmakeCmd, tdi, lang, "$out", "$DYNDEP_INTERMEDIATE_FILE", "$in"));
740
0
      }
741
742
0
      auto scanRule = GetScanRule(
743
0
        scanRuleName, ppFileName, scanDepType, vars, responseFlag, flags,
744
0
        rulePlaceholderExpander.get(), this->GetLocalGenerator(),
745
0
        std::move(scanCommands), config);
746
747
0
      scanRule.Comment =
748
0
        cmStrCat("Rule for generating ", lang, " dependencies.");
749
0
      if (compilationPreprocesses) {
750
0
        scanRule.Description =
751
0
          cmStrCat("Scanning $in for ", lang, " dependencies");
752
0
      } else {
753
0
        scanRule.Description =
754
0
          cmStrCat("Building ", lang, " preprocessed $out");
755
0
      }
756
757
0
      this->GetGlobalGenerator()->AddRule(scanRule);
758
0
    }
759
760
0
    if (!compilationPreprocesses) {
761
      // Compilation will not preprocess, so it does not need the defines
762
      // unless the compiler wants them for some other purpose.
763
0
      if (!this->CompileWithDefines(lang)) {
764
0
        vars.Defines = "";
765
0
      }
766
767
      // Rule to scan dependencies of sources that do not need preprocessing.
768
0
      std::string const& scanRuleName = this->LanguageScanRule(lang, config);
769
0
      std::vector<std::string> scanCommands;
770
0
      scanCommands.emplace_back(
771
0
        GetScanCommand(cmakeCmd, tdi, lang, "$in", "$out"));
772
773
0
      auto scanRule =
774
0
        GetScanRule(scanRuleName, "", scanDepType, vars, "", flags,
775
0
                    rulePlaceholderExpander.get(), this->GetLocalGenerator(),
776
0
                    std::move(scanCommands), config);
777
778
      // Write the rule for generating dependencies for the given language.
779
0
      scanRule.Comment = cmStrCat("Rule for generating ", lang,
780
0
                                  " dependencies on non-preprocessed files.");
781
0
      scanRule.Description =
782
0
        cmStrCat("Generating ", lang, " dependencies for $in");
783
784
0
      this->GetGlobalGenerator()->AddRule(scanRule);
785
0
    }
786
787
    // Write the rule for ninja dyndep file generation.
788
0
    cmNinjaRule rule(this->LanguageDyndepRule(lang, config));
789
    // Command line length is almost always limited -> use response file for
790
    // dyndep rules
791
0
    rule.RspFile = "$out.rsp";
792
0
    rule.RspContent = "$in";
793
    // Ninja's collator writes all outputs using `cmGeneratedFileStream`, so
794
    // they are only updated if contents actually change. Avoid running
795
    // dependent jobs if the contents don't change by telling `ninja` to check
796
    // the timestamp again.
797
0
    rule.Restat = "1";
798
799
    // Run CMake dependency scanner on the source file (using the preprocessed
800
    // source if that was performed).
801
0
    std::string ddModmapArg;
802
0
    if (!modmapFormat.empty()) {
803
0
      ddModmapArg += cmStrCat(" --modmapfmt=", modmapFormat);
804
0
    }
805
0
    {
806
0
      std::vector<std::string> ddCmds;
807
0
      {
808
0
        std::string ccmd = cmStrCat(
809
0
          cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi, " --lang=", lang,
810
0
          ddModmapArg, " --dd=$out @", rule.RspFile);
811
0
        ddCmds.emplace_back(std::move(ccmd));
812
0
      }
813
0
      rule.Command =
814
0
        this->GetLocalGenerator()->BuildCommandLine(ddCmds, config, config);
815
0
    }
816
0
    rule.Comment =
817
0
      cmStrCat("Rule to generate ninja dyndep files for ", lang, '.');
818
0
    rule.Description = cmStrCat("Generating ", lang, " dyndep file $out");
819
0
    this->GetGlobalGenerator()->AddRule(rule);
820
0
  }
821
822
0
  cmNinjaRule rule(this->LanguageCompilerRule(lang, config, withScanning));
823
  // If using a response file, move defines, includes, and flags into it.
824
0
  if (!responseFlag.empty()) {
825
0
    rule.RspFile = "$RSP_FILE";
826
0
    rule.RspContent =
827
0
      cmStrCat(' ', vars.Defines, ' ', vars.Includes, ' ', flags);
828
0
    flags = cmStrCat(responseFlag, rule.RspFile);
829
0
    vars.Defines = "";
830
0
    vars.Includes = "";
831
832
    // Swift consumes all source files in a module at once, which reaches
833
    // command line length limits pretty quickly. Inject source files into the
834
    // response file in this case as well.
835
0
    if (lang == "Swift") {
836
0
      rule.RspContent = cmStrCat(rule.RspContent, ' ', vars.Source);
837
0
      vars.Source = "";
838
0
    }
839
0
  }
840
841
  // Tell ninja dependency format so all deps can be loaded into a database
842
0
  std::string cldeps;
843
0
  if (!compilationPreprocesses) {
844
    // The compiler will not do preprocessing, so it has no such dependencies.
845
0
  } else if (mf->IsOn(cmStrCat("CMAKE_NINJA_CMCLDEPS_", lang))) {
846
    // For the MS resource compiler we need cmcldeps, but skip dependencies
847
    // for source-file try_compile cases because they are always fresh.
848
0
    if (!mf->GetIsSourceFileTryCompile()) {
849
0
      rule.DepType = "gcc";
850
0
      rule.DepFile = "$DEP_FILE";
851
0
      cmValue d = mf->GetDefinition("CMAKE_C_COMPILER");
852
0
      std::string const cl =
853
0
        d ? *d : mf->GetSafeDefinition("CMAKE_CXX_COMPILER");
854
0
      std::string cmcldepsPath;
855
0
      cmSystemTools::GetShortPath(cmSystemTools::GetCMClDepsCommand(),
856
0
                                  cmcldepsPath);
857
0
      cldeps = cmStrCat(cmcldepsPath, ' ', lang, ' ', vars.Source,
858
0
                        " $DEP_FILE $out \"",
859
0
                        mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"),
860
0
                        "\" \"", cl, "\" ");
861
0
    }
862
0
  } else {
863
0
    auto const& depType = this->GetMakefile()->GetSafeDefinition(
864
0
      cmStrCat("CMAKE_", lang, "_DEPFILE_FORMAT"));
865
0
    if (depType == "msvc"_s) {
866
0
      rule.DepType = "msvc";
867
0
      rule.DepFile.clear();
868
0
    } else {
869
0
      rule.DepType = "gcc";
870
0
      rule.DepFile = "$DEP_FILE";
871
0
    }
872
0
    vars.DependencyFile = rule.DepFile.c_str();
873
0
    vars.DependencyTarget = "$out";
874
875
0
    std::string const flagsName = cmStrCat("CMAKE_DEPFILE_FLAGS_", lang);
876
0
    std::string depfileFlags = mf->GetSafeDefinition(flagsName);
877
0
    if (!depfileFlags.empty()) {
878
0
      rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
879
0
                                                   depfileFlags, vars);
880
0
      flags += cmStrCat(' ', depfileFlags);
881
0
    }
882
0
  }
883
884
0
  if (withScanning == WithScanning::Yes && !modmapFormat.empty()) {
885
0
    std::string modmapFlags =
886
0
      mf->GetRequiredDefinition(cmStrCat("CMAKE_", lang, "_MODULE_MAP_FLAG"));
887
0
    cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>",
888
0
                                 "$DYNDEP_MODULE_MAP_FILE");
889
0
    flags += cmStrCat(' ', modmapFlags);
890
0
  }
891
892
0
  vars.Flags = flags.c_str();
893
0
  vars.DependencyFile = rule.DepFile.c_str();
894
895
0
  std::string cudaCompileMode;
896
0
  if (lang == "CUDA") {
897
0
    if (this->GeneratorTarget->GetPropertyAsBool(
898
0
          "CUDA_SEPARABLE_COMPILATION")) {
899
0
      std::string const& rdcFlag =
900
0
        this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG");
901
0
      cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, ' ');
902
0
    }
903
0
    static std::array<cm::string_view, 4> const compileModes{
904
0
      { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s }
905
0
    };
906
0
    bool useNormalCompileMode = true;
907
0
    for (cm::string_view mode : compileModes) {
908
0
      auto propName = cmStrCat("CUDA_", mode, "_COMPILATION");
909
0
      auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG");
910
0
      if (this->GeneratorTarget->GetPropertyAsBool(propName)) {
911
0
        std::string const& flag =
912
0
          this->Makefile->GetRequiredDefinition(defName);
913
0
        cudaCompileMode = cmStrCat(cudaCompileMode, flag);
914
0
        useNormalCompileMode = false;
915
0
        break;
916
0
      }
917
0
    }
918
0
    if (useNormalCompileMode) {
919
0
      std::string const& wholeFlag =
920
0
        this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG");
921
0
      cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag);
922
0
    }
923
0
    vars.CudaCompileMode = cudaCompileMode.c_str();
924
0
  }
925
926
  // Rule for compiling object file.
927
0
  std::string const cmdVar = cmStrCat("CMAKE_", lang, "_COMPILE_OBJECT");
928
0
  std::string const& compileCmd = mf->GetRequiredDefinition(cmdVar);
929
0
  cmList compileCmds(compileCmd);
930
931
0
  if (!compileCmds.empty()) {
932
0
    compileCmds.front().insert(0, "${CODE_CHECK}");
933
0
    compileCmds.front().insert(0, "${LAUNCHER}");
934
0
  }
935
936
0
  if (!compileCmds.empty()) {
937
0
    compileCmds.front().insert(0, cldeps);
938
0
  }
939
940
0
  auto const& extraCommands = this->GetMakefile()->GetSafeDefinition(
941
0
    cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS"));
942
0
  if (!extraCommands.empty()) {
943
0
    compileCmds.append(extraCommands);
944
0
  }
945
946
0
  for (auto& i : compileCmds) {
947
0
    i = cmStrCat(launcher, i);
948
0
    rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), i,
949
0
                                                 vars);
950
0
  }
951
952
0
  rule.Command =
953
0
    this->GetLocalGenerator()->BuildCommandLine(compileCmds, config, config);
954
955
  // Write the rule for compiling file of the given language.
956
0
  rule.Comment = cmStrCat("Rule for compiling ", lang, " files.");
957
0
  rule.Description = cmStrCat("Building ", lang, " object $out");
958
0
  this->GetGlobalGenerator()->AddRule(rule);
959
0
}
960
961
void cmNinjaTargetGenerator::WriteObjectBuildStatements(
962
  std::string const& config, std::string const& fileConfig,
963
  bool firstForConfig)
964
0
{
965
0
  this->GeneratorTarget->CheckCxxModuleStatus(config);
966
967
  // Write comments.
968
0
  cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
969
0
  this->GetImplFileStream(fileConfig)
970
0
    << "# Object build statements for "
971
0
    << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
972
0
    << " target " << this->GetTargetName() << "\n\n";
973
974
0
  std::vector<cmCustomCommand const*> customCommands;
975
0
  {
976
0
    std::vector<cmSourceFile const*> customCommandSources;
977
0
    this->GeneratorTarget->GetCustomCommands(customCommandSources, config);
978
0
    for (cmSourceFile const* sf : customCommandSources) {
979
0
      cmCustomCommand const* cc = sf->GetCustomCommand();
980
0
      this->GetLocalGenerator()->AddCustomCommandTarget(
981
0
        cc, this->GetGeneratorTarget());
982
0
      customCommands.push_back(cc);
983
0
    }
984
0
  }
985
0
  {
986
0
    std::vector<cmSourceFile const*> headerSources;
987
0
    this->GeneratorTarget->GetHeaderSources(headerSources, config);
988
0
    this->OSXBundleGenerator->GenerateMacOSXContentStatements(
989
0
      headerSources, this->Configs[fileConfig].MacOSXContentGenerator.get(),
990
0
      config);
991
0
  }
992
0
  {
993
0
    std::vector<cmSourceFile const*> extraSources;
994
0
    this->GeneratorTarget->GetExtraSources(extraSources, config);
995
0
    this->OSXBundleGenerator->GenerateMacOSXContentStatements(
996
0
      extraSources, this->Configs[fileConfig].MacOSXContentGenerator.get(),
997
0
      config);
998
0
  }
999
0
  if (firstForConfig) {
1000
0
    cmValue pchExtension =
1001
0
      this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
1002
1003
0
    std::vector<cmSourceFile const*> externalObjects;
1004
0
    this->GeneratorTarget->GetExternalObjects(externalObjects, config);
1005
0
    for (cmSourceFile const* sf : externalObjects) {
1006
0
      auto objectFileName = this->GetGlobalGenerator()->ExpandCFGIntDir(
1007
0
        this->ConvertToNinjaPath(sf->GetFullPath()), config);
1008
0
      if (!cmHasSuffix(objectFileName, pchExtension)) {
1009
0
        this->Configs[config].Objects.push_back(objectFileName);
1010
0
      }
1011
0
    }
1012
0
  }
1013
1014
0
  if (!this->GetGlobalGenerator()->SupportsCWDDepend()) {
1015
    // Ensure that the object directory exists. If there are no objects in the
1016
    // target (e.g., an empty `OBJECT` library), the directory is still listed
1017
    // as an order-only depends in the build files. Alternate `ninja`
1018
    // implementations may not allow this (such as `samu`). See #25526.
1019
0
    auto const objectDir = this->GetObjectFileDir(config);
1020
0
    this->EnsureDirectoryExists(objectDir);
1021
0
  }
1022
1023
0
  {
1024
0
    cmNinjaBuild build("phony");
1025
0
    build.Comment =
1026
0
      cmStrCat("Order-only phony target for ", this->GetTargetName());
1027
0
    build.Outputs.push_back(this->OrderDependsTargetForTarget(config));
1028
1029
    // Gather order-only dependencies on custom command outputs.
1030
0
    std::vector<std::string> ccouts;
1031
0
    std::vector<std::string> ccouts_private;
1032
0
    bool usePrivateGeneratedSources = false;
1033
0
    if (this->GeneratorTarget->Target->HasFileSets()) {
1034
0
      switch (this->GetGeneratorTarget()->GetPolicyStatusCMP0154()) {
1035
0
        case cmPolicies::WARN:
1036
0
        case cmPolicies::OLD:
1037
0
          break;
1038
0
        case cmPolicies::NEW:
1039
0
          usePrivateGeneratedSources = true;
1040
0
          break;
1041
0
      }
1042
0
    }
1043
0
    for (cmCustomCommand const* cc : customCommands) {
1044
0
      cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator());
1045
0
      std::vector<std::string> const& ccoutputs = ccg.GetOutputs();
1046
0
      std::vector<std::string> const& ccbyproducts = ccg.GetByproducts();
1047
0
      auto const nPreviousOutputs = ccouts.size();
1048
0
      ccouts.insert(ccouts.end(), ccoutputs.begin(), ccoutputs.end());
1049
0
      ccouts.insert(ccouts.end(), ccbyproducts.begin(), ccbyproducts.end());
1050
0
      if (usePrivateGeneratedSources) {
1051
0
        auto it = ccouts.begin();
1052
        // Skip over outputs that were already detected.
1053
0
        std::advance(it, nPreviousOutputs);
1054
0
        while (it != ccouts.end()) {
1055
0
          cmFileSet const* fileset =
1056
0
            this->GeneratorTarget->GetFileSetForSource(
1057
0
              config, this->Makefile->GetOrCreateGeneratedSource(*it));
1058
0
          bool isVisible = fileset &&
1059
0
            cmFileSetVisibilityIsForInterface(fileset->GetVisibility());
1060
0
          bool isIncludeable =
1061
0
            !fileset || cmFileSetTypeCanBeIncluded(fileset->GetType());
1062
0
          if (fileset && isVisible && isIncludeable) {
1063
0
            ++it;
1064
0
            continue;
1065
0
          }
1066
0
          if (!fileset || isIncludeable) {
1067
0
            ccouts_private.push_back(*it);
1068
0
          }
1069
0
          it = ccouts.erase(it);
1070
0
        }
1071
0
      }
1072
0
    }
1073
1074
0
    cmNinjaDeps& orderOnlyDeps = build.OrderOnlyDeps;
1075
0
    this->GetLocalGenerator()->AppendTargetDepends(
1076
0
      this->GeneratorTarget, orderOnlyDeps, config, fileConfig,
1077
0
      DependOnTargetOrdering);
1078
1079
    // Add order-only dependencies on other files associated with the target.
1080
0
    cm::append(orderOnlyDeps, this->Configs[config].ExtraFiles);
1081
1082
    // Add order-only dependencies on custom command outputs.
1083
0
    std::transform(ccouts.begin(), ccouts.end(),
1084
0
                   std::back_inserter(orderOnlyDeps), this->MapToNinjaPath());
1085
1086
0
    std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end());
1087
0
    orderOnlyDeps.erase(
1088
0
      std::unique(orderOnlyDeps.begin(), orderOnlyDeps.end()),
1089
0
      orderOnlyDeps.end());
1090
1091
    // The phony target must depend on at least one input or ninja will explain
1092
    // that "output ... of phony edge with no inputs doesn't exist" and
1093
    // consider the phony output "dirty".
1094
0
    if (orderOnlyDeps.empty()) {
1095
0
      std::string tgtDir;
1096
0
      if (this->GetGlobalGenerator()->SupportsCWDDepend()) {
1097
0
        tgtDir = ".";
1098
0
      } else {
1099
        // Any path that always exists will work here.
1100
0
        tgtDir = this->GetObjectFileDir(config);
1101
0
      }
1102
0
      orderOnlyDeps.push_back(this->ConvertToNinjaPath(tgtDir));
1103
0
    }
1104
1105
0
    this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1106
0
                                           build);
1107
1108
    // Add order-only dependencies on custom command outputs that are
1109
    // private to this target.
1110
0
    this->HasPrivateGeneratedSources = !ccouts_private.empty();
1111
0
    if (this->HasPrivateGeneratedSources) {
1112
0
      cmNinjaBuild buildPrivate("phony");
1113
0
      cmNinjaDeps& orderOnlyDepsPrivate = buildPrivate.OrderOnlyDeps;
1114
0
      orderOnlyDepsPrivate.push_back(
1115
0
        this->OrderDependsTargetForTarget(config));
1116
1117
0
      buildPrivate.Outputs.push_back(
1118
0
        this->OrderDependsTargetForTargetPrivate(config));
1119
1120
0
      std::transform(ccouts_private.begin(), ccouts_private.end(),
1121
0
                     std::back_inserter(orderOnlyDepsPrivate),
1122
0
                     this->MapToNinjaPath());
1123
1124
0
      std::sort(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end());
1125
0
      orderOnlyDepsPrivate.erase(
1126
0
        std::unique(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end()),
1127
0
        orderOnlyDepsPrivate.end());
1128
1129
0
      this->GetGlobalGenerator()->WriteBuild(
1130
0
        this->GetImplFileStream(fileConfig), buildPrivate);
1131
0
    }
1132
0
  }
1133
0
  {
1134
0
    std::vector<cmSourceFile const*> objectSources;
1135
0
    this->GeneratorTarget->GetObjectSources(objectSources, config);
1136
1137
0
    std::vector<cmSourceFile const*> swiftSources;
1138
1139
0
    for (cmSourceFile const* sf : objectSources) {
1140
0
      if (this->GetLocalGenerator()->IsSplitSwiftBuild() &&
1141
0
          sf->GetLanguage() == "Swift") {
1142
0
        swiftSources.push_back(sf);
1143
0
      } else {
1144
0
        this->WriteObjectBuildStatement(sf, config, fileConfig,
1145
0
                                        firstForConfig);
1146
0
      }
1147
0
    }
1148
0
    WriteSwiftObjectBuildStatement(swiftSources, config, fileConfig,
1149
0
                                   firstForConfig);
1150
0
  }
1151
1152
0
  {
1153
0
    std::vector<cmSourceFile const*> bmiOnlySources;
1154
0
    this->GeneratorTarget->GetCxxModuleSources(bmiOnlySources, config);
1155
1156
0
    for (cmSourceFile const* sf : bmiOnlySources) {
1157
0
      this->WriteCxxModuleBmiBuildStatement(sf, config, fileConfig,
1158
0
                                            firstForConfig);
1159
0
    }
1160
0
  }
1161
1162
  // Detect sources in `CXX_MODULES` which are not compiled.
1163
0
  {
1164
0
    std::vector<cmSourceFile*> sources;
1165
0
    this->GeneratorTarget->GetSourceFiles(sources, config);
1166
0
    for (cmSourceFile const* sf : sources) {
1167
0
      cmFileSet const* fs =
1168
0
        this->GeneratorTarget->GetFileSetForSource(config, sf);
1169
0
      if (!fs) {
1170
0
        continue;
1171
0
      }
1172
0
      if (fs->GetType() != "CXX_MODULES"_s) {
1173
0
        continue;
1174
0
      }
1175
0
      if (sf->GetLanguage().empty()) {
1176
0
        this->GeneratorTarget->Makefile->IssueMessage(
1177
0
          MessageType::FATAL_ERROR,
1178
0
          cmStrCat("Target \"", this->GeneratorTarget->GetName(),
1179
0
                   "\" has source file\n  ", sf->GetFullPath(),
1180
0
                   "\nin a \"FILE_SET TYPE CXX_MODULES\" but it is not "
1181
0
                   "scheduled for compilation."));
1182
0
      }
1183
0
    }
1184
0
  }
1185
1186
  // Check if there are Fortran objects which need to participate in forwarding
1187
  // module requirements.
1188
0
  if (this->GeneratorTarget->HaveFortranSources(config) &&
1189
0
      !this->Configs[config].ScanningInfo.count("Fortran")) {
1190
0
    ScanningFiles files;
1191
0
    this->Configs[config].ScanningInfo["Fortran"].emplace_back(files);
1192
0
    this->WriteCompileRule("Fortran", config, WithScanning::Yes);
1193
0
  }
1194
1195
0
  for (auto const& langScanningFiles : this->Configs[config].ScanningInfo) {
1196
0
    std::string const& language = langScanningFiles.first;
1197
0
    std::vector<ScanningFiles> const& scanningFiles = langScanningFiles.second;
1198
1199
0
    cmNinjaBuild build(this->LanguageDyndepRule(language, config));
1200
0
    build.Outputs.push_back(this->GetDyndepFilePath(language, config));
1201
0
    build.ImplicitOuts.emplace_back(
1202
0
      cmStrCat(this->GetObjectFileDir(config), '/', language, "Modules.json"));
1203
0
    build.ImplicitDeps.emplace_back(
1204
0
      this->GetTargetDependInfoPath(language, config));
1205
0
    {
1206
0
      auto bdb_path =
1207
0
        this->GeneratorTarget->BuildDatabasePath(language, config);
1208
0
      if (!bdb_path.empty()) {
1209
0
        build.ImplicitOuts.emplace_back(this->ConvertToNinjaPath(bdb_path));
1210
0
      }
1211
0
    }
1212
0
    auto bdb_path = this->GeneratorTarget->BuildDatabasePath(language, config);
1213
0
    if (!bdb_path.empty()) {
1214
0
      auto db = cmBuildDatabase::ForTarget(this->GeneratorTarget, config);
1215
0
      auto mcdb_template_path = cmStrCat(bdb_path, ".in");
1216
0
      db.Write(mcdb_template_path);
1217
0
      build.ImplicitDeps.emplace_back(std::move(mcdb_template_path));
1218
0
      build.ImplicitOuts.emplace_back(std::move(bdb_path));
1219
0
    }
1220
0
    for (auto const& scanFiles : scanningFiles) {
1221
0
      if (!scanFiles.ScanningOutput.empty()) {
1222
0
        build.ExplicitDeps.push_back(scanFiles.ScanningOutput);
1223
0
      }
1224
0
      if (!scanFiles.ModuleMapFile.empty()) {
1225
0
        build.ImplicitOuts.push_back(scanFiles.ModuleMapFile);
1226
0
      }
1227
0
    }
1228
1229
0
    this->WriteTargetDependInfo(language, config);
1230
1231
0
    auto const linked_directories =
1232
0
      this->GetLinkedTargetDirectories(language, config);
1233
0
    for (std::string const& l : linked_directories.Direct) {
1234
0
      build.ImplicitDeps.emplace_back(
1235
0
        this->ConvertToNinjaPath(cmStrCat(l, '/', language, "Modules.json")));
1236
0
    }
1237
0
    for (std::string const& l : linked_directories.Forward) {
1238
0
      build.ImplicitDeps.emplace_back(
1239
0
        this->ConvertToNinjaPath(cmStrCat(l, '/', language, "Modules.json")));
1240
0
    }
1241
1242
0
    this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1243
0
                                           build);
1244
0
  }
1245
1246
0
  this->GetImplFileStream(fileConfig) << '\n';
1247
0
}
1248
1249
void cmNinjaTargetGenerator::GenerateSwiftOutputFileMap(
1250
  std::string const& config, std::string& flags)
1251
0
{
1252
0
  if (this->Configs[config].SwiftOutputMap.empty()) {
1253
0
    return;
1254
0
  }
1255
1256
0
  std::string const targetSwiftDepsPath = [this, config]() -> std::string {
1257
0
    cmGeneratorTarget const* target = this->GeneratorTarget;
1258
0
    if (cmValue name = target->GetProperty("Swift_DEPENDENCIES_FILE")) {
1259
0
      return *name;
1260
0
    }
1261
0
    return this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(),
1262
0
                                             '/', config, '/',
1263
0
                                             target->GetName(), ".swiftdeps"));
1264
0
  }();
1265
1266
0
  std::string mapFilePath =
1267
0
    cmStrCat(this->GeneratorTarget->GetSupportDirectory(), '/', config,
1268
0
             "/"
1269
0
             "output-file-map.json");
1270
1271
  // build the global target dependencies
1272
  // https://github.com/apple/swift/blob/master/docs/Driver.md#output-file-maps
1273
0
  Json::Value deps(Json::objectValue);
1274
0
  deps["swift-dependencies"] = targetSwiftDepsPath;
1275
0
  this->Configs[config].SwiftOutputMap[""] = deps;
1276
1277
0
  cmGeneratedFileStream output(mapFilePath);
1278
0
  output << this->Configs[config].SwiftOutputMap;
1279
1280
  // Add flag
1281
0
  this->LocalGenerator->AppendFlags(flags, "-output-file-map");
1282
0
  this->LocalGenerator->AppendFlags(
1283
0
    flags,
1284
0
    this->GetLocalGenerator()->ConvertToOutputFormat(
1285
0
      ConvertToNinjaPath(mapFilePath), cmOutputConverter::SHELL));
1286
0
}
1287
1288
namespace {
1289
cmNinjaBuild GetScanBuildStatement(std::string const& ruleName,
1290
                                   std::string const& ppFileName,
1291
                                   bool compilePP, bool compilePPWithDefines,
1292
                                   bool compilationPreprocesses,
1293
                                   cmNinjaBuild& objBuild, cmNinjaVars& vars,
1294
                                   std::string const& objectFileName,
1295
                                   cmNinjaTargetGenerator* tg)
1296
0
{
1297
0
  cmNinjaBuild scanBuild(ruleName);
1298
1299
0
  if (compilePP) {
1300
    // Move compilation dependencies to the scan/preprocessing build statement.
1301
0
    std::swap(scanBuild.ExplicitDeps, objBuild.ExplicitDeps);
1302
0
    std::swap(scanBuild.ImplicitDeps, objBuild.ImplicitDeps);
1303
0
    std::swap(scanBuild.OrderOnlyDeps, objBuild.OrderOnlyDeps);
1304
0
    std::swap(scanBuild.Variables["IN_ABS"], vars["IN_ABS"]);
1305
1306
    // The actual compilation will now use the preprocessed source.
1307
0
    objBuild.ExplicitDeps.push_back(ppFileName);
1308
0
  } else {
1309
    // Copy compilation dependencies to the scan/preprocessing build statement.
1310
0
    scanBuild.ExplicitDeps = objBuild.ExplicitDeps;
1311
0
    scanBuild.ImplicitDeps = objBuild.ImplicitDeps;
1312
0
    scanBuild.OrderOnlyDeps = objBuild.OrderOnlyDeps;
1313
0
    scanBuild.Variables["IN_ABS"] = vars["IN_ABS"];
1314
0
  }
1315
1316
  // Scanning and compilation generally use the same flags.
1317
0
  scanBuild.Variables["FLAGS"] = vars["FLAGS"];
1318
1319
0
  if (compilePP && !compilePPWithDefines) {
1320
    // Move preprocessor definitions to the scan/preprocessor build statement.
1321
0
    std::swap(scanBuild.Variables["DEFINES"], vars["DEFINES"]);
1322
0
  } else {
1323
    // Copy preprocessor definitions to the scan/preprocessor build statement.
1324
0
    scanBuild.Variables["DEFINES"] = vars["DEFINES"];
1325
0
  }
1326
1327
  // Copy include directories to the preprocessor build statement.  The
1328
  // Fortran compilation build statement still needs them for the INCLUDE
1329
  // directive.
1330
0
  scanBuild.Variables["INCLUDES"] = vars["INCLUDES"];
1331
1332
  // Tell dependency scanner the object file that will result from
1333
  // compiling the source.
1334
0
  scanBuild.Variables["OBJ_FILE"] = objectFileName;
1335
1336
  // Tell dependency scanner where to store dyndep intermediate results.
1337
0
  std::string ddiFileName = cmStrCat(objectFileName, ".ddi");
1338
0
  scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFileName;
1339
0
  scanBuild.RspFile = cmStrCat(ddiFileName, ".rsp");
1340
1341
  // Outputs of the scan/preprocessor build statement.
1342
0
  if (compilePP) {
1343
0
    scanBuild.Outputs.push_back(ppFileName);
1344
0
    scanBuild.ImplicitOuts.push_back(ddiFileName);
1345
0
  } else {
1346
0
    scanBuild.Outputs.push_back(ddiFileName);
1347
0
    scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] = ppFileName;
1348
0
    if (!compilationPreprocesses) {
1349
      // Compilation does not preprocess and we are not compiling an
1350
      // already-preprocessed source.  Make compilation depend on the scan
1351
      // results to honor implicit dependencies discovered during scanning
1352
      // (such as Fortran INCLUDE directives).
1353
0
      objBuild.ImplicitDeps.emplace_back(ddiFileName);
1354
0
    }
1355
0
  }
1356
1357
  // Scanning always provides a depfile for preprocessor dependencies. This
1358
  // variable is unused in `msvc`-deptype scanners.
1359
0
  tg->AddDepfileBinding(scanBuild.Variables,
1360
0
                        cmStrCat(scanBuild.Outputs.front(), ".d"));
1361
0
  if (compilePP) {
1362
    // The actual compilation does not need a depfile because it
1363
    // depends on the already-preprocessed source.
1364
0
    tg->RemoveDepfileBinding(vars);
1365
0
  }
1366
1367
0
  return scanBuild;
1368
0
}
1369
}
1370
1371
void cmNinjaTargetGenerator::WriteObjectBuildStatement(
1372
  cmSourceFile const* source, std::string const& config,
1373
  std::string const& fileConfig, bool firstForConfig)
1374
0
{
1375
0
  std::string const language = source->GetLanguage();
1376
0
  std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source);
1377
0
  std::string const targetSupportDir =
1378
0
    this->ConvertToNinjaPath(this->GeneratorTarget->GetCMFSupportDirectory());
1379
0
  std::string const objectDir =
1380
0
    this->ConvertToNinjaPath(this->GetObjectFileDir(config));
1381
0
  std::string const objectFileName =
1382
0
    this->ConvertToNinjaPath(this->GetObjectFilePath(source, config));
1383
0
  std::string const objectFileDir =
1384
0
    cmSystemTools::GetFilenamePath(objectFileName);
1385
1386
0
  std::string cmakeVarLang = cmStrCat("CMAKE_", language);
1387
1388
  // build response file name
1389
0
  std::string cmakeLinkVar = cmStrCat(cmakeVarLang, "_RESPONSE_FILE_FLAG");
1390
1391
0
  cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
1392
1393
0
  bool const lang_supports_response =
1394
0
    !(language == "RC" || (language == "CUDA" && !flag));
1395
0
  int const commandLineLengthLimit =
1396
0
    ((lang_supports_response && this->ForceResponseFile())) ? -1 : 0;
1397
0
  cmValue pchExtension =
1398
0
    this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
1399
0
  bool const isPch = cmHasSuffix(objectFileName, pchExtension);
1400
0
  bool const needDyndep = !isPch &&
1401
0
    this->GeneratorTarget->NeedDyndepForSource(language, config, source);
1402
1403
0
  WithScanning withScanning =
1404
0
    needDyndep ? WithScanning::Yes : WithScanning::No;
1405
0
  cmNinjaBuild objBuild(
1406
0
    this->LanguageCompilerRule(language, config, withScanning));
1407
0
  cmNinjaVars& vars = objBuild.Variables;
1408
0
  vars["FLAGS"] =
1409
0
    this->ComputeFlagsForObject(source, language, config, objectFileName);
1410
0
  vars["DEFINES"] = this->ComputeDefines(source, language, config);
1411
0
  vars["INCLUDES"] = this->ComputeIncludes(source, language, config);
1412
0
  vars["CONFIG"] = config;
1413
0
  if (this->GetGeneratorTarget()->GetUseShortObjectNames()) {
1414
0
    vars.emplace(
1415
0
      "description",
1416
0
      cmStrCat("Compiling object ", objectFileName, " from source ",
1417
0
               this->GetLocalGenerator()->GetRelativeSourceFileName(*source)));
1418
0
  }
1419
1420
0
  auto compilerLauncher = this->GetCompilerLauncher(language, config);
1421
1422
0
  cmValue const srcSkipCodeCheckVal = source->GetProperty("SKIP_LINTING");
1423
0
  bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet()
1424
0
    ? srcSkipCodeCheckVal.IsOn()
1425
0
    : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING");
1426
1427
0
  if (!skipCodeCheck) {
1428
0
    auto const cmakeCmd = this->GetLocalGenerator()->ConvertToOutputFormat(
1429
0
      cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
1430
0
    vars["CODE_CHECK"] =
1431
0
      this->GenerateCodeCheckRules(*source, compilerLauncher, cmakeCmd, config,
1432
0
                                   [this](std::string const& path) {
1433
0
                                     return this->ConvertToNinjaPath(path);
1434
0
                                   });
1435
0
  }
1436
1437
  // If compiler launcher was specified and not consumed above, it
1438
  // goes to the beginning of the command line.
1439
0
  if (!compilerLauncher.empty()) {
1440
0
    cmList args{ compilerLauncher, cmList::EmptyElements::Yes };
1441
0
    if (!args.empty()) {
1442
0
      args[0] = this->LocalGenerator->ConvertToOutputFormat(
1443
0
        args[0], cmOutputConverter::SHELL);
1444
0
      for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) {
1445
0
        i = this->LocalGenerator->EscapeForShell(i);
1446
0
      }
1447
0
      vars["LAUNCHER"] = args.join(" ") + " ";
1448
0
    }
1449
0
  }
1450
1451
0
  if (this->GetMakefile()->GetSafeDefinition(
1452
0
        cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) {
1453
0
    bool replaceExt = false;
1454
0
    if (!language.empty()) {
1455
0
      std::string repVar =
1456
0
        cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE");
1457
0
      replaceExt = this->Makefile->IsOn(repVar);
1458
0
    }
1459
0
    this->AddDepfileBinding(
1460
0
      vars,
1461
0
      replaceExt ? cmStrCat(objectFileDir, '/',
1462
0
                            cmSystemTools::GetFilenameWithoutLastExtension(
1463
0
                              objectFileName),
1464
0
                            ".d")
1465
0
                 : cmStrCat(objectFileName, ".d"));
1466
0
  }
1467
1468
0
  this->SetMsvcTargetPdbVariable(vars, config);
1469
1470
0
  if (firstForConfig) {
1471
0
    this->ExportObjectCompileCommand(
1472
0
      language, sourceFilePath, objectDir, targetSupportDir, objectFileName,
1473
0
      objectFileDir, vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"],
1474
0
      vars["TARGET_COMPILE_PDB"], vars["TARGET_PDB"], config, withScanning);
1475
0
  }
1476
1477
0
  objBuild.Outputs.push_back(objectFileName);
1478
0
  if (firstForConfig && !isPch) {
1479
    // Add this object to the list of object files.
1480
0
    this->Configs[config].Objects.push_back(objectFileName);
1481
0
  }
1482
1483
0
  objBuild.ExplicitDeps.push_back(sourceFilePath);
1484
1485
  // Add precompile headers dependencies
1486
0
  std::vector<std::string> depList;
1487
1488
0
  std::vector<std::string> pchArchs =
1489
0
    this->GeneratorTarget->GetPchArchs(config, language);
1490
1491
0
  std::unordered_set<std::string> pchSources;
1492
0
  for (std::string const& arch : pchArchs) {
1493
0
    std::string const pchSource =
1494
0
      this->GeneratorTarget->GetPchSource(config, language, arch);
1495
1496
0
    if (!pchSource.empty()) {
1497
0
      pchSources.insert(pchSource);
1498
0
    }
1499
0
  }
1500
1501
0
  if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
1502
0
    for (std::string const& arch : pchArchs) {
1503
0
      depList.push_back(
1504
0
        this->GeneratorTarget->GetPchHeader(config, language, arch));
1505
0
      if (pchSources.find(source->GetFullPath()) == pchSources.end()) {
1506
0
        depList.push_back(
1507
0
          this->GeneratorTarget->GetPchFile(config, language, arch));
1508
0
      }
1509
0
    }
1510
0
  }
1511
1512
0
  if (cmValue objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
1513
0
    cmList objDepList{ *objectDeps };
1514
0
    std::copy(objDepList.begin(), objDepList.end(),
1515
0
              std::back_inserter(depList));
1516
0
  }
1517
1518
0
  if (!depList.empty()) {
1519
0
    for (std::string& odi : depList) {
1520
0
      if (cmSystemTools::FileIsFullPath(odi)) {
1521
0
        odi = cmSystemTools::CollapseFullPath(odi);
1522
0
      }
1523
0
    }
1524
0
    std::transform(depList.begin(), depList.end(),
1525
0
                   std::back_inserter(objBuild.ImplicitDeps),
1526
0
                   this->MapToNinjaPath());
1527
0
  }
1528
1529
0
  if (this->HasPrivateGeneratedSources) {
1530
0
    objBuild.OrderOnlyDeps.push_back(
1531
0
      this->OrderDependsTargetForTargetPrivate(config));
1532
0
  } else {
1533
0
    objBuild.OrderOnlyDeps.push_back(
1534
0
      this->OrderDependsTargetForTarget(config));
1535
0
  }
1536
1537
  // If the source file is GENERATED and does not have a custom command
1538
  // (either attached to this source file or another one), assume that one of
1539
  // the target dependencies, OBJECT_DEPENDS or header file custom commands
1540
  // will rebuild the file.
1541
0
  if (source->GetIsGenerated() &&
1542
0
      !source->GetPropertyAsBool("__CMAKE_GENERATED_BY_CMAKE") &&
1543
0
      !source->GetCustomCommand() &&
1544
0
      !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFilePath)) {
1545
0
    this->GetGlobalGenerator()->AddAssumedSourceDependencies(
1546
0
      sourceFilePath, objBuild.OrderOnlyDeps);
1547
0
  }
1548
1549
  // For some cases we scan to dynamically discover dependencies.
1550
0
  bool const compilationPreprocesses =
1551
0
    !this->NeedExplicitPreprocessing(language);
1552
1553
0
  std::string modmapFormat;
1554
0
  if (needDyndep) {
1555
0
    std::string const modmapFormatVar =
1556
0
      cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT");
1557
0
    modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar);
1558
0
  }
1559
1560
0
  if (needDyndep) {
1561
    // If source/target has preprocessing turned off, we still need to
1562
    // generate an explicit dependency step
1563
0
    auto const srcpp = source->GetSafeProperty("Fortran_PREPROCESS");
1564
0
    cmOutputConverter::FortranPreprocess preprocess =
1565
0
      cmOutputConverter::GetFortranPreprocess(srcpp);
1566
0
    if (preprocess == cmOutputConverter::FortranPreprocess::Unset) {
1567
0
      auto const& tgtpp =
1568
0
        this->GeneratorTarget->GetSafeProperty("Fortran_PREPROCESS");
1569
0
      preprocess = cmOutputConverter::GetFortranPreprocess(tgtpp);
1570
0
    }
1571
1572
0
    bool const compilePP = !compilationPreprocesses &&
1573
0
      (preprocess != cmOutputConverter::FortranPreprocess::NotNeeded);
1574
0
    bool const compilePPWithDefines =
1575
0
      compilePP && this->CompileWithDefines(language);
1576
1577
0
    std::string scanRuleName;
1578
0
    std::string ppFileName;
1579
0
    if (compilePP) {
1580
0
      scanRuleName = this->LanguagePreprocessAndScanRule(language, config);
1581
0
      ppFileName = this->ConvertToNinjaPath(
1582
0
        this->GetPreprocessedFilePath(source, config));
1583
0
    } else {
1584
0
      scanRuleName = this->LanguageScanRule(language, config);
1585
0
      ppFileName = cmStrCat(objectFileName, ".ddi.i");
1586
0
    }
1587
1588
0
    cmNinjaBuild ppBuild = GetScanBuildStatement(
1589
0
      scanRuleName, ppFileName, compilePP, compilePPWithDefines,
1590
0
      compilationPreprocesses, objBuild, vars, objectFileName, this);
1591
1592
0
    if (compilePP) {
1593
      // In case compilation requires flags that are incompatible with
1594
      // preprocessing, include them here.
1595
0
      std::string const& postFlag = this->Makefile->GetSafeDefinition(
1596
0
        cmStrCat("CMAKE_", language, "_POSTPROCESS_FLAG"));
1597
0
      this->LocalGenerator->AppendFlags(vars["FLAGS"], postFlag);
1598
1599
      // Prepend source file's original directory as an include directory
1600
      // so e.g. Fortran INCLUDE statements can look for files in it.
1601
0
      std::vector<std::string> sourceDirectory;
1602
0
      sourceDirectory.push_back(
1603
0
        cmSystemTools::GetParentDirectory(source->GetFullPath()));
1604
1605
0
      std::string sourceDirectoryFlag = this->LocalGenerator->GetIncludeFlags(
1606
0
        sourceDirectory, this->GeneratorTarget, language, config, false);
1607
1608
0
      vars["INCLUDES"] = cmStrCat(sourceDirectoryFlag, ' ', vars["INCLUDES"]);
1609
0
    }
1610
1611
0
    ScanningFiles scanningFiles;
1612
1613
0
    if (firstForConfig) {
1614
0
      scanningFiles.ScanningOutput = cmStrCat(objectFileName, ".ddi");
1615
0
    }
1616
1617
0
    this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
1618
0
                               source, ppBuild.Variables);
1619
1620
0
    this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1621
0
                                           ppBuild, commandLineLengthLimit);
1622
1623
0
    std::string const dyndep = this->GetDyndepFilePath(language, config);
1624
0
    objBuild.OrderOnlyDeps.push_back(dyndep);
1625
0
    vars["dyndep"] = dyndep;
1626
1627
0
    if (!modmapFormat.empty()) {
1628
      // XXX(modmap): If changing this path construction, change
1629
      // `cmGlobalNinjaGenerator::WriteDyndep` and
1630
      // `cmNinjaTargetGenerator::ExportObjectCompileCommand` to expect the
1631
      // corresponding file path.
1632
0
      std::string ddModmapFile = cmStrCat(objectFileName, ".modmap");
1633
0
      vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
1634
0
      objBuild.ImplicitDeps.push_back(ddModmapFile);
1635
0
      scanningFiles.ModuleMapFile = std::move(ddModmapFile);
1636
0
    }
1637
1638
0
    if (!scanningFiles.IsEmpty()) {
1639
0
      this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles);
1640
0
    }
1641
0
  }
1642
1643
0
  this->EnsureParentDirectoryExists(objectFileName);
1644
1645
0
  vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1646
0
    objectDir, cmOutputConverter::SHELL);
1647
0
  vars["TARGET_SUPPORT_DIR"] =
1648
0
    this->GetLocalGenerator()->ConvertToOutputFormat(targetSupportDir,
1649
0
                                                     cmOutputConverter::SHELL);
1650
0
  vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1651
0
    objectFileDir, cmOutputConverter::SHELL);
1652
1653
0
  this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
1654
0
                             source, vars);
1655
1656
0
  if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
1657
0
    auto pchIt = pchSources.find(source->GetFullPath());
1658
0
    if (pchIt != pchSources.end()) {
1659
0
      this->addPoolNinjaVariable("JOB_POOL_PRECOMPILE_HEADER",
1660
0
                                 this->GetGeneratorTarget(), nullptr, vars);
1661
0
    }
1662
0
  }
1663
1664
0
  objBuild.RspFile = cmStrCat(objectFileName, ".rsp");
1665
1666
0
  if (language == "ISPC") {
1667
0
    std::string const& objectName =
1668
0
      this->GeneratorTarget->GetObjectName(source);
1669
0
    std::string ispcSource =
1670
0
      cmSystemTools::GetFilenameWithoutLastExtension(objectName);
1671
0
    ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource);
1672
1673
0
    cmValue ispcSuffixProp =
1674
0
      this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX");
1675
0
    assert(ispcSuffixProp);
1676
1677
0
    std::string ispcHeaderDirectory =
1678
0
      this->GeneratorTarget->GetObjectDirectory(config);
1679
0
    if (cmValue prop =
1680
0
          this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) {
1681
0
      ispcHeaderDirectory =
1682
0
        cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop);
1683
0
    }
1684
1685
0
    std::string ispcHeader =
1686
0
      cmStrCat(ispcHeaderDirectory, '/', ispcSource, *ispcSuffixProp);
1687
0
    ispcHeader = this->ConvertToNinjaPath(ispcHeader);
1688
1689
    // Make sure ninja knows what command generates the header
1690
0
    objBuild.ImplicitOuts.push_back(ispcHeader);
1691
1692
    // Make sure ninja knows how to clean the generated header
1693
0
    this->GetGlobalGenerator()->AddAdditionalCleanFile(ispcHeader, config);
1694
1695
0
    auto ispcSuffixes =
1696
0
      detail::ComputeISPCObjectSuffixes(this->GeneratorTarget);
1697
0
    if (ispcSuffixes.size() > 1) {
1698
0
      std::string rootObjectDir =
1699
0
        this->GeneratorTarget->GetObjectDirectory(config);
1700
0
      auto ispcSideEffectObjects = detail::ComputeISPCExtraObjects(
1701
0
        objectName, rootObjectDir, ispcSuffixes);
1702
1703
0
      for (auto sideEffect : ispcSideEffectObjects) {
1704
0
        sideEffect = this->ConvertToNinjaPath(sideEffect);
1705
0
        objBuild.ImplicitOuts.emplace_back(sideEffect);
1706
0
        this->GetGlobalGenerator()->AddAdditionalCleanFile(sideEffect, config);
1707
0
      }
1708
0
    }
1709
1710
0
    vars["ISPC_HEADER_FILE"] =
1711
0
      this->GetLocalGenerator()->ConvertToOutputFormat(
1712
0
        ispcHeader, cmOutputConverter::SHELL);
1713
0
  } else {
1714
0
    auto headers = this->GeneratorTarget->GetGeneratedISPCHeaders(config);
1715
0
    if (!headers.empty()) {
1716
0
      std::transform(headers.begin(), headers.end(), headers.begin(),
1717
0
                     this->MapToNinjaPath());
1718
0
      objBuild.OrderOnlyDeps.insert(objBuild.OrderOnlyDeps.end(),
1719
0
                                    headers.begin(), headers.end());
1720
0
    }
1721
0
  }
1722
1723
0
  if (language == "Swift") {
1724
0
    this->EmitSwiftDependencyInfo(source, config);
1725
0
  } else {
1726
0
    this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1727
0
                                           objBuild, commandLineLengthLimit);
1728
0
  }
1729
1730
0
  if (cmValue objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) {
1731
0
    std::string evaluatedObjectOutputs = cmGeneratorExpression::Evaluate(
1732
0
      *objectOutputs, this->LocalGenerator, config);
1733
1734
0
    if (!evaluatedObjectOutputs.empty()) {
1735
0
      cmNinjaBuild build("phony");
1736
0
      build.Comment = "Additional output files.";
1737
0
      build.Outputs = cmList{ evaluatedObjectOutputs }.data();
1738
0
      std::transform(build.Outputs.begin(), build.Outputs.end(),
1739
0
                     build.Outputs.begin(), this->MapToNinjaPath());
1740
0
      build.ExplicitDeps = objBuild.Outputs;
1741
0
      this->GetGlobalGenerator()->WriteBuild(
1742
0
        this->GetImplFileStream(fileConfig), build);
1743
0
    }
1744
0
  }
1745
0
}
1746
1747
void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement(
1748
  cmSourceFile const* source, std::string const& config,
1749
  std::string const& fileConfig, bool firstForConfig)
1750
0
{
1751
0
  std::string const language = source->GetLanguage();
1752
0
  if (language != "CXX"_s) {
1753
0
    this->GetMakefile()->IssueMessage(
1754
0
      MessageType::FATAL_ERROR,
1755
0
      cmStrCat("Source file '", source->GetFullPath(), "' of target '",
1756
0
               this->GetTargetName(), "' is a '", language,
1757
0
               "' source but must be 'CXX' in order to have a BMI build "
1758
0
               "statement generated."));
1759
0
    return;
1760
0
  }
1761
1762
0
  std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source);
1763
0
  std::string const targetSupportDir =
1764
0
    this->ConvertToNinjaPath(this->GeneratorTarget->GetCMFSupportDirectory());
1765
0
  std::string const bmiDir = this->ConvertToNinjaPath(
1766
0
    cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
1767
0
             this->GetGlobalGenerator()->ConfigDirectory(config)));
1768
0
  std::string const bmiFileName =
1769
0
    this->ConvertToNinjaPath(this->GetBmiFilePath(source, config));
1770
0
  std::string const bmiFileDir = cmSystemTools::GetFilenamePath(bmiFileName);
1771
1772
0
  int const commandLineLengthLimit = this->ForceResponseFile() ? -1 : 0;
1773
1774
0
  cmNinjaBuild bmiBuild(
1775
0
    this->LanguageCompilerRule(language, config, WithScanning::Yes));
1776
0
  cmNinjaVars& vars = bmiBuild.Variables;
1777
0
  vars["FLAGS"] =
1778
0
    this->ComputeFlagsForObject(source, language, config, bmiFileName);
1779
0
  vars["DEFINES"] = this->ComputeDefines(source, language, config);
1780
0
  vars["INCLUDES"] = this->ComputeIncludes(source, language, config);
1781
0
  vars["CONFIG"] = config;
1782
1783
0
  if (this->GetMakefile()->GetSafeDefinition(
1784
0
        cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) {
1785
0
    bool replaceExt = false;
1786
0
    if (!language.empty()) {
1787
0
      std::string repVar =
1788
0
        cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE");
1789
0
      replaceExt = this->Makefile->IsOn(repVar);
1790
0
    }
1791
0
    this->AddDepfileBinding(
1792
0
      vars,
1793
0
      replaceExt
1794
0
        ? cmStrCat(bmiFileDir, '/',
1795
0
                   cmSystemTools::GetFilenameWithoutLastExtension(bmiFileName),
1796
0
                   ".d")
1797
0
        : cmStrCat(bmiFileName, ".d"));
1798
0
  }
1799
1800
0
  std::string d =
1801
0
    this->GeneratorTarget->GetClangTidyExportFixesDirectory(language);
1802
0
  if (!d.empty()) {
1803
0
    this->GlobalCommonGenerator->AddClangTidyExportFixesDir(d);
1804
0
    std::string fixesFile =
1805
0
      this->GetClangTidyReplacementsFilePath(d, *source, config);
1806
0
    this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile);
1807
0
    cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(fixesFile));
1808
0
    fixesFile = this->ConvertToNinjaPath(fixesFile);
1809
0
    vars["CLANG_TIDY_EXPORT_FIXES"] = fixesFile;
1810
0
  }
1811
1812
0
  this->SetMsvcTargetPdbVariable(vars, config);
1813
1814
0
  if (firstForConfig) {
1815
0
    this->ExportObjectCompileCommand(
1816
0
      language, sourceFilePath, bmiDir, targetSupportDir, bmiFileName,
1817
0
      bmiFileDir, vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"],
1818
0
      vars["TARGET_COMPILE_PDB"], vars["TARGET_PDB"], config,
1819
0
      WithScanning::Yes);
1820
0
  }
1821
1822
0
  bmiBuild.Outputs.push_back(bmiFileName);
1823
0
  bmiBuild.ExplicitDeps.push_back(sourceFilePath);
1824
1825
0
  std::vector<std::string> depList;
1826
1827
0
  bmiBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config));
1828
1829
  // For some cases we scan to dynamically discover dependencies.
1830
0
  std::string modmapFormat;
1831
0
  if (true) {
1832
0
    std::string const modmapFormatVar =
1833
0
      cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT");
1834
0
    modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar);
1835
0
  }
1836
1837
0
  {
1838
0
    bool const compilePPWithDefines = this->CompileWithDefines(language);
1839
1840
0
    std::string scanRuleName = this->LanguageScanRule(language, config);
1841
0
    std::string ppFileName = cmStrCat(bmiFileName, ".ddi.i");
1842
1843
0
    cmNinjaBuild ppBuild = GetScanBuildStatement(
1844
0
      scanRuleName, ppFileName, false, compilePPWithDefines, true, bmiBuild,
1845
0
      vars, bmiFileName, this);
1846
1847
0
    ScanningFiles scanningFiles;
1848
1849
0
    if (firstForConfig) {
1850
0
      scanningFiles.ScanningOutput = cmStrCat(bmiFileName, ".ddi");
1851
0
    }
1852
1853
0
    this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
1854
0
                               source, ppBuild.Variables);
1855
1856
0
    this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1857
0
                                           ppBuild, commandLineLengthLimit);
1858
1859
0
    std::string const dyndep = this->GetDyndepFilePath(language, config);
1860
0
    bmiBuild.OrderOnlyDeps.push_back(dyndep);
1861
0
    vars["dyndep"] = dyndep;
1862
1863
0
    if (!modmapFormat.empty()) {
1864
0
      std::string ddModmapFile = cmStrCat(bmiFileName, ".modmap");
1865
0
      vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
1866
0
      scanningFiles.ModuleMapFile = std::move(ddModmapFile);
1867
0
    }
1868
1869
0
    if (!scanningFiles.IsEmpty()) {
1870
0
      this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles);
1871
0
    }
1872
0
  }
1873
1874
0
  this->EnsureParentDirectoryExists(bmiFileName);
1875
1876
0
  vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1877
0
    bmiDir, cmOutputConverter::SHELL);
1878
0
  vars["TARGET_SUPPORT_DIR"] =
1879
0
    this->GetLocalGenerator()->ConvertToOutputFormat(targetSupportDir,
1880
0
                                                     cmOutputConverter::SHELL);
1881
0
  vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1882
0
    bmiFileDir, cmOutputConverter::SHELL);
1883
1884
0
  this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
1885
0
                             source, vars);
1886
1887
0
  bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp");
1888
1889
0
  this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
1890
0
                                         bmiBuild, commandLineLengthLimit);
1891
0
}
1892
1893
void cmNinjaTargetGenerator::WriteSwiftObjectBuildStatement(
1894
  std::vector<cmSourceFile const*> const& sources, std::string const& config,
1895
  std::string const& fileConfig, bool firstForConfig)
1896
0
{
1897
  // Swift sources are compiled as a module, not individually like with C/C++.
1898
  // Flags, header search paths, and definitions are passed to the entire
1899
  // module build, but we still need to emit compile-commands for each source
1900
  // file in order to support CMAKE_EXPORT_COMPILE_COMMANDS.
1901
  // In whole-module mode, with a single thread, the Swift compiler will
1902
  // only emit a single object file, but if more than one thread is specified,
1903
  // or building in other modes, the compiler will emit multiple object files.
1904
  // When building a single-output, we do not provide an output-file-map (OFM),
1905
  // and instead pass `-o` to tell the compiler where to write the object.
1906
  // When building multiple outputs, we provide an OFM to tell the compiler
1907
  // where to put each object.
1908
  //
1909
  //
1910
  // Per-Target (module):
1911
  //  - Flags
1912
  //  - Definitions
1913
  //  - Include paths
1914
  //  - (single-output) output object filename
1915
  //  - Swiftmodule
1916
  //
1917
  //  Per-File:
1918
  //  - compile-command
1919
  //  - (multi-output) OFM data
1920
  //  - (multi-output) output object filename
1921
  //
1922
  //  Note: Due to the differences in the build models, we are only able to
1923
  //  build the object build-graph if we know what mode the target is built in.
1924
  //  For that, we need the "NEW" behavior for CMP0157. Otherwise, we have to
1925
  //  fall back on the old "linker" build. Otherwise, this should be
1926
  //  indistinguishable from the old behavior.
1927
1928
0
  if (sources.empty()) {
1929
0
    return;
1930
0
  }
1931
1932
0
  cmSwiftCompileMode compileMode;
1933
0
  if (cm::optional<cmSwiftCompileMode> optionalCompileMode =
1934
0
        this->LocalGenerator->GetSwiftCompileMode(this->GeneratorTarget,
1935
0
                                                  config)) {
1936
0
    compileMode = *optionalCompileMode;
1937
0
  } else {
1938
    // CMP0157 is not NEW, bailing early!
1939
0
    return;
1940
0
  }
1941
1942
0
  std::string const language = "Swift";
1943
0
  std::string const objectDir = this->ConvertToNinjaPath(
1944
0
    cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
1945
0
             this->GetGlobalGenerator()->ConfigDirectory(config)));
1946
1947
0
  cmGeneratorTarget const& target = *this->GeneratorTarget;
1948
0
  cmNinjaBuild objBuild(
1949
0
    this->LanguageCompilerRule(language, config, WithScanning::No));
1950
0
  cmNinjaVars& vars = objBuild.Variables;
1951
1952
  // The swift toolchain leaves outputs untouched if there are no meaningful
1953
  // changes to input files (e.g. addition of a comment).
1954
0
  vars.emplace("restat", "1");
1955
1956
0
  std::string const moduleName = target.GetSwiftModuleName();
1957
0
  std::string const moduleFilepath =
1958
0
    this->ConvertToNinjaPath(target.GetSwiftModulePath(config));
1959
1960
0
  vars.emplace("description",
1961
0
               cmStrCat("Building Swift Module '", moduleName, "' with ",
1962
0
                        sources.size(),
1963
0
                        sources.size() == 1 ? " source" : " sources"));
1964
1965
0
  bool const isSingleOutput = [this, compileMode]() -> bool {
1966
0
    bool isMultiThread = false;
1967
0
    if (cmValue numThreadStr =
1968
0
          this->GetMakefile()->GetDefinition("CMAKE_Swift_NUM_THREADS")) {
1969
0
      unsigned long numThreads;
1970
0
      cmStrToULong(*numThreadStr, &numThreads);
1971
      // numThreads == 1 is multi-threaded according to swiftc
1972
0
      isMultiThread = numThreads > 0;
1973
0
    }
1974
0
    return !isMultiThread && compileMode == cmSwiftCompileMode::Wholemodule;
1975
0
  }();
1976
1977
  // Without `-emit-library` or `-emit-executable`, targets with a single
1978
  // source file parse as a Swift script instead of like normal source. For
1979
  // non-executable targets, append this to ensure that they are parsed like a
1980
  // normal source.
1981
0
  if (target.GetType() != cmStateEnums::EXECUTABLE) {
1982
0
    this->LocalGenerator->AppendFlags(vars["FLAGS"], "-parse-as-library");
1983
0
  }
1984
1985
0
  if (target.GetType() == cmStateEnums::STATIC_LIBRARY) {
1986
0
    this->LocalGenerator->AppendFlags(vars["FLAGS"], "-static");
1987
0
  }
1988
1989
  // Does this swift target emit a module file for importing into other
1990
  // targets?
1991
0
  auto isImportableTarget = [](cmGeneratorTarget const& tgt) -> bool {
1992
    // Everything except for executables that don't export anything should emit
1993
    // some way to import them.
1994
0
    if (tgt.GetType() == cmStateEnums::EXECUTABLE) {
1995
0
      return tgt.IsExecutableWithExports();
1996
0
    }
1997
0
    return true;
1998
0
  };
1999
2000
  // Swift modules only make sense to emit from things that can be imported.
2001
  // Executables that don't export symbols can't be imported, so don't try to
2002
  // emit a swiftmodule for them. It will break.
2003
0
  if (isImportableTarget(target)) {
2004
0
    std::string const emitModuleFlag = "-emit-module";
2005
0
    std::string const modulePathFlag = "-emit-module-path";
2006
0
    this->LocalGenerator->AppendFlags(
2007
0
      vars["FLAGS"],
2008
0
      { emitModuleFlag, modulePathFlag,
2009
0
        this->LocalGenerator->ConvertToOutputFormat(
2010
0
          moduleFilepath, cmOutputConverter::SHELL) });
2011
0
    objBuild.Outputs.push_back(moduleFilepath);
2012
0
  }
2013
0
  this->LocalGenerator->AppendFlags(vars["FLAGS"],
2014
0
                                    cmStrCat("-module-name ", moduleName));
2015
2016
0
  if (target.GetType() != cmStateEnums::EXECUTABLE) {
2017
0
    std::string const libraryLinkNameFlag = "-module-link-name";
2018
0
    std::string const libraryLinkName =
2019
0
      this->GetGeneratorTarget()->GetLibraryNames(config).Base;
2020
0
    this->LocalGenerator->AppendFlags(
2021
0
      vars["FLAGS"], cmStrCat(libraryLinkNameFlag, ' ', libraryLinkName));
2022
0
  }
2023
2024
0
  this->LocalGenerator->AppendFlags(vars["FLAGS"],
2025
0
                                    this->GetFlags(language, config));
2026
0
  vars["DEFINES"] = this->GetDefines(language, config);
2027
0
  vars["INCLUDES"] = this->GetIncludes(language, config);
2028
0
  vars["CONFIG"] = config;
2029
2030
  // target-level object filename
2031
0
  std::string const targetObjectFilename = this->ConvertToNinjaPath(cmStrCat(
2032
0
    objectDir, '/', moduleName,
2033
0
    this->GetGlobalGenerator()->GetLanguageOutputExtension(language)));
2034
0
  objBuild.RspFile = cmStrCat(targetObjectFilename, ".swift.rsp");
2035
2036
0
  if (isSingleOutput) {
2037
0
    this->LocalGenerator->AppendFlags(vars["FLAGS"],
2038
0
                                      cmStrCat("-o ", targetObjectFilename));
2039
0
    objBuild.Outputs.push_back(targetObjectFilename);
2040
0
    this->Configs[config].Objects.push_back(targetObjectFilename);
2041
0
  }
2042
2043
0
  for (cmSourceFile const* sf : sources) {
2044
    // Add dependency to object build on each source file
2045
0
    std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(sf);
2046
0
    objBuild.ExplicitDeps.push_back(sourceFilePath);
2047
2048
0
    if (!isSingleOutput) {
2049
      // Object outputs
2050
0
      std::string const objectFilepath =
2051
0
        this->ConvertToNinjaPath(this->GetObjectFilePath(sf, config));
2052
0
      this->EnsureParentDirectoryExists(objectFilepath);
2053
0
      objBuild.Outputs.push_back(objectFilepath);
2054
0
      this->Configs[config].Objects.push_back(objectFilepath);
2055
2056
      // Add OFM data
2057
0
      this->EmitSwiftDependencyInfo(sf, config);
2058
0
    }
2059
0
  }
2060
2061
0
  if (!isSingleOutput) {
2062
0
    this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]);
2063
0
  }
2064
2065
0
  if (firstForConfig) {
2066
0
    this->ExportSwiftObjectCompileCommand(
2067
0
      sources, targetObjectFilename, vars["FLAGS"], vars["DEFINES"],
2068
0
      vars["INCLUDES"], config, isSingleOutput);
2069
0
  }
2070
2071
0
  for (cmTargetDepend const& dep :
2072
0
       this->GetGlobalGenerator()->GetTargetDirectDepends(&target)) {
2073
0
    if (!dep->IsLanguageUsed("Swift", config)) {
2074
0
      continue;
2075
0
    }
2076
2077
    // If the dependency emits a swiftmodule, add a dependency edge on that
2078
    // swiftmodule to the ninja build graph.
2079
0
    if (isImportableTarget(*dep)) {
2080
0
      std::string const depModuleFilepath =
2081
0
        this->ConvertToNinjaPath(dep->GetSwiftModulePath(config));
2082
0
      objBuild.ImplicitDeps.push_back(depModuleFilepath);
2083
0
    }
2084
0
  }
2085
2086
0
  objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config));
2087
2088
  // Write object build
2089
0
  this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
2090
0
                                         objBuild,
2091
0
                                         this->ForceResponseFile() ? -1 : 0);
2092
0
}
2093
2094
void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
2095
                                                   std::string const& config)
2096
0
{
2097
0
  Json::Value tdi(Json::objectValue);
2098
0
  tdi["language"] = lang;
2099
0
  tdi["compiler-id"] = this->Makefile->GetSafeDefinition(
2100
0
    cmStrCat("CMAKE_", lang, "_COMPILER_ID"));
2101
0
  tdi["compiler-simulate-id"] = this->Makefile->GetSafeDefinition(
2102
0
    cmStrCat("CMAKE_", lang, "_SIMULATE_ID"));
2103
0
  tdi["compiler-frontend-variant"] = this->Makefile->GetSafeDefinition(
2104
0
    cmStrCat("CMAKE_", lang, "_COMPILER_FRONTEND_VARIANT"));
2105
2106
0
  std::string mod_dir;
2107
0
  if (lang == "Fortran") {
2108
0
    mod_dir = this->GeneratorTarget->GetFortranModuleDirectory(
2109
0
      this->Makefile->GetHomeOutputDirectory());
2110
0
  } else if (lang == "CXX") {
2111
0
    mod_dir = this->GetGlobalGenerator()->ExpandCFGIntDir(
2112
0
      cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory),
2113
0
      config);
2114
0
  }
2115
0
  if (mod_dir.empty()) {
2116
0
    mod_dir = this->Makefile->GetCurrentBinaryDirectory();
2117
0
  }
2118
0
  tdi["module-dir"] = mod_dir;
2119
2120
0
  if (lang == "Fortran") {
2121
0
    tdi["submodule-sep"] =
2122
0
      this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP");
2123
0
    tdi["submodule-ext"] =
2124
0
      this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT");
2125
0
  } else if (lang == "CXX") {
2126
    // No extra information necessary.
2127
0
  }
2128
2129
0
  tdi["dir-cur-bld"] = this->Makefile->GetCurrentBinaryDirectory();
2130
0
  tdi["dir-cur-src"] = this->Makefile->GetCurrentSourceDirectory();
2131
0
  tdi["dir-top-bld"] = this->Makefile->GetHomeOutputDirectory();
2132
0
  tdi["dir-top-src"] = this->Makefile->GetHomeDirectory();
2133
2134
0
  Json::Value& tdi_include_dirs = tdi["include-dirs"] = Json::arrayValue;
2135
0
  std::vector<std::string> includes;
2136
0
  this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
2137
0
                                              lang, config);
2138
0
  for (std::string const& i : includes) {
2139
    // Convert the include directories the same way we do for -I flags.
2140
    // See upstream ninja issue 1251.
2141
0
    tdi_include_dirs.append(this->ConvertToNinjaPath(i));
2142
0
  }
2143
2144
0
  Json::Value& tdi_linked_target_dirs = tdi["linked-target-dirs"] =
2145
0
    Json::arrayValue;
2146
0
  auto const linked_directories =
2147
0
    this->GetLinkedTargetDirectories(lang, config);
2148
0
  for (std::string const& l : linked_directories.Direct) {
2149
0
    tdi_linked_target_dirs.append(l);
2150
0
  }
2151
2152
0
  Json::Value& tdi_forward_modules_from_target_dirs =
2153
0
    tdi["forward-modules-from-target-dirs"] = Json::arrayValue;
2154
0
  for (std::string const& l : linked_directories.Forward) {
2155
0
    tdi_forward_modules_from_target_dirs.append(l);
2156
0
  }
2157
2158
0
  cmDyndepGeneratorCallbacks cb;
2159
0
  cb.ObjectFilePath = [this](cmSourceFile const* sf, std::string const& cnf) {
2160
0
    return this->GetObjectFilePath(sf, cnf);
2161
0
  };
2162
0
  cb.BmiFilePath = [this](cmSourceFile const* sf, std::string const& cnf) {
2163
0
    return this->GetBmiFilePath(sf, cnf);
2164
0
  };
2165
2166
0
#if !defined(CMAKE_BOOTSTRAP)
2167
0
  cmDyndepCollation::AddCollationInformation(tdi, this->GeneratorTarget,
2168
0
                                             config, cb);
2169
0
#endif
2170
2171
0
  std::string const tdin = this->GetTargetDependInfoPath(lang, config);
2172
0
  cmGeneratedFileStream tdif(tdin);
2173
0
  tdif << tdi;
2174
0
}
2175
2176
void cmNinjaTargetGenerator::EmitSwiftDependencyInfo(
2177
  cmSourceFile const* source, std::string const& config)
2178
0
{
2179
0
  std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source);
2180
0
  std::string const objectFilePath =
2181
0
    this->ConvertToNinjaPath(this->GetObjectFilePath(source, config));
2182
0
  std::string const swiftDepsPath = [source, objectFilePath]() -> std::string {
2183
0
    if (cmValue name = source->GetProperty("Swift_DEPENDENCIES_FILE")) {
2184
0
      return *name;
2185
0
    }
2186
0
    return cmStrCat(objectFilePath, ".swiftdeps");
2187
0
  }();
2188
0
  std::string const swiftDiaPath = [source, objectFilePath]() -> std::string {
2189
0
    if (cmValue name = source->GetProperty("Swift_DIAGNOSTICS_FILE")) {
2190
0
      return *name;
2191
0
    }
2192
0
    return cmStrCat(objectFilePath, ".dia");
2193
0
  }();
2194
0
  std::string const makeDepsPath = [this, source, config]() -> std::string {
2195
0
    cmLocalNinjaGenerator const* local = this->GetLocalGenerator();
2196
0
    std::string const objectFileName =
2197
0
      this->ConvertToNinjaPath(this->GetObjectFilePath(source, config));
2198
0
    std::string const objectFileDir =
2199
0
      cmSystemTools::GetFilenamePath(objectFileName);
2200
2201
0
    if (this->Makefile->IsOn("CMAKE_Swift_DEPFLE_EXTNSION_REPLACE")) {
2202
0
      std::string dependFileName = cmStrCat(
2203
0
        cmSystemTools::GetFilenameWithoutLastExtension(objectFileName), ".d");
2204
0
      return local->ConvertToOutputFormat(
2205
0
        cmStrCat(objectFileDir, '/', dependFileName),
2206
0
        cmOutputConverter::SHELL);
2207
0
    }
2208
0
    return local->ConvertToOutputFormat(cmStrCat(objectFileName, ".d"),
2209
0
                                        cmOutputConverter::SHELL);
2210
0
  }();
2211
2212
  // build the source file mapping
2213
  // https://github.com/apple/swift/blob/master/docs/Driver.md#output-file-maps
2214
0
  Json::Value entry = Json::Value(Json::objectValue);
2215
0
  entry["object"] = objectFilePath;
2216
0
  entry["dependencies"] = makeDepsPath;
2217
0
  entry["swift-dependencies"] = swiftDepsPath;
2218
0
  entry["diagnostics"] = swiftDiaPath;
2219
0
  this->Configs[config].SwiftOutputMap[sourceFilePath] = entry;
2220
0
}
2221
2222
void cmNinjaTargetGenerator::ExportObjectCompileCommand(
2223
  std::string const& language, std::string const& sourceFileName,
2224
  std::string const& objectDir, std::string const& targetSupportDir,
2225
  std::string const& objectFileName, std::string const& objectFileDir,
2226
  std::string const& flags, std::string const& defines,
2227
  std::string const& includes, std::string const& targetCompilePdb,
2228
  std::string const& targetPdb, std::string const& outputConfig,
2229
  WithScanning withScanning)
2230
0
{
2231
0
  if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) {
2232
0
    return;
2233
0
  }
2234
2235
0
  cmRulePlaceholderExpander::RuleVariables compileObjectVars;
2236
0
  compileObjectVars.Language = language.c_str();
2237
2238
0
  std::string escapedSourceFileName = sourceFileName;
2239
2240
0
  if (!cmSystemTools::FileIsFullPath(sourceFileName)) {
2241
0
    escapedSourceFileName =
2242
0
      cmSystemTools::CollapseFullPath(escapedSourceFileName,
2243
0
                                      this->GetGlobalGenerator()
2244
0
                                        ->GetCMakeInstance()
2245
0
                                        ->GetHomeOutputDirectory());
2246
0
  }
2247
2248
0
  escapedSourceFileName = this->LocalGenerator->ConvertToOutputFormat(
2249
0
    escapedSourceFileName, cmOutputConverter::SHELL);
2250
2251
0
  std::string fullFlags = flags;
2252
0
  if (withScanning == WithScanning::Yes) {
2253
0
    std::string const modmapFormatVar =
2254
0
      cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT");
2255
0
    std::string const modmapFormat =
2256
0
      this->Makefile->GetSafeDefinition(modmapFormatVar);
2257
0
    if (!modmapFormat.empty()) {
2258
0
      std::string modmapFlags = this->GetMakefile()->GetRequiredDefinition(
2259
0
        cmStrCat("CMAKE_", language, "_MODULE_MAP_FLAG"));
2260
      // XXX(modmap): If changing this path construction, change
2261
      // `cmGlobalNinjaGenerator::WriteDyndep` and
2262
      // `cmNinjaTargetGenerator::WriteObjectBuildStatement` to expect the
2263
      // corresponding file path.
2264
0
      cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>",
2265
0
                                   cmStrCat(objectFileName, ".modmap"));
2266
0
      fullFlags += cmStrCat(' ', modmapFlags);
2267
0
    }
2268
0
  }
2269
2270
0
  compileObjectVars.Source = escapedSourceFileName.c_str();
2271
0
  std::string escapedObjectFileName =
2272
0
    this->LocalGenerator->ConvertToOutputFormat(objectFileName,
2273
0
                                                cmOutputConverter::SHELL);
2274
0
  compileObjectVars.Object = escapedObjectFileName.c_str();
2275
0
  compileObjectVars.ObjectDir = objectDir.c_str();
2276
0
  compileObjectVars.TargetSupportDir = targetSupportDir.c_str();
2277
0
  compileObjectVars.ObjectFileDir = objectFileDir.c_str();
2278
0
  compileObjectVars.Flags = fullFlags.c_str();
2279
0
  compileObjectVars.Defines = defines.c_str();
2280
0
  compileObjectVars.Includes = includes.c_str();
2281
0
  compileObjectVars.TargetCompilePDB = targetCompilePdb.c_str();
2282
0
  compileObjectVars.TargetPDB = targetPdb.c_str();
2283
2284
  // Rule for compiling object file.
2285
0
  std::string cudaCompileMode;
2286
0
  if (language == "CUDA") {
2287
0
    if (this->GeneratorTarget->GetPropertyAsBool(
2288
0
          "CUDA_SEPARABLE_COMPILATION")) {
2289
0
      std::string const& rdcFlag =
2290
0
        this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG");
2291
0
      cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, ' ');
2292
0
    }
2293
0
    static std::array<cm::string_view, 4> const compileModes{
2294
0
      { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s }
2295
0
    };
2296
0
    bool useNormalCompileMode = true;
2297
0
    for (cm::string_view mode : compileModes) {
2298
0
      auto propName = cmStrCat("CUDA_", mode, "_COMPILATION");
2299
0
      auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG");
2300
0
      if (this->GeneratorTarget->GetPropertyAsBool(propName)) {
2301
0
        std::string const& flag =
2302
0
          this->Makefile->GetRequiredDefinition(defName);
2303
0
        cudaCompileMode = cmStrCat(cudaCompileMode, flag);
2304
0
        useNormalCompileMode = false;
2305
0
        break;
2306
0
      }
2307
0
    }
2308
0
    if (useNormalCompileMode) {
2309
0
      std::string const& wholeFlag =
2310
0
        this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG");
2311
0
      cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag);
2312
0
    }
2313
0
    compileObjectVars.CudaCompileMode = cudaCompileMode.c_str();
2314
0
  }
2315
2316
0
  std::string const cmdVar = cmStrCat("CMAKE_", language, "_COMPILE_OBJECT");
2317
0
  std::string const& compileCmd =
2318
0
    this->Makefile->GetRequiredDefinition(cmdVar);
2319
0
  cmList compileCmds(compileCmd);
2320
2321
0
  auto rulePlaceholderExpander =
2322
0
    this->GetLocalGenerator()->CreateRulePlaceholderExpander();
2323
2324
0
  for (auto& i : compileCmds) {
2325
    // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS
2326
0
    rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), i,
2327
0
                                                 compileObjectVars);
2328
0
  }
2329
2330
0
  std::string cmdLine = this->GetLocalGenerator()->BuildCommandLine(
2331
0
    compileCmds, outputConfig, outputConfig);
2332
2333
0
  this->GetGlobalGenerator()->AddCXXCompileCommand(cmdLine, sourceFileName,
2334
0
                                                   objectFileName);
2335
0
}
2336
2337
void cmNinjaTargetGenerator::ExportSwiftObjectCompileCommand(
2338
  std::vector<cmSourceFile const*> const& moduleSourceFiles,
2339
  std::string const& moduleObjectFilename, std::string const& flags,
2340
  std::string const& defines, std::string const& includes,
2341
  std::string const& outputConfig, bool singleOutput)
2342
0
{
2343
0
  if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) {
2344
0
    return;
2345
0
  }
2346
2347
0
  auto escapeSourceFileName = [this](std::string srcFilename) -> std::string {
2348
0
    if (!cmSystemTools::FileIsFullPath(srcFilename)) {
2349
0
      srcFilename =
2350
0
        cmSystemTools::CollapseFullPath(srcFilename,
2351
0
                                        this->GetGlobalGenerator()
2352
0
                                          ->GetCMakeInstance()
2353
0
                                          ->GetHomeOutputDirectory());
2354
0
    }
2355
2356
0
    return this->LocalGenerator->ConvertToOutputFormat(
2357
0
      srcFilename, cmOutputConverter::SHELL);
2358
0
  };
2359
0
  auto escapedModuleObjectFilename =
2360
0
    this->ConvertToNinjaPath(moduleObjectFilename);
2361
2362
0
  cmRulePlaceholderExpander::RuleVariables compileObjectVars;
2363
0
  compileObjectVars.Language = "Swift";
2364
0
  compileObjectVars.Flags = flags.c_str();
2365
0
  compileObjectVars.Defines = defines.c_str();
2366
0
  compileObjectVars.Includes = includes.c_str();
2367
2368
  // Build up the list of source files in the module
2369
0
  std::vector<std::string> filenames;
2370
0
  filenames.reserve(moduleSourceFiles.size());
2371
0
  for (cmSourceFile const* sf : moduleSourceFiles) {
2372
0
    filenames.emplace_back(
2373
0
      escapeSourceFileName(this->GetCompiledSourceNinjaPath(sf)));
2374
0
  }
2375
  // Note that `escapedSourceFilenames` must remain alive until the
2376
  // compileObjectVars is consumed or Source will be a dangling pointer.
2377
0
  std::string const escapedSourceFilenames = cmJoin(filenames, " ");
2378
0
  compileObjectVars.Source = escapedSourceFilenames.c_str();
2379
2380
0
  std::string const& compileCommand =
2381
0
    this->Makefile->GetRequiredDefinition("CMAKE_Swift_COMPILE_OBJECT");
2382
0
  cmList compileCmds(compileCommand);
2383
2384
0
  auto rulePlaceholderExpander =
2385
0
    this->GetLocalGenerator()->CreateRulePlaceholderExpander();
2386
2387
0
  for (cmSourceFile const* sf : moduleSourceFiles) {
2388
0
    std::string const sourceFilename = this->GetCompiledSourceNinjaPath(sf);
2389
0
    std::string objectFilename = escapedModuleObjectFilename;
2390
2391
0
    if (!singleOutput) {
2392
      // If it's not single-output, each source file gets a separate object
2393
0
      objectFilename =
2394
0
        this->ConvertToNinjaPath(this->GetObjectFilePath(sf, outputConfig));
2395
0
    }
2396
0
    compileObjectVars.Objects = objectFilename.c_str();
2397
2398
0
    for (std::string& cmd : compileCmds) {
2399
0
      rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
2400
0
                                                   cmd, compileObjectVars);
2401
0
    }
2402
2403
0
    std::string commandLine = this->GetLocalGenerator()->BuildCommandLine(
2404
0
      compileCmds, outputConfig, outputConfig);
2405
2406
0
    this->GetGlobalGenerator()->AddCXXCompileCommand(
2407
0
      commandLine, sourceFilename, objectFilename);
2408
0
  }
2409
0
}
2410
2411
void cmNinjaTargetGenerator::AdditionalCleanFiles(std::string const& config)
2412
0
{
2413
0
  if (cmValue prop_value =
2414
0
        this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
2415
0
    cmLocalNinjaGenerator* lg = this->LocalGenerator;
2416
0
    cmList cleanFiles(cmGeneratorExpression::Evaluate(*prop_value, lg, config,
2417
0
                                                      this->GeneratorTarget));
2418
0
    std::string const& binaryDir = lg->GetCurrentBinaryDirectory();
2419
0
    cmGlobalNinjaGenerator* gg = lg->GetGlobalNinjaGenerator();
2420
0
    for (auto const& cleanFile : cleanFiles) {
2421
      // Support relative paths
2422
0
      gg->AddAdditionalCleanFile(
2423
0
        cmSystemTools::CollapseFullPath(cleanFile, binaryDir), config);
2424
0
    }
2425
0
  }
2426
0
}
2427
2428
cmNinjaDeps cmNinjaTargetGenerator::GetObjects(std::string const& config) const
2429
0
{
2430
0
  auto const it = this->Configs.find(config);
2431
0
  if (it != this->Configs.end()) {
2432
0
    return it->second.Objects;
2433
0
  }
2434
0
  return {};
2435
0
}
2436
2437
void cmNinjaTargetGenerator::EnsureDirectoryExists(
2438
  std::string const& path) const
2439
0
{
2440
0
  if (cmSystemTools::FileIsFullPath(path)) {
2441
0
    cmSystemTools::MakeDirectory(path);
2442
0
  } else {
2443
0
    cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator();
2444
0
    std::string fullPath = gg->GetCMakeInstance()->GetHomeOutputDirectory();
2445
    // Also ensures there is a trailing slash.
2446
0
    gg->StripNinjaOutputPathPrefixAsSuffix(fullPath);
2447
0
    fullPath += path;
2448
0
    cmSystemTools::MakeDirectory(fullPath);
2449
0
  }
2450
0
}
2451
2452
void cmNinjaTargetGenerator::EnsureParentDirectoryExists(
2453
  std::string const& path) const
2454
0
{
2455
0
  this->EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path));
2456
0
}
2457
2458
void cmNinjaTargetGenerator::MacOSXContentGeneratorType::operator()(
2459
  cmSourceFile const& source, char const* pkgloc, std::string const& config)
2460
0
{
2461
  // Skip OS X content when not building a Framework or Bundle.
2462
0
  if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) {
2463
0
    return;
2464
0
  }
2465
2466
0
  std::string macdir =
2467
0
    this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc,
2468
0
                                                                    config);
2469
2470
  // Reject files that collide with files from the Ninja file's native config.
2471
0
  if (config != this->FileConfig) {
2472
0
    std::string nativeMacdir =
2473
0
      this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(
2474
0
        pkgloc, this->FileConfig);
2475
0
    if (macdir == nativeMacdir) {
2476
0
      return;
2477
0
    }
2478
0
  }
2479
2480
  // Get the input file location.
2481
0
  std::string input = source.GetFullPath();
2482
0
  input = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(input);
2483
2484
  // Get the output file location.
2485
0
  std::string output =
2486
0
    cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input));
2487
0
  output = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(output);
2488
2489
  // Write a build statement to copy the content into the bundle.
2490
0
  this->Generator->GetGlobalGenerator()->WriteMacOSXContentBuild(
2491
0
    input, output, this->FileConfig);
2492
2493
  // Add as a dependency to the target so that it gets called.
2494
0
  this->Generator->Configs[config].ExtraFiles.push_back(std::move(output));
2495
0
}
2496
2497
void cmNinjaTargetGenerator::AddDepfileBinding(cmNinjaVars& vars,
2498
                                               std::string depfile) const
2499
0
{
2500
0
  std::string depfileForShell =
2501
0
    this->GetLocalGenerator()->ConvertToOutputFormat(depfile,
2502
0
                                                     cmOutputConverter::SHELL);
2503
0
  if (depfile != depfileForShell) {
2504
0
    vars["depfile"] = std::move(depfile);
2505
0
  }
2506
0
  vars["DEP_FILE"] = std::move(depfileForShell);
2507
0
}
2508
2509
void cmNinjaTargetGenerator::RemoveDepfileBinding(cmNinjaVars& vars) const
2510
0
{
2511
0
  vars.erase("DEP_FILE");
2512
0
  vars.erase("depfile");
2513
0
}
2514
2515
void cmNinjaTargetGenerator::addPoolNinjaVariable(
2516
  std::string const& pool_property, cmGeneratorTarget* target,
2517
  cmSourceFile const* source, cmNinjaVars& vars)
2518
0
{
2519
  // First check the current source properties, then if not found, its target
2520
  // ones. Allows to override a target-wide compile pool with a source-specific
2521
  // one.
2522
0
  cmValue pool = {};
2523
0
  if (source) {
2524
0
    pool = source->GetProperty(pool_property);
2525
0
  }
2526
0
  if (!pool) {
2527
0
    pool = target->GetProperty(pool_property);
2528
0
  }
2529
0
  if (pool) {
2530
0
    vars["pool"] = *pool;
2531
0
  }
2532
0
}
2533
2534
bool cmNinjaTargetGenerator::ForceResponseFile()
2535
0
{
2536
0
  static std::string const forceRspFile = "CMAKE_NINJA_FORCE_RESPONSE_FILE";
2537
0
  return (this->GetMakefile()->IsDefinitionSet(forceRspFile) ||
2538
0
          cmSystemTools::HasEnv(forceRspFile));
2539
0
}