Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmFastbuildTargetGenerator.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 "cmFastbuildTargetGenerator.h"
4
5
#include <algorithm>
6
#include <cstddef>
7
#include <unordered_map>
8
#include <unordered_set>
9
10
#include <cm/memory>
11
#include <cm/optional>
12
13
#include "cmCryptoHash.h"
14
#include "cmCustomCommand.h"
15
#include "cmCustomCommandGenerator.h"
16
#include "cmCustomCommandLines.h"
17
#include "cmFastbuildNormalTargetGenerator.h"
18
#include "cmFastbuildUtilityTargetGenerator.h"
19
#include "cmGeneratorExpression.h"
20
#include "cmGeneratorTarget.h"
21
#include "cmGlobalCommonGenerator.h"
22
#include "cmGlobalFastbuildGenerator.h"
23
#include "cmList.h"
24
#include "cmListFileCache.h"
25
#include "cmLocalCommonGenerator.h"
26
#include "cmLocalFastbuildGenerator.h"
27
#include "cmLocalGenerator.h"
28
#include "cmMakefile.h"
29
#include "cmOSXBundleGenerator.h"
30
#include "cmOutputConverter.h"
31
#include "cmRulePlaceholderExpander.h"
32
#include "cmSourceFile.h"
33
#include "cmState.h"
34
#include "cmStateTypes.h"
35
#include "cmStringAlgorithms.h"
36
#include "cmSystemTools.h"
37
#include "cmTarget.h"
38
#include "cmValue.h"
39
40
#define FASTBUILD_DOLLAR_TAG "FASTBUILD_DOLLAR_TAG"
41
42
constexpr auto FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT =
43
  "CMAKE_FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT";
44
constexpr auto FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC =
45
  "CMAKE_FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC";
46
47
cmFastbuildTargetGenerator* cmFastbuildTargetGenerator::New(
48
  cmGeneratorTarget* target, std::string config)
49
0
{
50
0
  switch (target->GetType()) {
51
0
    case cmStateEnums::EXECUTABLE:
52
0
    case cmStateEnums::SHARED_LIBRARY:
53
0
    case cmStateEnums::STATIC_LIBRARY:
54
0
    case cmStateEnums::MODULE_LIBRARY:
55
0
    case cmStateEnums::OBJECT_LIBRARY:
56
0
      return new cmFastbuildNormalTargetGenerator(target, std::move(config));
57
58
0
    case cmStateEnums::UTILITY:
59
0
    case cmStateEnums::GLOBAL_TARGET:
60
0
    case cmStateEnums::INTERFACE_LIBRARY:
61
0
      return new cmFastbuildUtilityTargetGenerator(target, std::move(config));
62
63
0
    default:
64
0
      return nullptr;
65
0
  }
66
0
}
67
68
cmFastbuildTargetGenerator::cmFastbuildTargetGenerator(
69
  cmGeneratorTarget* target, std::string configParam)
70
0
  : cmCommonTargetGenerator(target)
71
  , LocalGenerator(
72
0
      static_cast<cmLocalFastbuildGenerator*>(target->GetLocalGenerator()))
73
0
  , TargetDirectDependencies(
74
0
      this->GlobalCommonGenerator->GetTargetDirectDepends(GeneratorTarget))
75
0
  , Config(std::move(configParam))
76
0
{
77
0
  this->MacOSXContentGenerator =
78
0
    cm::make_unique<MacOSXContentGeneratorType>(this, Config);
79
0
}
80
81
void cmFastbuildTargetGenerator::LogMessage(std::string const& m) const
82
0
{
83
0
  this->GetGlobalGenerator()->LogMessage(m);
84
0
}
85
86
std::string cmFastbuildTargetGenerator::GetUtilityAliasFromBuildStep(
87
  FastbuildBuildStep step) const
88
0
{
89
0
  if (step == FastbuildBuildStep::PRE_BUILD) {
90
0
    return GetTargetName() + FASTBUILD_PRE_BUILD_ALIAS_POSTFIX;
91
0
  }
92
0
  if (step == FastbuildBuildStep::PRE_LINK) {
93
0
    return GetTargetName() + FASTBUILD_PRE_LINK_ALIAS_POSTFIX;
94
0
  }
95
0
  if (step == FastbuildBuildStep::POST_BUILD) {
96
0
    return GetTargetName() + FASTBUILD_POST_BUILD_ALIAS_POSTFIX;
97
0
  }
98
0
  return GetTargetName() + FASTBUILD_CUSTOM_COMMAND_ALIAS_POSTFIX;
99
0
}
100
101
void cmFastbuildTargetGenerator::MacOSXContentGeneratorType::operator()(
102
  cmSourceFile const& source, char const* pkgloc,
103
  std::string const& configName)
104
0
{
105
  // Skip OS X content when not building a Framework or Bundle.
106
0
  if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) {
107
0
    return;
108
0
  }
109
110
  // Get the input file location.
111
0
  std::string input = source.GetFullPath();
112
0
  input = this->Generator->GetGlobalGenerator()->ConvertToFastbuildPath(input);
113
114
  // Get the output file location.
115
0
  std::string output =
116
0
    this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(
117
0
      pkgloc, configName);
118
119
0
  output += "/";
