Coverage Report

Created: 2026-02-09 06:05

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(dep, Config, dep)) {
360
0
      LogMessage("Real dep: " + dep);
361
0
      if (!dep.empty()) {
362
0
        LogMessage("Custom command real dep: " + dep);
363
0
        for (auto const& item : cmList{ cmGeneratorExpression::Evaluate(
364
0
               this->ConvertToFastbuildPath(dep), this->LocalGenerator,
365
0
               Config) }) {
366
0
          fileLevelDeps.emplace_back(item);
367
0
        }
368
0
      }
369
0
    }
370
0
    dep = this->ConvertToFastbuildPath(dep);
371
0
    LogMessage("Real dep converted: " + dep);
372
373
0
    auto const targetInfo = this->LocalGenerator->GetSourcesWithOutput(dep);
374
0
    if (targetInfo.Target) {
375
0
      LogMessage(
376
0
        cmStrCat("dep: ", dep, ", target: ", targetInfo.Target->GetName()));
377
0
      auto const& target = targetInfo.Target;
378
0
      auto const processCCs = [this, &currentCCName, &targetDep,
379
0
                               dep](std::vector<cmCustomCommand> const& ccs,
380
0
                                    FastbuildBuildStep step) {
381
0
        for (auto const& cc : ccs) {
382
0
          for (auto const& output : cc.GetOutputs()) {
383
0
            LogMessage(cmStrCat("dep: ", dep, ", post output: ",
384
0
                                this->ConvertToFastbuildPath(output)));
385
0
            if (this->ConvertToFastbuildPath(output) == dep) {
386
0
              auto ccName = this->GetCustomCommandTargetName(cc, step);
387
0
              if (ccName != currentCCName) {
388
0
                LogMessage("Additional CC dep from target: " + ccName);
389
0
                targetDep.emplace(std::move(ccName));
390
0
              }
391
0
            }
392
0
          }
393
0
          for (auto const& byproduct : cc.GetByproducts()) {
394
0
            LogMessage(cmStrCat("dep: ", dep, ", post byproduct: ",
395
0
                                this->ConvertToFastbuildPath(byproduct)));
396
0
            if (this->ConvertToFastbuildPath(byproduct) == dep) {
397
0
              auto ccName = this->GetCustomCommandTargetName(cc, step);
398
0
              if (ccName != currentCCName) {
399
0
                LogMessage("Additional CC dep from target: " + ccName);
400
0
                targetDep.emplace(std::move(ccName));
401
0
              }
402
0
            }
403
0
          }
404
0
        }
405
0
      };
406
0
      processCCs(target->GetPreBuildCommands(), FastbuildBuildStep::PRE_BUILD);
407
0
      processCCs(target->GetPreLinkCommands(), FastbuildBuildStep::PRE_LINK);
408
0
      processCCs(target->GetPostBuildCommands(),
409
0
                 FastbuildBuildStep::POST_BUILD);
410
0
      continue;
411
0
    }
412
0
    if (!targetInfo.Source) {
413
0
      LogMessage(cmStrCat("dep: ", dep, ", no source, byproduct: ",
414
0
                          targetInfo.SourceIsByproduct));
415
      // Tested in "OutDir" test.
416
0
      if (!cmSystemTools::FileIsFullPath(orig)) {
417
0
        targetDep.emplace(std::move(orig));
418
0
      }
419
0
      continue;
420
0
    }
421
0
    if (!targetInfo.Source->GetCustomCommand()) {
422
0
      LogMessage(cmStrCat("dep: ", dep, ", no GetCustomCommand"));
423
0
      continue;
424
0
    }
425
0
    if (targetInfo.Source && targetInfo.Source->GetCustomCommand()) {
426
0
      auto ccName = this->GetCustomCommandTargetName(
427
0
        *targetInfo.Source->GetCustomCommand(), FastbuildBuildStep::REST);
428
0
      if (ccName != currentCCName) {
429
0
        LogMessage("Additional CC dep: " + ccName);
430
0
        targetDep.emplace(std::move(ccName));
431
0
      }
432
0
    }
433
0
  }
434
0
}
435
436
void cmFastbuildTargetGenerator::ReplaceProblematicMakeVars(
437
  std::string& command) const
