Coverage Report

Created: 2026-07-14 07:09

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