120
0
  output += cmSystemTools::GetFilenameName(input);
121
0
  output =
122
0
    this->Generator->GetGlobalGenerator()->ConvertToFastbuildPath(output);
123
124
0
  FastbuildCopyNode node;
125
0
  node.Name = "Copy_" + output;
126
0
  node.Source = std::move(input);
127
0
  if (cmSystemTools::FileIsDirectory(node.Source)) {
128
0
    node.CopyDir = true;
129
0
  }
130
0
  node.Dest = std::move(output);
131
  // Just in case if "from" is generated by some custom command.
132
  // Tested in "BundleTest" test.
133
0
  node.PreBuildDependencies =
134
0
    this->Generator->GetTargetName() + FASTBUILD_CUSTOM_COMMAND_ALIAS_POSTFIX;
135
136
0
  this->Generator->CopyNodes.emplace_back(std::move(node));
137
0
}
138
139
std::string cmFastbuildTargetGenerator::GetCustomCommandTargetName(
140
  cmCustomCommand const& cc, FastbuildBuildStep step) const
141
0
{
142
0
  std::string const extra = this->Makefile->GetCurrentBinaryDirectory();
143
0
  std::string targetName = "cc";
144
145
0
  std::string extras = extra;
146
147
  // Compute hash based on commands & args & output.
148
0
  for (cmCustomCommandLine const& commandLine : cc.GetCommandLines()) {
149
0
    extras += cmJoin(commandLine, "");
150
0
  }
151
0
  for (std::string const& output : cc.GetOutputs()) {
152
0
    extras += output;
153
0
  }
154
155
0
  extras += std::to_string(static_cast<int>(step));
156
157
0
  cmCryptoHash hash(cmCryptoHash::AlgoSHA256);
158
0
  targetName += "-" + hash.HashString(extras).substr(0, 14);
159
160
0
  return targetName;
161
0
}
162
163
void cmFastbuildTargetGenerator::WriteScriptProlog(cmsys::ofstream& file) const
164
0
{
165
#ifdef _WIN32
166
  file << "@echo off\n";
167
#else
168
0
  file << "set -e\n\n";
169
0
#endif
170
0
}
171
void cmFastbuildTargetGenerator::WriteScriptEpilog(cmsys::ofstream& file) const
172
0
{
173
0
  (void)file;
174
#ifdef _WIN32
175
  file << "goto :EOF\n\n"
176
          ":ABORT\n"
177
          "set ERROR_CODE=%ERRORLEVEL%\n"
178
          "echo Batch file failed at line %FAIL_LINE% "
179
          "with errorcode %ERRORLEVEL%\n"
180
          "exit /b %ERROR_CODE%";
181
#endif
182
0
}
183
184
std::string cmFastbuildTargetGenerator::GetScriptWorkingDir(
185
  cmCustomCommandGenerator const& ccg) const
186
0
{
187
0
  std::string workingDirectory = ccg.GetWorkingDirectory();
188
0
  if (workingDirectory.empty()) {
189
0
    return this->LocalCommonGenerator->GetCurrentBinaryDirectory();
190
0
  }
191
0
  return workingDirectory;
192
0
}
193
194
std::string cmFastbuildTargetGenerator::GetScriptFilename(
195
  std::string const& utilityTargetName) const
196
0
{
197
0
  std::string scriptFileName = Makefile->GetCurrentBinaryDirectory();
198
0
  scriptFileName += "/CMakeFiles/";
199
0
  scriptFileName += utilityTargetName;
200
0
  scriptFileName += FASTBUILD_SCRIPT_FILE_EXTENSION;
201
0
  return scriptFileName;
202
0
}
203
204
void cmFastbuildTargetGenerator::AddCommentPrinting(
205
  std::vector<std::string>& cmdLines,
206
  cmCustomCommandGenerator const& ccg) const