438
0
{
439
  // TODO: fix problematic global targets.  For now, search and replace the
440
  // makefile vars.
441
0
  cmSystemTools::ReplaceString(
442
0
    command, "$(CMAKE_SOURCE_DIR)",
443
0
    this->LocalGenerator->ConvertToOutputFormat(
444
0
      this->LocalGenerator->GetSourceDirectory(), cmOutputConverter::SHELL));
445
0
  cmSystemTools::ReplaceString(
446
0
    command, "$(CMAKE_BINARY_DIR)",
447
0
    this->LocalGenerator->ConvertToOutputFormat(
448
0
      this->LocalGenerator->GetBinaryDirectory(), cmOutputConverter::SHELL));
449
0
  cmSystemTools::ReplaceString(command, "$(ARGS)", "");
450
0
}
451
452
FastbuildExecNode cmFastbuildTargetGenerator::GetAppleTextStubCommand() const
453
0
{
454
0
  FastbuildExecNode res;
455
0
  if (!this->GeneratorTarget->IsApple() ||
456
0
      !this->GeneratorTarget->HasImportLibrary(Config)) {
457
0
    return res;
458
0
  }
459
460
0
  auto const names = DetectOutput();
461
0
  std::string const outpathImp =
462
0
    this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
463
0
      Config, cmStateEnums::ImportLibraryArtifact));
464
465
0
  std::string const binPath =
466
0
    this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
467
0
      Config, cmStateEnums::RuntimeBinaryArtifact));
468
469
0
  cmSystemTools::MakeDirectory(outpathImp);
470
471
0
  std::string rule = this->LocalGenerator->GetMakefile()->GetSafeDefinition(
472
0
    "CMAKE_CREATE_TEXT_STUBS");
473
0
  LogMessage("CMAKE_CREATE_TEXT_STUBS:" + rule);
474
475
0
  auto rulePlaceholderExpander =
476
0
    this->GetLocalGenerator()->CreateRulePlaceholderExpander();
477
478
0
  cmRulePlaceholderExpander::RuleVariables vars;
479
0
  res.ExecOutput = cmStrCat(outpathImp, '/', names.ImportReal);
480
0
  res.ExecInput = { cmStrCat(binPath, '/', names.SharedObject) };
481
482
0
  vars.Target = res.ExecInput[0].c_str();
483
0
  rulePlaceholderExpander->SetTargetImpLib(res.ExecOutput);
484
0
  rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), rule,
485
0
                                               vars);
486
487
0
  LogMessage("CMAKE_CREATE_TEXT_STUBS expanded:" + rule);
488
0
  std::string executable;
489
0
  std::string args;
490
0
  if (!cmSystemTools::SplitProgramFromArgs(rule, executable, args)) {
491
0
    cmSystemTools::Error("Failed to split program from args: " + rule);
492
0
    return res;
493
0
  }
494
495
0
  res.Name = cmStrCat("create_", names.ImportOutput, "_text_stub");
496
0
  res.ExecExecutable = std::move(executable);
497
0
  res.ExecArguments = std::move(args);
498
0
  res.ExecWorkingDir = this->LocalCommonGenerator->GetCurrentBinaryDirectory();
499
500
  // Wait for the build.
501
0
  res.PreBuildDependencies.emplace(this->GetTargetName());
502
0
  return res;
503
0
}
504
FastbuildExecNode cmFastbuildTargetGenerator::GetDepsCheckExec(
505
  FastbuildExecNode const& depender)
506
0
{
507
0
  FastbuildExecNode exec;
508
0
  exec.Name = depender.Name + "-check-depends";
509
0
  exec.ExecAlways = true;
510
0
  exec.ExecUseStdOutAsOutput = true;
511
0
  exec.ExecOutput = depender.ExecOutput + ".deps-checker";
512
0
  exec.ExecExecutable = cmSystemTools::GetCMakeCommand();
513
0
  exec.ExecArguments =
514
0
    cmStrCat(std::move(exec.ExecArguments),
515
0
             "-E cmake_fastbuild_check_depends ", depender.ExecOutput);
516
0
  for (auto const& dep : depender.OutputsAlias.PreBuildDependencies) {
517
0
    exec.ExecArguments =
518
0
      cmStrCat(std::move(exec.ExecArguments), ' ', dep.Name);
519
0
  }
520
0
  for (auto const& dep : depender.ByproductsAlias.PreBuildDependencies) {
521
0
    exec.ExecArguments =
522
0
      cmStrCat(std::move(exec.ExecArguments), ' ', dep.Name);
523
0
  }
524
0
  return exec;
525
0
}
526
527
FastbuildExecNodes cmFastbuildTargetGenerator::GenerateCommands(
528
  FastbuildBuildStep buildStep)
