Coverage Report

Created: 2026-10-28 06:38

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