207
0
{
208
0
  std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat(
209
0
    cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
210
0
  auto const comment = ccg.GetComment();
211
0
  if (comment) {
212
    // Comment printing should be first. Tested in
213
    // RunCMake.ExternalProject:EnvVars-build test.
214
0
    cmdLines.insert(
215
0
      cmdLines.begin(),
216
0
      cmakeCommand.append(" -E echo ")
217
0
        .append(LocalGenerator->EscapeForShell(cmGeneratorExpression::Evaluate(
218
0
          *comment, this->LocalGenerator, Config))));
219
0
  }
220
0
}
221
222
std::string cmFastbuildTargetGenerator::GetCdCommand(
223
  cmCustomCommandGenerator const& ccg) const
224
0
{
225
0
  return cmStrCat(FASTBUILD_SCRIPT_CD,
226
0
                  this->LocalGenerator->ConvertToOutputFormat(
227
0
                    GetScriptWorkingDir(ccg), cmOutputConverter::SHELL));
228
0
}
229
230
void cmFastbuildTargetGenerator::WriteCmdsToFile(
231
  cmsys::ofstream& file, std::vector<std::string> const& cmds) const
232
0
{
233
#ifdef _WIN32
234
  int line = 1;
235
  for (auto cmd : cmds) {
236
    // On Windows batch, '%' is a special character that needs to be
237
    // doubled to be escaped
238
    cmSystemTools::ReplaceString(cmd, "%", "%%");
239
    file << cmd << " || (set FAIL_LINE=" << ++line << "& goto :ABORT)" << '\n';
240
#else
241
0
  for (auto const& cmd : cmds) {
242
0
    file << cmd << '\n';
243
0
#endif
244
0
  }
245
0
}
246
247
void cmFastbuildTargetGenerator::AddOutput(cmCustomCommandGenerator const& ccg,
248
                                           FastbuildExecNode& exec)
249
0
{
250
0
  std::string dummyOutput = cmSystemTools::JoinPath(
251
0
    { LocalCommonGenerator->GetMakefile()->GetHomeOutputDirectory(),
252
0
      "/_fbuild_dummy" });
253
0
  this->GetGlobalGenerator()->AllFoldersToClean.insert(dummyOutput);
254
255
0
  dummyOutput.append("/").append(exec.Name).append(
256
0
    FASTBUILD_DUMMY_OUTPUT_EXTENSION);
257
258
0
  std::vector<std::string> const& outputs = ccg.GetOutputs();
259
0
  std::vector<std::string> const& byproducts = ccg.GetByproducts();
260
261
0
  exec.OutputsAlias.Name = exec.Name + FASTBUILD_OUTPUTS_ALIAS_POSTFIX;
262
  // If CC doesn't have any output - we should always run it.
263
  // Tested in "RunCMake.CMakePresetsBuild" test.
264
0
  bool hasAnyNonSymbolicOutput = false;
265
266
0
  bool const trackByproducts =
267
0
    this->Makefile->IsDefinitionSet(FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT);
268
269
0
  auto const isSymbolic = [this](std::string const& file) {
270
0
    cmSourceFile* sf = this->Makefile->GetSource(file);
271
0
    if (sf && sf->GetPropertyAsBool("SYMBOLIC")) {
272
0
      LogMessage("Skipping symbolic file: " + file);
273
0
      return true;
274
0
    }
275
0
    return false;
276
0
  };
277
278
0
  for (std::string const& output : outputs) {
279
    // Tested in "RunCMake.BuildDepends".
280
0
    if (isSymbolic(output)) {
281
0
      continue;
282
0
    }
283
0
    hasAnyNonSymbolicOutput = true;
284
0
    std::string const outputPath = this->ConvertToFastbuildPath(output);
285
0
    LogMessage("CC's output: " + outputPath);
286
0
    exec.OutputsAlias.PreBuildDependencies.emplace(outputPath);
287
    // Ensure output path exists. For some reason, "CMake -E touch" fails with
288
    // "cmake -E touch: failed to update "...
289
0
    cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(outputPath));
290
0
    this->GetGlobalGenerator()->AddFileToClean(outputPath);
291
0
  }
292
293
0
  exec.ByproductsAlias.Name = exec.Name + FASTBUILD_BYPRODUCTS_ALIAS_POSTFIX;
294
0
  for (std::string const& byproduct : byproducts) {
295
0
    if (trackByproducts) {
296
0
      hasAnyNonSymbolicOutput = true;
297
0
    }
298
0
    std::string const byproductPath = this->ConvertToFastbuildPath(byproduct);
299
0
    exec.ByproductsAlias.PreBuildDependencies.emplace(byproductPath);
300
0
    this->GetGlobalGenerator()->AddFileToClean(byproductPath);
301
0
  }
302
303
0
  auto const addDummyOutput = [&] {
304
    // So that the dummy file is always created.
305
0
    exec.ExecUseStdOutAsOutput = true;
306
0
    exec.ExecOutput = this->ConvertToFastbuildPath(dummyOutput);
307
0
    for (auto const& output : exec.OutputsAlias.PreBuildDependencies) {
308
0
      OutputsToReplace[output.Name] = exec.ExecOutput;
309
0
      LogMessage(cmStrCat("Adding replace from ", output.Name, " to ",
310
0
                          exec.ExecOutput));
311
0
    }
312
0
  };
313
314
  // We don't have any output that is expected to appear on disk -> run always.
315
  // Tested in "RunCMake.ExternalProject":BUILD_ALWAYS
316
0
  if (!hasAnyNonSymbolicOutput) {
317
0
    exec.ExecAlways = true;
318
0
    addDummyOutput();
319
0
    return;
320
0
  }
321
322
0
  if (!exec.OutputsAlias.PreBuildDependencies.empty()) {
323
0
    exec.ExecOutput = this->ConvertToFastbuildPath(
324
0
      exec.OutputsAlias.PreBuildDependencies.begin()->Name);
325
0
  } else {
326
0
    exec.ExecOutput = this->ConvertToFastbuildPath(
327
0
      exec.ByproductsAlias.PreBuildDependencies.begin()->Name);
328
0
  }
329
330
  // Optionally add the "deps-check" Exec if we have more than 1 OUTPUT, but
331
  // allow user to opt out.
332
0
  if (exec.OutputsAlias.PreBuildDependencies.size() > 1 &&
333
0
      !this->Makefile->IsDefinitionSet(
334
0
        FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC)) {
335
0
    exec.NeedsDepsCheckExec = true;
336
0
  }
337
0
}
338
339
void cmFastbuildTargetGenerator::AddExecArguments(
340
  FastbuildExecNode& exec, std::string const& scriptFilename) const
341
0
{
342
0
  exec.ExecArguments =
343
0
    cmStrCat(FASTBUILD_SCRIPT_FILE_ARG,
344
0
             cmGlobalFastbuildGenerator::QuoteIfHasSpaces(scriptFilename));
345
346
0
  exec.ScriptFile = scriptFilename;
347
0
  exec.ExecExecutable =
348
0
    cmGlobalFastbuildGenerator::GetExternalShellExecutable();
349
0
}
350
351
void cmFastbuildTargetGenerator::GetDepends(
352
  cmCustomCommandGenerator const& ccg, std::string const& currentCCName,
353
  std::vector<std::string>& fileLevelDeps,
354
  std::set<FastbuildTargetDep>& targetDep) const
355
0
{
356
0
  for (auto dep : ccg.GetDepends()) {
357
0
    LogMessage("Dep: " + dep);
358
0
    auto orig = dep;
359
0
    if (this->LocalCommonGenerator->GetRealDependency(
360
0
          dep, Config, dep, ccg.GetCC().GetCMP0212Status())) {
361
0
      LogMessage("Real dep: " + dep);
362
0
      if (!dep.empty()) {
363
0
        LogMessage("Custom command real dep: " + dep);
364
0
        for (auto const& item : cmList{ cmGeneratorExpression::Evaluate(
365
0
               this->ConvertToFastbuildPath(dep), this->LocalGenerator,
366
0
               Config) }) {
367
0
          fileLevelDeps.emplace_back(item);
368
0
        }
369
0
      }
370
0
    }
371
0
    dep = this->ConvertToFastbuildPath(dep);
372
0
    LogMessage("Real dep converted: " + dep);
373
374
0
    auto const targetInfo = this->LocalGenerator->GetSourcesWithOutput(dep);
375
0
    if (targetInfo.Target) {
376
0
      LogMessage(
377
0
        cmStrCat("dep: ", dep, ", target: ", targetInfo.Target->GetName()));
378
0
      auto const& target = targetInfo.Target;
379
0
      auto const processCCs = [this, &currentCCName, &targetDep,
380
0
                               dep](std::vector<cmCustomCommand> const& ccs,
381
0
                                    FastbuildBuildStep step) {
382
0
        for (auto const& cc : ccs) {
383
0
          for (auto const& output : cc.GetOutputs()) {
384
0
            LogMessage(cmStrCat("dep: ", dep, ", post output: ",
385
0
                                this->ConvertToFastbuildPath(output)));
386
0
            if (this->ConvertToFastbuildPath(output) == dep) {
387
0
              auto ccName = this->GetCustomCommandTargetName(cc, step);
388
0
              if (ccName != currentCCName) {
389
0
                LogMessage("Additional CC dep from target: " + ccName);
390
0
                targetDep.emplace(std::move(ccName));
391
0
              }
392
0
            }
393
0
          }
394
0
          for (auto const& byproduct : cc.GetByproducts()) {
395
0
            LogMessage(cmStrCat("dep: ", dep, ", post byproduct: ",
396
0
                                this->ConvertToFastbuildPath(byproduct)));
397
0
            if (this->ConvertToFastbuildPath(byproduct) == dep) {
398
0
              auto ccName = this->GetCustomCommandTargetName(cc, step);
399
0
              if (ccName != currentCCName) {
400
0
                LogMessage("Additional CC dep from target: " + ccName);
401
0
                targetDep.emplace(std::move(ccName));
402
0
              }
403
0
            }
404
0
          }
405
0
        }
406
0
      };
407
0
      processCCs(target->GetPreBuildCommands(), FastbuildBuildStep::PRE_BUILD);
408
0
      processCCs(target->GetPreLinkCommands(), FastbuildBuildStep::PRE_LINK);
409
0
      processCCs(target->GetPostBuildCommands(),
410
0
                 FastbuildBuildStep::POST_BUILD);
411
0
      continue;
412
0
    }
413
0
    if (!targetInfo.Source) {
414
0
      LogMessage(cmStrCat("dep: ", dep, ", no source, byproduct: ",
415
0
                          targetInfo.SourceIsByproduct));
416
      // Tested in "OutDir" test.
417
0
      if (!cmSystemTools::FileIsFullPath(orig)) {
418
0
        targetDep.emplace(std::move(orig));
419
0
      }
420
0
      continue;
421
0
    }
422
0
    if (!targetInfo.Source->GetCustomCommand()) {
423
0
      LogMessage(cmStrCat("dep: ", dep, ", no GetCustomCommand"));
424
0
      continue;
425
0
    }
426
0
    if (targetInfo.Source && targetInfo.Source->GetCustomCommand()) {
427
0
      auto ccName = this->GetCustomCommandTargetName(
428
0
        *targetInfo.Source->GetCustomCommand(), FastbuildBuildStep::REST);
429
0
      if (ccName != currentCCName) {
430
0
        LogMessage("Additional CC dep: " + ccName);
431
0
        targetDep.emplace(std::move(ccName));
432
0
      }
433
0
    }
434
0
  }
435
0
}
436
437
void cmFastbuildTargetGenerator::ReplaceProblematicMakeVars(
438
  std::string& command) const
439
0
{
440
  // TODO: fix problematic global targets.  For now, search and replace the
441
  // makefile vars.
442
0
  cmSystemTools::ReplaceString(
443
0
    command, "$(CMAKE_SOURCE_DIR)",
444
0
    this->LocalGenerator->ConvertToOutputFormat(
445
0
      this->LocalGenerator->GetSourceDirectory(), cmOutputConverter::SHELL));
446
0
  cmSystemTools::ReplaceString(
447
0
    command, "$(CMAKE_BINARY_DIR)",
448
0
    this->LocalGenerator->ConvertToOutputFormat(
449
0
      this->LocalGenerator->GetBinaryDirectory(), cmOutputConverter::SHELL));
450
0
  cmSystemTools::ReplaceString(command, "$(ARGS)", "");
451
0
}
452
453
FastbuildExecNode cmFastbuildTargetGenerator::GetAppleTextStubCommand() const
454
0
{
455
0
  FastbuildExecNode res;
456
0
  if (!this->GeneratorTarget->IsApple() ||
457
0
      !this->GeneratorTarget->HasImportLibrary(Config)) {
458
0
    return res;
459
0
  }
460
461
0
  auto const names = DetectOutput();
462
0
  std::string const outpathImp =
463
0
    this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
464
0
      Config, cmStateEnums::ImportLibraryArtifact));