529
0
{
530
0
  FastbuildExecNodes execs;
531
0
  execs.Alias.Name = GetUtilityAliasFromBuildStep(buildStep);
532
533
0
  std::vector<cmCustomCommand> commands;
534
0
  if (buildStep == FastbuildBuildStep::PRE_BUILD) {
535
0
    commands = GeneratorTarget->GetPreBuildCommands();
536
0
    LogMessage("STEP: PRE_BUILD");
537
0
  } else if (buildStep == FastbuildBuildStep::PRE_LINK) {
538
0
    commands = GeneratorTarget->GetPreLinkCommands();
539
0
    LogMessage("STEP: PRE_LINK");
540
0
  } else if (buildStep == FastbuildBuildStep::POST_BUILD) {
541
0
    commands = GeneratorTarget->GetPostBuildCommands();
542
0
    LogMessage("STEP: POST_BUILD");
543
0
  } else {
544
0
    LogMessage("STEP: ALL CUSTOM COMMANDS");
545
0
    std::vector<cmSourceFile const*> customCommands;
546
0
    GeneratorTarget->GetCustomCommands(customCommands, Config);
547
0
    for (cmSourceFile const* source : customCommands) {
548
0
      cmCustomCommand const* cmd = source->GetCustomCommand();
549
0
      if (!cmd->GetCommandLines().empty()) {
550
0
        commands.emplace_back(*cmd);
551
0
      }
552
0
    }
553
0
  }
554
0
  LogMessage(cmStrCat("Number of custom commands: ", commands.size()));
555
0
  for (cmCustomCommand const& customCommand : commands) {
556
0
    cmCustomCommandGenerator ccg(customCommand, Config, LocalCommonGenerator);
557
0
    std::string launcher = this->MakeCustomLauncher(ccg);
558
559
0
    std::string const execName =
560
0
      GetCustomCommandTargetName(customCommand, buildStep);
561
562
0
    std::vector<std::string> cmdLines;
563
0
    if (ccg.GetNumberOfCommands() > 0) {
564
0
      cmdLines.push_back(GetCdCommand(ccg));
565
0
    }
566
567
    // Since we are not using FASTBuild Exec nodes natively, we need to
568
    // have shell specific escape.
569
0
    this->LocalGenerator->GetState()->SetFastbuildMake(false);
570
    // To avoid replacing $ with $$ in the command line.
571
0
    this->LocalGenerator->SetLinkScriptShell(true);
572
0
    for (unsigned j = 0; j != ccg.GetNumberOfCommands(); ++j) {
573
0
      std::string const command = ccg.GetCommand(j);
574
      // Tested in "CustomCommand" ("empty_command") test.
575
0
      if (!command.empty()) {
576
577
0
        cmdLines.emplace_back(launcher +
578
0
                              this->LocalGenerator->ConvertToOutputFormat(
579
0
                                command, cmOutputConverter::SHELL));
580
581
0
        std::string& cmd = cmdLines.back();
582
0
        ccg.AppendArguments(j, cmd);
583
0
        ReplaceProblematicMakeVars(cmd);
584
0
        LogMessage("cmCustomCommandLine: " + cmd);
585
0
      }
586
0
    }
587
0
    if (cmdLines.empty()) {
588
0
      return {};
589
0
    }
590
0
    this->LocalGenerator->GetState()->SetFastbuildMake(true);
591
592
0
    FastbuildExecNode execNode;
593
0
    execNode.Name = execName;
594
595
    // Add dependencies to "ExecInput" so that FASTBuild will re-run the Exec
596
    // when needed, but also add to "PreBuildDependencies" for correct sorting.
597
    // Tested in "ObjectLibrary / complexOneConfig" tests.
598
0
    GetDepends(ccg, execName, execNode.ExecInput,
599
0
               execNode.PreBuildDependencies);
600
0
    for (auto const& util : ccg.GetUtilities()) {
601
0
      auto const& utilTargetName = util.Value.first;
602
0
      LogMessage("Util: " + utilTargetName +
603
0
                 ", cross: " + std::to_string(util.Value.second));
604
0
      auto* const target = this->Makefile->FindTargetToUse(utilTargetName);
605
606
0
      if (target && target->IsImported()) {
607
0
        std::string importedLoc =
608
0
          this->ConvertToFastbuildPath(target->ImportedGetFullPath(
609
0
            Config, cmStateEnums::ArtifactType::RuntimeBinaryArtifact));
610
0
        if (importedLoc.empty()) {
611
0
          importedLoc =
612
0
            this->ConvertToFastbuildPath(target->ImportedGetFullPath(
613
0
              Config, cmStateEnums::ArtifactType::ImportLibraryArtifact));
614
0
        }
615
0
        LogMessage("adding file level dep on imported target: " + importedLoc);
616
0
        execNode.ExecInput.emplace_back(std::move(importedLoc));
617
0
        continue;
618
0
      }
619
      // This CC uses some executable produced by another target. Add explicit
620
      // dep. Tested in "CustomCommand" test.
621
0
      if (util.Value.second) {
622
0
        if (utilTargetName != customCommand.GetTarget()) {
623
0
          LogMessage("Adding util dep: " + utilTargetName);
624
0
          execNode.PreBuildDependencies.emplace(utilTargetName);
625
0
        }
626
0
      }
627
0
    }
628
629
0
    execs.Alias.PreBuildDependencies.emplace(execNode.Name);
630
631
0
    LogMessage(cmStrCat("cmdLines size ", cmdLines.size()));
632
633
0
    if (!cmdLines.empty()) {
634
0
      std::string const scriptFileName = GetScriptFilename(execName);
635
0
      cmsys::ofstream scriptFile(scriptFileName.c_str());
636
637
0
      AddOutput(ccg, execNode);
638
0
      AddExecArguments(execNode, scriptFileName);
639
0
      AddCommentPrinting(cmdLines, ccg);
640
641
0
      WriteScriptProlog(scriptFile);
642
0
      WriteCmdsToFile(scriptFile, cmdLines);
643
0
      WriteScriptEpilog(scriptFile);
644
0
    }
645
646
0
    if (buildStep == FastbuildBuildStep::POST_BUILD) {
647
      // Execute POST_BUILD in order in which they are declared.
648
      // Tested in "complex" test.
649
0
      for (auto& exec : execs.Nodes) {
650
0
        execNode.PreBuildDependencies.emplace(exec.Name);
651
0
      }
652
0
    }
653
0
    for (auto const& out : execNode.OutputsAlias.PreBuildDependencies) {
654
0
      LogMessage(cmStrCat("Adding replace from ", out.Name, " to ", execName));
655
0
      OutputToExecName[out.Name] = execName;
656
0
    }
657
0
    execs.Nodes.emplace_back(std::move(execNode));
658
0
  }
659
0
  for (auto& exec : execs.Nodes) {
660
0
    for (auto& inputFile : exec.ExecInput) {
661
0
      auto const iter = OutputsToReplace.find(inputFile);
662
0
      if (iter != OutputsToReplace.end()) {
663
0
        LogMessage(
664
0
          cmStrCat("Replacing input: ", inputFile, " with ", iter->second));
665
0
        inputFile = iter->second;
666
0
      }
667
0
      auto const depIter = std::find_if(
668
0
        exec.PreBuildDependencies.begin(), exec.PreBuildDependencies.end(),
669
0
        [this](FastbuildTargetDep const& dep) {
670
0
          return !OutputToExecName[dep.Name].empty();
671
0
        });
672
0
      if (depIter != exec.PreBuildDependencies.end()) {
673
0
        LogMessage(cmStrCat("Replacing dep ", depIter->Name, " with ",
674
0
                            OutputToExecName[depIter->Name]));
675
0
        exec.PreBuildDependencies.emplace(OutputToExecName[depIter->Name]);
676
0
        exec.PreBuildDependencies.erase(depIter);
677
0
      }
678
0
    }
679
0
    if (exec.NeedsDepsCheckExec) {
680
0
      auto depsCheckExec = GetDepsCheckExec(exec);
681
0
      LogMessage("Adding deps check Exec: " + depsCheckExec.Name);
682
0
      exec.PreBuildDependencies.emplace(depsCheckExec.Name);
683
0
      this->GetGlobalGenerator()->AddTarget(std::move(depsCheckExec));
684
0
    }
685
0
  }
686
0
  return execs;
687
0
}
688
689
std::string cmFastbuildTargetGenerator::MakeCustomLauncher(
690
  cmCustomCommandGenerator const& ccg)