465
466
0
  std::string const binPath =
467
0
    this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
468
0
      Config, cmStateEnums::RuntimeBinaryArtifact));
469
470
0
  cmSystemTools::MakeDirectory(outpathImp);
471
472
0
  std::string rule = this->LocalGenerator->GetMakefile()->GetSafeDefinition(
473
0
    "CMAKE_CREATE_TEXT_STUBS");
474
0
  LogMessage("CMAKE_CREATE_TEXT_STUBS:" + rule);
475
476
0
  auto rulePlaceholderExpander =
477
0
    this->GetLocalGenerator()->CreateRulePlaceholderExpander();
478
479
0
  cmRulePlaceholderExpander::RuleVariables vars;
480
0
  res.ExecOutput = cmStrCat(outpathImp, '/', names.ImportReal);
481
0
  res.ExecInput = { cmStrCat(binPath, '/', names.SharedObject) };
482
483
0
  vars.Target = res.ExecInput[0].c_str();
484
0
  rulePlaceholderExpander->SetTargetImpLib(res.ExecOutput);
485
0
  rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), rule,
486
0
                                               vars);
487
488
0
  LogMessage("CMAKE_CREATE_TEXT_STUBS expanded:" + rule);
489
0
  std::string executable;
490
0
  std::string args;
491
0
  if (!cmSystemTools::SplitProgramFromArgs(rule, executable, args)) {
492
0
    cmSystemTools::Error("Failed to split program from args: " + rule);
493
0
    return res;
494
0
  }
495
496
0
  res.Name = cmStrCat("create_", names.ImportOutput, "_text_stub");
497
0
  res.ExecExecutable = std::move(executable);
498
0
  res.ExecArguments = std::move(args);
499
0
  res.ExecWorkingDir = this->LocalCommonGenerator->GetCurrentBinaryDirectory();
500
501
  // Wait for the build.
502
0
  res.PreBuildDependencies.emplace(this->GetTargetName());
503
0
  return res;
504
0
}
505
FastbuildExecNode cmFastbuildTargetGenerator::GetDepsCheckExec(
506
  FastbuildExecNode const& depender)
507
0
{
508
0
  FastbuildExecNode exec;
509
0
  exec.Name = depender.Name + "-check-depends";
510
0
  exec.ExecAlways = true;
511
0
  exec.ExecUseStdOutAsOutput = true;
512
0
  exec.ExecOutput = depender.ExecOutput + ".deps-checker";
513
0
  exec.ExecExecutable = cmSystemTools::GetCMakeCommand();
514
0
  exec.ExecArguments =
515
0
    cmStrCat(std::move(exec.ExecArguments),
516
0
             "-E cmake_fastbuild_check_depends ", depender.ExecOutput);
517
0
  for (auto const& dep : depender.OutputsAlias.PreBuildDependencies) {
518
0
    exec.ExecArguments =
519
0
      cmStrCat(std::move(exec.ExecArguments), ' ', dep.Name);
520
0
  }
521
0
  for (auto const& dep : depender.ByproductsAlias.PreBuildDependencies) {
522
0
    exec.ExecArguments =
523
0
      cmStrCat(std::move(exec.ExecArguments), ' ', dep.Name);
524
0
  }
525
0
  return exec;
526
0
}
527
528
FastbuildExecNodes cmFastbuildTargetGenerator::GenerateCommands(
529
  FastbuildBuildStep buildStep)