691
0
{
692
  // Copied from cmLocalNinjaGenerator::MakeCustomLauncher.
693
0
  cmValue property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM");
694
695
0
  if (!cmNonempty(property_value)) {
696
0
    return std::string();
697
0
  }
698
699
  // Expand rule variables referenced in the given launcher command.
700
0
  cmRulePlaceholderExpander::RuleVariables vars;
701
702
0
  std::string output;
703
0
  std::vector<std::string> const& outputs = ccg.GetOutputs();
704
0
  for (size_t i = 0; i < outputs.size(); ++i) {
705
0
    output =
706
0
      cmStrCat(output,
707
0
               this->LocalGenerator->ConvertToOutputFormat(
708
0
                 ccg.GetWorkingDirectory().empty()
709
0
                   ? this->LocalGenerator->MaybeRelativeToCurBinDir(outputs[i])
710
0
                   : outputs[i],
711
0
                 cmOutputConverter::SHELL));
712
0
    if (i != outputs.size() - 1) {
713
0
      output = cmStrCat(output, ',');
714
0
    }
715
0
  }
716
0
  vars.Output = output.c_str();
717
0
  vars.Role = ccg.GetCC().GetRole().c_str();
718
0
  vars.CMTargetName = ccg.GetCC().GetTarget().c_str();
719
0
  vars.Config = ccg.GetOutputConfig().c_str();
720
721
0
  auto rulePlaceholderExpander =
722
0
    this->LocalGenerator->CreateRulePlaceholderExpander();
723
724
0
  std::string launcher = *property_value;
725
0
  rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, launcher,
726
0
                                               vars);