530
0
{
531
0
  FastbuildExecNodes execs;
532
0
  execs.Alias.Name = GetUtilityAliasFromBuildStep(buildStep);
533
534
0
  std::vector<cmCustomCommand> commands;
535
0
  if (buildStep == FastbuildBuildStep::PRE_BUILD) {
536
0
    commands = GeneratorTarget->GetPreBuildCommands();
537
0
    LogMessage("STEP: PRE_BUILD");
538
0
  } else if (buildStep == FastbuildBuildStep::PRE_LINK) {
539
0
    commands = GeneratorTarget->GetPreLinkCommands();
540
0
    LogMessage("STEP: PRE_LINK");
541
0
  } else if (buildStep == FastbuildBuildStep::POST_BUILD) {
542
0
    commands = GeneratorTarget->GetPostBuildCommands();
543
0
    LogMessage("STEP: POST_BUILD");
544
0
  } else {
545
0
    LogMessage("STEP: ALL CUSTOM COMMANDS");
546
0
    std::vector<cmSourceFile const*> customCommands;
547
0
    GeneratorTarget->GetCustomCommands(customCommands, Config);
548
0
    for (cmSourceFile const* source : customCommands) {
549
0
      cmCustomCommand const* cmd = source->GetCustomCommand();
550
0
      if (!cmd->GetCommandLines().empty()) {
551
0
        commands.emplace_back(*cmd);
552
0
      }
553
0
    }
554
0
  }
555
0
  LogMessage(cmStrCat("Number of custom commands: ", commands.size()));
556
0
  for (cmCustomCommand const& customCommand : commands) {
557
0
    cmCustomCommandGenerator ccg(customCommand, Config, LocalCommonGenerator);
558
0
    std::string launcher = this->MakeCustomLauncher(ccg);
559
560
0
    std::string const execName =
561
0
      GetCustomCommandTargetName(customCommand, buildStep);
562
563
0
    std::vector<std::string> cmdLines;
564
0
    if (ccg.GetNumberOfCommands() > 0) {
565
0
      cmdLines.push_back(GetCdCommand(ccg));
566
0
    }
567
568
    // Since we are not using FASTBuild Exec nodes natively, we need to
569
    // have shell specific escape.
570
0
    this->LocalGenerator->GetState()->SetFastbuildMake(false);
571
    // To avoid replacing $ with $$ in the command line.
572
0
    this->LocalGenerator->SetLinkScriptShell(true);
573
0
    for (unsigned j = 0; j != ccg.GetNumberOfCommands(); ++j) {
574
0
      std::string const command = ccg.GetCommand(j);
575
      // Tested in "CustomCommand" ("empty_command") test.
576
0
      if (!command.empty()) {
577
578
0
        cmdLines.emplace_back(launcher +
579
0
                              this->LocalGenerator->ConvertToOutputFormat(
580
0
                                command, cmOutputConverter::SHELL));
581
582
0
        std::string& cmd = cmdLines.back();
583
0
        ccg.AppendArguments(j, cmd);
584
0
        ReplaceProblematicMakeVars(cmd);
585
0
        LogMessage("cmCustomCommandLine: " + cmd);
586
0
      }
587
0
    }
588
0
    if (cmdLines.empty()) {
589
0
      return {};
590
0
    }
591
0
    this->LocalGenerator->GetState()->SetFastbuildMake(true);
592
593
0
    FastbuildExecNode execNode;
594
0
    execNode.Name = execName;
595
596
    // Add dependencies to "ExecInput" so that FASTBuild will re-run the Exec
597
    // when needed, but also add to "PreBuildDependencies" for correct sorting.
598
    // Tested in "ObjectLibrary / complexOneConfig" tests.
599
0
    GetDepends(ccg, execName, execNode.ExecInput,
600
0
               execNode.PreBuildDependencies);
601
0
    for (auto const& util : ccg.GetUtilities()) {
602
0
      auto const& utilTargetName = util.Value.first;
603
0
      LogMessage("Util: " + utilTargetName +
604
0
                 ", cross: " + std::to_string(util.Value.second));
605
0
      auto* const target = this->Makefile->FindTargetToUse(utilTargetName);
606
607
0
      if (target && target->IsImported()) {
608
0
        std::string importedLoc =
609
0
          this->ConvertToFastbuildPath(target->ImportedGetFullPath(
610
0
            Config, cmStateEnums::ArtifactType::RuntimeBinaryArtifact));
611
0
        if (importedLoc.empty()) {
612
0
          importedLoc =
613
0
            this->ConvertToFastbuildPath(target->ImportedGetFullPath(
614
0
              Config, cmStateEnums::ArtifactType::ImportLibraryArtifact));
615
0
        }
616
0
        LogMessage("adding file level dep on imported target: " + importedLoc);
617
0
        execNode.ExecInput.emplace_back(std::move(importedLoc));
618
0
        continue;
619
0
      }
620
      // This CC uses some executable produced by another target. Add explicit
621
      // dep. Tested in "CustomCommand" test.
622
0
      if (util.Value.second) {
623
0
        if (utilTargetName != customCommand.GetTarget()) {
624
0
          LogMessage("Adding util dep: " + utilTargetName);
625
0
          execNode.PreBuildDependencies.emplace(utilTargetName);
626
0
        }
627
0
      }
628
0
    }
629
630
0
    execs.Alias.PreBuildDependencies.emplace(execNode.Name);
631
632
0
    LogMessage(cmStrCat("cmdLines size ", cmdLines.size()));
633
634
0
    if (!cmdLines.empty()) {
635
0
      std::string const scriptFileName = GetScriptFilename(execName);
636
0
      cmsys::ofstream scriptFile(scriptFileName.c_str());
637
638
0
      AddOutput(ccg, execNode);
639
0
      AddExecArguments(execNode, scriptFileName);
640
0
      AddCommentPrinting(cmdLines, ccg);
641
642
0
      WriteScriptProlog(scriptFile);
643
0
      WriteCmdsToFile(scriptFile, cmdLines);
644
0
      WriteScriptEpilog(scriptFile);
645
0
    }
646
647
0
    if (buildStep == FastbuildBuildStep::POST_BUILD) {
648
      // Execute POST_BUILD in order in which they are declared.
649
      // Tested in "complex" test.
650
0
      for (auto& exec : execs.Nodes) {
651
0
        execNode.PreBuildDependencies.emplace(exec.Name);
652
0
      }
653
0
    }
654
0
    for (auto const& out : execNode.OutputsAlias.PreBuildDependencies) {
655
0
      LogMessage(cmStrCat("Adding replace from ", out.Name, " to ", execName));
656
0
      OutputToExecName[out.Name] = execName;
657
0
    }
658
0
    execs.Nodes.emplace_back(std::move(execNode));
659
0
  }
660
0
  for (auto& exec : execs.Nodes) {
661
0
    for (auto& inputFile : exec.ExecInput) {
662
0
      auto const iter = OutputsToReplace.find(inputFile);
663
0
      if (iter != OutputsToReplace.end()) {
664
0
        LogMessage(
665
0
          cmStrCat("Replacing input: ", inputFile, " with ", iter->second));
666
0
        inputFile = iter->second;
667
0
      }
668
0
      auto const depIter = std::find_if(
669
0
        exec.PreBuildDependencies.begin(), exec.PreBuildDependencies.end(),
670
0
        [this](FastbuildTargetDep const& dep) {
671
0
          return !OutputToExecName[dep.Name].empty();
672
0
        });
673
0
      if (depIter != exec.PreBuildDependencies.end()) {
674
0
        LogMessage(cmStrCat("Replacing dep ", depIter->Name, " with ",
675
0
                            OutputToExecName[depIter->Name]));
676
0
        exec.PreBuildDependencies.emplace(OutputToExecName[depIter->Name]);
677
0
        exec.PreBuildDependencies.erase(depIter);
678
0
      }
679
0
    }
680
0
    if (exec.NeedsDepsCheckExec) {
681
0
      auto depsCheckExec = GetDepsCheckExec(exec);
682
0
      LogMessage("Adding deps check Exec: " + depsCheckExec.Name);
683
0
      exec.PreBuildDependencies.emplace(depsCheckExec.Name);
684
0
      this->GetGlobalGenerator()->AddTarget(std::move(depsCheckExec));
685
0
    }
686
0
  }
687
0
  return execs;
688
0
}
689
690
std::string cmFastbuildTargetGenerator::MakeCustomLauncher(
691
  cmCustomCommandGenerator const& ccg)
692
0
{
693
  // Copied from cmLocalNinjaGenerator::MakeCustomLauncher.
694
0
  cmValue property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM");
695
696
0
  if (!cmNonempty(property_value)) {
697
0
    return std::string();
698
0
  }
699
700
  // Expand rule variables referenced in the given launcher command.
701
0
  cmRulePlaceholderExpander::RuleVariables vars;
702
703
0
  std::string output;
704
0
  std::vector<std::string> const& outputs = ccg.GetOutputs();
705
0
  for (size_t i = 0; i < outputs.size(); ++i) {
706
0
    output =
707
0
      cmStrCat(output,
708
0
               this->LocalGenerator->ConvertToOutputFormat(
709
0
                 ccg.GetWorkingDirectory().empty()
710
0
                   ? this->LocalGenerator->MaybeRelativeToCurBinDir(outputs[i])
711
0
                   : outputs[i],
712
0
                 cmOutputConverter::SHELL));
713
0
    if (i != outputs.size() - 1) {
714
0
      output = cmStrCat(output, ',');
715
0
    }
716
0
  }
717
0
  vars.Output = output.c_str();
718
0
  vars.Role = ccg.GetCC().GetRole().c_str();
719
0
  vars.CMTargetName = ccg.GetCC().GetTarget().c_str();
720
0
  vars.Config = ccg.GetOutputConfig().c_str();
721
722
0
  auto rulePlaceholderExpander =
723
0
    this->LocalGenerator->CreateRulePlaceholderExpander();
724
725
0
  std::string launcher = *property_value;
726
0
  rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, launcher,
727
0
                                               vars);
728
0
  if (!launcher.empty()) {
729
0
    launcher += " ";
730
0
  }
731
732
0
  LogMessage("CC Launcher: " + launcher);
733
0
  return launcher;
734
0
}
735
736
std::string cmFastbuildTargetGenerator::GetTargetName() const
737
0
{
738
0
  if (this->GeneratorTarget->GetType() == cmStateEnums::GLOBAL_TARGET) {
739
0
    return this->GetGlobalGenerator()->GetTargetName(GeneratorTarget);
740
0
  }
741
0
  return this->GeneratorTarget->GetName();
742
0
}
743
744
cmGeneratorTarget::Names cmFastbuildTargetGenerator::DetectOutput() const
745
0
{
746
0
  if (GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
747
0
    return GeneratorTarget->GetExecutableNames(Config);
748
0
  }
749
0
  return GeneratorTarget->GetLibraryNames(Config);
750
0
}
751
752
void cmFastbuildTargetGenerator::AddObjectDependencies(
753
  FastbuildTarget& fastbuildTarget,
754
  std::vector<std::string>& allObjectDepends) const