727
0
  if (!launcher.empty()) {
728
0
    launcher += " ";
729
0
  }
730
731
0
  LogMessage("CC Launcher: " + launcher);
732
0
  return launcher;
733
0
}
734
735
std::string cmFastbuildTargetGenerator::GetTargetName() const
736
0
{
737
0
  if (this->GeneratorTarget->GetType() == cmStateEnums::GLOBAL_TARGET) {
738
0
    return this->GetGlobalGenerator()->GetTargetName(GeneratorTarget);
739
0
  }
740
0
  return this->GeneratorTarget->GetName();
741
0
}
742
743
cmGeneratorTarget::Names cmFastbuildTargetGenerator::DetectOutput() const
744
0
{
745
0
  if (GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
746
0
    return GeneratorTarget->GetExecutableNames(Config);
747
0
  }
748
0
  return GeneratorTarget->GetLibraryNames(Config);
749
0
}
750
751
void cmFastbuildTargetGenerator::AddObjectDependencies(
752
  FastbuildTarget& fastbuildTarget,
753
  std::vector<std::string>& allObjectDepends) const
754
0
{
755
0
  auto const FindObjListWhichOutputs = [&fastbuildTarget](
756
0
                                         std::string const& output) {
757
0
    for (FastbuildObjectListNode const& objList :
758
0
         fastbuildTarget.ObjectListNodes) {
759
0
      if (objList.ObjectOutputs.find(output) != objList.ObjectOutputs.end()) {
760
0
        return objList.Name;
761
0
      }
762
0
    }
763
0
    return std::string{};
764
0
  };
765
766
0
  for (FastbuildObjectListNode& objList : fastbuildTarget.ObjectListNodes) {
767
0
    for (auto const& objDep : objList.ObjectDepends) {
768
      // Check if there is another object list which outputs (OBJECT_OUTPUTS)
769
      // something that this object list needs (OBJECT_DEPENDS).
770
0
      auto anotherObjList = FindObjListWhichOutputs(objDep);
771
0
      if (!anotherObjList.empty()) {
772
0
        LogMessage("Adding explicit <OBJECT_DEPENDS> dep: " + anotherObjList);
773
0
        allObjectDepends.emplace_back(anotherObjList);
774
0
        objList.PreBuildDependencies.emplace(std::move(anotherObjList));
775
776
0
      } else {
777
0
        LogMessage("Adding <OBJECT_DEPENDS> dep: " + objDep);
778
0
        allObjectDepends.emplace_back(objDep);
779
0
        objList.PreBuildDependencies.emplace(objDep);
780
0
      }
781
0
    }
782
0
  }
783
0
  cmGlobalFastbuildGenerator::TopologicalSort(fastbuildTarget.ObjectListNodes);
784
0
}
785
786
void cmFastbuildTargetGenerator::AddLinkerNodeDependencies(
787
  FastbuildTarget& fastbuildTarget)
788
0
{
789
0
  for (auto& linkerNode : fastbuildTarget.LinkerNode) {
790
0
    if (!fastbuildTarget.PreLinkExecNodes.Nodes.empty()) {
791
0
      linkerNode.PreBuildDependencies.emplace(
792
0
        fastbuildTarget.Name + FASTBUILD_PRE_LINK_ALIAS_POSTFIX);
793
0
    }
794
0
  }
795
0
}
796
797
std::string cmFastbuildTargetGenerator::GetClangTidyReplacementsFilePath(
798
  std::string const& directory, cmSourceFile const& source,
799
  std::string const& /*config*/) const
800
0
{
801
802
0
  std::string objectDir =
803
0
    this->ConvertToFastbuildPath(this->GeneratorTarget->GetSupportDirectory());
804
0
  std::string const& objectName =
805
0
    this->GeneratorTarget->GetObjectName(&source);
806
0
  std::string path =
807
0
    cmStrCat(directory, '/', objectDir, '/', objectName, ".yaml");
808
0
  LogMessage("ClangTidy replacements file: " + path);
809
0
  return path;
810
0
}
811
812
void cmFastbuildTargetGenerator::AddIncludeFlags(std::string& languageFlags,
813
                                                 std::string const& language,
814
                                                 std::string const&)
815
0
{
816
0
  std::vector<std::string> includes;
817
0
  this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
818
0
                                              language, Config);
819
  // Add include directory flags.
820
0
  std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
821
0
    includes, this->GeneratorTarget, language, Config, false);
822
823
0
  this->LocalGenerator->AppendFlags(languageFlags, includeFlags);
824
0
}
825
826
std::string cmFastbuildTargetGenerator::GetName()
827
0
{
828
0
  return GeneratorTarget->GetName();
829
0
}
830
831
std::string cmFastbuildTargetGenerator::ConvertToFastbuildPath(
832
  std::string const& path) const
833
0
{
834
0
  return GetGlobalGenerator()->ConvertToFastbuildPath(path);
835
0
}
836
837
cmGlobalFastbuildGenerator* cmFastbuildTargetGenerator::GetGlobalGenerator()
838
  const
839
0
{
840
0
  return this->LocalGenerator->GetGlobalFastbuildGenerator();
841
0
}
842
843
void cmFastbuildTargetGenerator::AdditionalCleanFiles()
844
0
{
845
0
  if (cmValue prop_value =
846
0
        this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
847
0
    auto* lg = this->LocalGenerator;
848
0
    cmList cleanFiles(cmGeneratorExpression::Evaluate(*prop_value, lg, Config,
849
0
                                                      this->GeneratorTarget));
850
0
    std::string const& binaryDir = lg->GetCurrentBinaryDirectory();
851
0
    auto* gg = lg->GetGlobalFastbuildGenerator();
852
0
    for (auto const& cleanFile : cleanFiles) {
853
      // Support relative paths
854
0
      gg->AddFileToClean(gg->ConvertToFastbuildPath(
855
0
        cmSystemTools::CollapseFullPath(cleanFile, binaryDir)));
856
0
    }
857
0
  }
858
0
}