755
0
{
756
0
  auto const FindObjListWhichOutputs = [&fastbuildTarget](
757
0
                                         std::string const& output) {
758
0
    for (FastbuildObjectListNode const& objList :
759
0
         fastbuildTarget.ObjectListNodes) {
760
0
      if (objList.ObjectOutputs.find(output) != objList.ObjectOutputs.end()) {
761
0
        return objList.Name;
762
0
      }
763
0
    }
764
0
    return std::string{};
765
0
  };
766
767
0
  for (FastbuildObjectListNode& objList : fastbuildTarget.ObjectListNodes) {
768
0
    for (auto const& objDep : objList.ObjectDepends) {
769
      // Check if there is another object list which outputs (OBJECT_OUTPUTS)
770
      // something that this object list needs (OBJECT_DEPENDS).
771
0
      auto anotherObjList = FindObjListWhichOutputs(objDep);
772
0
      if (!anotherObjList.empty()) {
773
0
        LogMessage("Adding explicit <OBJECT_DEPENDS> dep: " + anotherObjList);
774
0
        allObjectDepends.emplace_back(anotherObjList);
775
0
        objList.PreBuildDependencies.emplace(std::move(anotherObjList));
776
777
0
      } else {
778
0
        LogMessage("Adding <OBJECT_DEPENDS> dep: " + objDep);
779
0
        allObjectDepends.emplace_back(objDep);
780
0
        objList.PreBuildDependencies.emplace(objDep);
781
0
      }
782
0
    }
783
0
  }
784
0
  cmGlobalFastbuildGenerator::TopologicalSort(fastbuildTarget.ObjectListNodes);
785
0
}
786
787
void cmFastbuildTargetGenerator::AddLinkerNodeDependencies(
788
  FastbuildTarget& fastbuildTarget)
789
0
{
790
0
  for (auto& linkerNode : fastbuildTarget.LinkerNode) {
791
0
    if (!fastbuildTarget.PreLinkExecNodes.Nodes.empty()) {
792
0
      linkerNode.PreBuildDependencies.emplace(
793
0
        fastbuildTarget.Name + FASTBUILD_PRE_LINK_ALIAS_POSTFIX);
794
0
    }
795
0
  }
796
0
}
797
798
std::string cmFastbuildTargetGenerator::GetClangTidyReplacementsFilePath(
799
  std::string const& directory, cmSourceFile const& source,
800
  std::string const& /*config*/) const
801
0
{
802
803
0
  std::string objectDir =
804
0
    this->ConvertToFastbuildPath(this->GeneratorTarget->GetSupportDirectory());
805
0
  std::string const& objectName =
806
0
    this->GeneratorTarget->GetObjectName(&source);
807
0
  std::string path =
808
0
    cmStrCat(directory, '/', objectDir, '/', objectName, ".yaml");
809
0
  LogMessage("ClangTidy replacements file: " + path);
810
0
  return path;
811
0
}
812
813
void cmFastbuildTargetGenerator::AddIncludeFlags(std::string& languageFlags,
814
                                                 std::string const& language,
815
                                                 std::string const&)
816
0
{
817
0
  std::vector<std::string> includes;
818
0
  this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
819
0
                                              language, Config);
820
  // Add include directory flags.
821
0
  std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
822
0
    includes, this->GeneratorTarget, language, Config, false);
823
824
0
  this->LocalGenerator->AppendFlags(languageFlags, includeFlags);
825
0
}
826
827
std::string cmFastbuildTargetGenerator::GetName()
828
0
{
829
0
  return GeneratorTarget->GetName();
830
0
}
831
832
std::string cmFastbuildTargetGenerator::ConvertToFastbuildPath(
833
  std::string const& path) const
834
0
{
835
0
  return GetGlobalGenerator()->ConvertToFastbuildPath(path);
836
0
}
837
838
cmGlobalFastbuildGenerator* cmFastbuildTargetGenerator::GetGlobalGenerator()
839
  const
840
0
{
841
0
  return this->LocalGenerator->GetGlobalFastbuildGenerator();
842
0
}
843
844
void cmFastbuildTargetGenerator::AdditionalCleanFiles()
845
0
{
846
0
  if (cmValue prop_value =
847
0
        this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
848
0
    auto* lg = this->LocalGenerator;
849
0
    cmList cleanFiles(cmGeneratorExpression::Evaluate(*prop_value, lg, Config,
850
0
                                                      this->GeneratorTarget));
851
0
    std::string const& binaryDir = lg->GetCurrentBinaryDirectory();
852
0
    auto* gg = lg->GetGlobalFastbuildGenerator();
853
0
    for (auto const& cleanFile : cleanFiles) {
854
      // Support relative paths
855
0
      gg->AddFileToClean(gg->ConvertToFastbuildPath(
856
0
        cmSystemTools::CollapseFullPath(cleanFile, binaryDir)));
857
0
    }
858
0
  }
859
0
}