Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmCommonTargetGenerator.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 "cmCommonTargetGenerator.h"
4
5
#include <algorithm>
6
#include <utility>
7
8
#include <cm/filesystem>
9
#include <cm/string_view>
10
#include <cmext/algorithm>
11
#include <cmext/string_view>
12
13
#include "cmComputeLinkInformation.h"
14
#include "cmGenExContext.h"
15
#include "cmGeneratorExpression.h"
16
#include "cmGeneratorExpressionDAGChecker.h"
17
#include "cmGeneratorTarget.h"
18
#include "cmGlobalCommonGenerator.h"
19
#include "cmGlobalGenerator.h"
20
#include "cmList.h"
21
#include "cmListFileCache.h"
22
#include "cmLocalCommonGenerator.h"
23
#include "cmLocalGenerator.h"
24
#include "cmMakefile.h"
25
#include "cmMessageType.h"
26
#include "cmOutputConverter.h"
27
#include "cmRange.h"
28
#include "cmSourceFile.h"
29
#include "cmState.h"
30
#include "cmStringAlgorithms.h"
31
#include "cmSystemTools.h"
32
#include "cmTargetTypes.h"
33
#include "cmValue.h"
34
35
namespace {
36
bool LinkedTargetHasModules(cmGeneratorTarget const* target,
37
                            std::string const& lang, std::string const& config,
38
                            cmGlobalCommonGenerator const* gg,
39
                            cmGeneratorTarget const* currentTarget)
40
0
{
41
0
  if (!target) {
42
0
    return false;
43
0
  }
44
0
  if (lang == "CXX"_s && target->HasCxxImportModuleErrors()) {
45
0
    return true;
46
0
  }
47
0
  if (target->IsImported()) {
48
0
    return false;
49
0
  }
50
0
  if (!gg->TargetOrderIndexLess(target, currentTarget)) {
51
0
    return false;
52
0
  }
53
0
  if (target->GetType() == cm::TargetType::INTERFACE_LIBRARY &&
54
0
      !target->IsSynthetic()) {
55
0
    return false;
56
0
  }
57
0
  if (lang == "CXX"_s && target->HaveCxx20ModuleSources()) {
58
0
    return true;
59
0
  }
60
0
  if (lang == "Fortran"_s && target->HaveFortranSources(config)) {
61
0
    return true;
62
0
  }
63
0
  return false;
64
0
}
65
}
66
67
cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt)
68
0
  : GeneratorTarget(gt)
69
0
  , Makefile(gt->Makefile)
70
  , LocalCommonGenerator(
71
0
      static_cast<cmLocalCommonGenerator*>(gt->LocalGenerator))
72
0
  , GlobalCommonGenerator(static_cast<cmGlobalCommonGenerator*>(
73
0
      gt->LocalGenerator->GetGlobalGenerator()))
74
0
  , ConfigNames(this->LocalCommonGenerator->GetConfigNames())
75
0
{
76
0
}
77
78
0
cmCommonTargetGenerator::~cmCommonTargetGenerator() = default;
79
80
std::vector<std::string> const& cmCommonTargetGenerator::GetConfigNames() const
81
0
{
82
0
  return this->ConfigNames;
83
0
}
84
85
cmValue cmCommonTargetGenerator::GetFeature(std::string const& feature,
86
                                            std::string const& config)
87
0
{
88
0
  return this->GeneratorTarget->GetFeature(feature, config);
89
0
}
90
91
void cmCommonTargetGenerator::AppendFortranFormatFlags(
92
  std::string& flags, cmSourceFile const& source)
93
0
{
94
0
  std::string const srcfmt = source.GetSafeProperty("Fortran_FORMAT");
95
0
  cmOutputConverter::FortranFormat format =
96
0
    cmOutputConverter::GetFortranFormat(srcfmt);
97
0
  if (format == cmOutputConverter::FortranFormatNone) {
98
0
    std::string const& tgtfmt =
99
0
      this->GeneratorTarget->GetSafeProperty("Fortran_FORMAT");
100
0
    format = cmOutputConverter::GetFortranFormat(tgtfmt);
101
0
  }
102
0
  char const* var = nullptr;
103
0
  switch (format) {
104
0
    case cmOutputConverter::FortranFormatFixed:
105
0
      var = "CMAKE_Fortran_FORMAT_FIXED_FLAG";
106
0
      break;
107
0
    case cmOutputConverter::FortranFormatFree:
108
0
      var = "CMAKE_Fortran_FORMAT_FREE_FLAG";
109
0
      break;
110
0
    default:
111
0
      break;
112
0
  }
113
0
  if (var) {
114
0
    this->LocalCommonGenerator->AppendFlags(
115
0
      flags, this->Makefile->GetSafeDefinition(var));
116
0
  }
117
0
}
118
119
void cmCommonTargetGenerator::AppendFortranPreprocessFlags(
120
  std::string& flags, cmSourceFile const& source,
121
  PreprocessFlagsRequired requires_pp)
122
0
{
123
0
  std::string const srcpp = source.GetSafeProperty("Fortran_PREPROCESS");
124
0
  cmOutputConverter::FortranPreprocess preprocess =
125
0
    cmOutputConverter::GetFortranPreprocess(srcpp);
126
0
  if (preprocess == cmOutputConverter::FortranPreprocess::Unset) {
127
0
    std::string const& tgtpp =
128
0
      this->GeneratorTarget->GetSafeProperty("Fortran_PREPROCESS");
129
0
    preprocess = cmOutputConverter::GetFortranPreprocess(tgtpp);
130
0
  }
131
0
  char const* var = nullptr;
132
0
  switch (preprocess) {
133
0
    case cmOutputConverter::FortranPreprocess::Needed:
134
0
      if (requires_pp == PreprocessFlagsRequired::YES) {
135
0
        var = "CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON";
136
0
      }
137
0
      break;
138
0
    case cmOutputConverter::FortranPreprocess::NotNeeded:
139
0
      var = "CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF";
140
0
      break;
141
0
    default:
142
0
      break;
143
0
  }
144
0
  if (var) {
145
0
    this->LocalCommonGenerator->AppendCompileOptions(
146
0
      flags, this->Makefile->GetSafeDefinition(var));
147
0
  }
148
0
}
149
150
std::string cmCommonTargetGenerator::GetFlags(std::string const& l,
151
                                              std::string const& config,
152
                                              std::string const& arch)
153
0
{
154
0
  std::string const key = cmStrCat(config, arch);
155
156
0
  auto i = this->Configs[key].FlagsByLanguage.find(l);
157
0
  if (i == this->Configs[key].FlagsByLanguage.end()) {
158
0
    std::string flags;
159
160
0
    this->LocalCommonGenerator->GetTargetCompileFlags(this->GeneratorTarget,
161
0
                                                      config, l, flags, arch);
162
163
0
    ByLanguageMap::value_type entry(l, flags);
164
0
    i = this->Configs[key].FlagsByLanguage.insert(entry).first;
165
0
  }
166
0
  return i->second;
167
0
}
168
169
std::string cmCommonTargetGenerator::GetDefines(std::string const& l,
170
                                                std::string const& config)
171
0
{
172
0
  auto i = this->Configs[config].DefinesByLanguage.find(l);
173
0
  if (i == this->Configs[config].DefinesByLanguage.end()) {
174
0
    std::set<std::string> defines;
175
0
    this->LocalCommonGenerator->GetTargetDefines(this->GeneratorTarget, config,
176
0
                                                 l, defines);
177
178
0
    std::string definesString;
179
0
    this->LocalCommonGenerator->JoinDefines(defines, definesString, l);
180
181
0
    ByLanguageMap::value_type entry(l, definesString);
182
0
    i = this->Configs[config].DefinesByLanguage.insert(entry).first;
183
0
  }
184
0
  return i->second;
185
0
}
186
187
std::string cmCommonTargetGenerator::GetIncludes(std::string const& l,
188
                                                 std::string const& config)
189
0
{
190
0
  auto i = this->Configs[config].IncludesByLanguage.find(l);
191
0
  if (i == this->Configs[config].IncludesByLanguage.end()) {
192
0
    std::string includes;
193
0
    this->AddIncludeFlags(includes, l, config);
194
0
    ByLanguageMap::value_type entry(l, includes);
195
0
    i = this->Configs[config].IncludesByLanguage.insert(entry).first;
196
0
  }
197
0
  return i->second;
198
0
}
199
200
cmCommonTargetGenerator::LinkedTargetDirs
201
cmCommonTargetGenerator::GetLinkedTargetDirectories(
202
  std::string const& lang, std::string const& config) const
203
0
{
204
0
  LinkedTargetDirs dirs;
205
0
  std::set<cmGeneratorTarget const*> forward_emitted;
206
0
  std::set<cmGeneratorTarget const*> direct_emitted;
207
0
  cmGlobalCommonGenerator* const gg = this->GlobalCommonGenerator;
208
209
0
  enum class Forwarding
210
0
  {
211
0
    Yes,
212
0
    No
213
0
  };
214
215
0
  if (cmComputeLinkInformation* cli =
216
0
        this->GeneratorTarget->GetLinkInformation(config)) {
217
218
0
    auto findSyntheticTarget =
219
0
      [this, &config,
220
0
       cli](cmGeneratorTarget const* linkee) -> cmGeneratorTarget const* {
221
0
      if (!linkee) {
222
0
        return nullptr;
223
0
      }
224
225
      // Check the map of direct synthetic dependencies for a substitute
226
0
      auto const& synthDeps = this->GeneratorTarget->GetSyntheticDeps(config);
227
0
      auto it = synthDeps.find(linkee);
228
0
      if (it != synthDeps.end() && !it->second.empty()) {
229
0
        return *it->second.begin();
230
0
      }
231
232
      // Check linked targets to find synthetic targets for transitive deps
233
0
      std::vector<cmGeneratorTarget const*> pending;
234
0
      std::set<cmGeneratorTarget const*> visited;
235
0
      for (auto const& dep : synthDeps) {
236
0
        for (auto const* synth : dep.second) {
237
0
          if (synth && visited.insert(synth).second) {
238
0
            pending.push_back(synth);
239
0
          }
240
0
        }
241
0
      }
242
243
      // Also seed the search from direct linked targets that have
244
      // CXX20 module sources, in case they hold synthetic deps for
245
      // transitive dependencies that we need to inherit. Skip targets
246
      // that already have synthetic substitutes in our own synthDeps
247
      // (their transitive deps will be explored via the substitute).
248
0
      for (auto const& item : cli->GetItems()) {
249
0
        if (item.Target && item.Target->HaveCxx20ModuleSources() &&
250
0
            synthDeps.find(item.Target) == synthDeps.end() &&
251
0
            visited.insert(item.Target).second) {
252
0
          pending.push_back(item.Target);
253
0
        }
254
0
      }
255
256
0
      while (!pending.empty()) {
257
0
        auto const* current = pending.back();
258
0
        pending.pop_back();
259
0
        auto const& transitiveSynthDeps = current->GetSyntheticDeps(config);
260
0
        auto itLinkeeSynth = transitiveSynthDeps.find(linkee);
261
0
        if (itLinkeeSynth != transitiveSynthDeps.end() &&
262
0
            !itLinkeeSynth->second.empty()) {
263
0
          return *itLinkeeSynth->second.begin();
264
0
        }
265
0
        for (auto const& entry : transitiveSynthDeps) {
266
0
          for (auto const* synth : entry.second) {
267
0
            if (synth && visited.insert(synth).second) {
268
0
              pending.push_back(synth);
269
0
            }
270
0
          }
271
0
        }
272
0
      }
273
274
0
      return nullptr;
275
0
    };
276
277
0
    auto addLinkedTarget = [this, &lang, &config, &dirs, &direct_emitted,
278
0
                            &forward_emitted, &findSyntheticTarget,
279
0
                            gg](cmGeneratorTarget const* linkee,
280
0
                                Forwarding forward) {
281
      // Check if the linkee has a synthetic target to use for importing
282
0
      cmGeneratorTarget const* mappedLinkee = linkee;
283
0
      if (auto const* synth = findSyntheticTarget(linkee)) {
284
0
        mappedLinkee = synth;
285
0
      }
286
287
0
      if (mappedLinkee &&
288
0
          LinkedTargetHasModules(mappedLinkee, lang, config, gg,
289
0
                                 this->GeneratorTarget)) {
290
0
        cmLocalGenerator* lg = mappedLinkee->GetLocalGenerator();
291
0
        std::string di = mappedLinkee->GetSupportDirectory();
292
0
        if (lg->GetGlobalGenerator()->IsMultiConfig()) {
293
0
          di = cmStrCat(std::move(di), '/', config);
294
0
        }
295
0
        if (forward == Forwarding::Yes &&
296
0
            forward_emitted.insert(mappedLinkee).second) {
297
0
          dirs.Forward.push_back(di);
298
0
        }
299
0
        if (direct_emitted.insert(mappedLinkee).second) {
300
0
          dirs.Direct.emplace_back(di);
301
0
        }
302
0
      }
303
0
    };
304
0
    for (auto const& item : cli->GetItems()) {
305
0
      if (item.Target) {
306
0
        addLinkedTarget(item.Target, Forwarding::No);
307
0
      } else if (item.ObjectSource && lang == "Fortran"_s
308
                 /* Object source files do not have a language associated with
309
                    them. */
310
0
                 /* && item.ObjectSource->GetLanguage() == "Fortran"_s*/) {
311
        // Fortran modules provided by `$<TARGET_OBJECTS>` as linked items
312
        // should be collated for use in this target.
313
0
        addLinkedTarget(this->LocalCommonGenerator->FindGeneratorTargetToUse(
314
0
                          item.ObjectSource->GetObjectLibrary()),
315
0
                        Forwarding::Yes);
316
0
      }
317
0
    }
318
0
    for (cmGeneratorTarget const* target : cli->GetExternalObjectTargets()) {
319
0
      addLinkedTarget(target, Forwarding::No);
320
0
    }
321
0
    if (lang == "Fortran"_s) {
322
      // Fortran modules provided by `$<TARGET_OBJECTS>` as sources should be
323
      // collated for use in this target.
324
0
      for (cmGeneratorTarget const* target :
325
0
           this->GeneratorTarget->GetSourceObjectLibraries(config)) {
326
0
        addLinkedTarget(target, Forwarding::Yes);
327
0
      }
328
0
    }
329
0
  }
330
0
  return dirs;
331
0
}
332
333
std::string cmCommonTargetGenerator::ComputeTargetCompilePDB(
334
  std::string const& config) const
335
0
{
336
0
  std::string compilePdbPath;
337
0
  if (this->GeneratorTarget->GetType() > cm::TargetType::OBJECT_LIBRARY) {
338
0
    return compilePdbPath;
339
0
  }
340
341
0
  compilePdbPath = this->GeneratorTarget->GetCompilePDBPath(config);
342
0
  if (compilePdbPath.empty()) {
343
    // Match VS default: `$(IntDir)vc$(PlatformToolsetVersion).pdb`.
344
    // A trailing slash tells the toolchain to add its default file name.
345
0
    compilePdbPath = this->GeneratorTarget->GetSupportDirectory();
346
0
    if (this->GlobalCommonGenerator->IsMultiConfig()) {
347
0
      compilePdbPath = cmStrCat(std::move(compilePdbPath), '/', config);
348
0
    }
349
0
    compilePdbPath += '/';
350
0
    if (this->GeneratorTarget->GetType() == cm::TargetType::STATIC_LIBRARY) {
351
      // Match VS default for static libs: `$(IntDir)$(ProjectName).pdb`.
352
0
      compilePdbPath = cmStrCat(std::move(compilePdbPath),
353
0
                                this->GeneratorTarget->GetName(), ".pdb");
354
0
    }
355
0
  }
356
357
0
  return compilePdbPath;
358
0
}
359
360
std::string cmCommonTargetGenerator::GetManifests(std::string const& config)
361
0
{
362
0
  std::vector<cmSourceFile const*> manifest_srcs;
363
0
  this->GeneratorTarget->GetManifests(manifest_srcs, config);
364
365
0
  std::vector<std::string> manifests;
366
0
  manifests.reserve(manifest_srcs.size());
367
368
0
  std::string const lang = this->GeneratorTarget->GetLinkerLanguage(config);
369
0
  std::string const manifestFlag = this->Makefile->GetDefinition(
370
0
    cmStrCat("CMAKE_", lang, "_LINKER_MANIFEST_FLAG"));
371
0
  for (cmSourceFile const* manifest_src : manifest_srcs) {
372
0
    manifests.push_back(manifestFlag +
373
0
                        this->LocalCommonGenerator->ConvertToOutputFormat(
374
0
                          this->LocalCommonGenerator->MaybeRelativeToWorkDir(
375
0
                            manifest_src->GetFullPath()),
376
0
                          cmOutputConverter::SHELL));
377
0
  }
378
379
0
  return cmJoin(manifests, " ");
380
0
}
381
382
std::string cmCommonTargetGenerator::GetAIXExports(std::string const&)
383
0
{
384
0
  std::string aixExports;
385
0
  if (this->GeneratorTarget->IsAIX()) {
386
0
    if (cmValue exportAll =
387
0
          this->GeneratorTarget->GetProperty("AIX_EXPORT_ALL_SYMBOLS")) {
388
0
      if (exportAll.IsOff()) {
389
0
        aixExports = "-n";
390
0
      }
391
0
    }
392
0
  }
393
0
  return aixExports;
394
0
}
395
396
void cmCommonTargetGenerator::AppendOSXVerFlag(std::string& flags,
397
                                               std::string const& lang,
398
                                               char const* name, bool so)
399
0
{
400
  // Lookup the flag to specify the version.
401
0
  std::string const fvar =
402
0
    cmStrCat("CMAKE_", lang, "_OSX_", name, "_VERSION_FLAG");
403
0
  cmValue flag = this->Makefile->GetDefinition(fvar);
404
405
  // Skip if no such flag.
406
0
  if (!flag) {
407
0
    return;
408
0
  }
409
410
  // Lookup the target version information.
411
0
  int major;
412
0
  int minor;
413
0
  int patch;
414
0
  std::string const prop = cmStrCat("MACHO_", name, "_VERSION");
415
0
  std::string const fallbackProp = so ? "SOVERSION" : "VERSION";
416
0
  this->GeneratorTarget->GetTargetVersionFallback(prop, fallbackProp, major,
417
0
                                                  minor, patch);
418
0
  if (major > 0 || minor > 0 || patch > 0) {
419
    // Append the flag since a non-zero version is specified.
420
0
    std::string vflag = cmStrCat(*flag, major, '.', minor, '.', patch);
421
0
    this->LocalCommonGenerator->AppendFlags(flags, vflag);
422
0
  }
423
0
}
424
425
std::vector<std::string> cmCommonTargetGenerator::GetCompilerLauncher(
426
  std::string const& lang, std::string const& config)
427
0
{
428
0
  std::vector<std::string> compilerLauncher;
429
0
  if (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" ||
430
0
      lang == "HIP" || lang == "ISPC" || lang == "OBJC" || lang == "OBJCXX" ||
431
0
      lang == "SYCL" || lang == "Rust") {
432
    // FIXME(#27402,#28110): The FASTBuild generator's limitations w.r.t.
433
    // shell escaping and command line ordering means we can't support
434
    // wrappers for now.
435
0
    std::string const generatorName =
436
0
      this->GeneratorTarget->GetLocalGenerator()
437
0
        ->GetGlobalGenerator()
438
0
        ->GetName();
439
0
    if (generatorName.find("FASTBuild") == std::string::npos) {
440
      // Wrappers go outside of the launcher.
441
0
      compilerLauncher = this->ParseWrappers(
442
0
        this->GeneratorTarget->GetCompileWrappers(config, lang),
443
0
        "CMAKE_COMPILE_WRAPPER_"_s, "COMPILE_WRAPPERS"_s,
444
0
        this->GeneratorTarget->GetName(), config, lang);
445
0
    }
446
447
0
    std::string const propName = cmStrCat(lang, "_COMPILER_LAUNCHER");
448
0
    cmValue cLauncherValue = this->GeneratorTarget->GetProperty(propName);
449
0
    std::string const evaluatedCLauncher = cmGeneratorExpression::Evaluate(
450
0
      *cLauncherValue, this->GeneratorTarget->GetLocalGenerator(), config,
451
0
      this->GeneratorTarget, nullptr, this->GeneratorTarget, lang);
452
0
    if (!evaluatedCLauncher.empty()) {
453
0
      cm::append(compilerLauncher,
454
0
                 cmList{ evaluatedCLauncher, cmList::EmptyElements::Yes });
455
0
    }
456
0
  }
457
0
  return compilerLauncher;
458
0
}
459
460
std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
461
  cmSourceFile const& source, std::vector<std::string>& compilerLauncher,
462
  std::string const& cmakeCmd, std::string const& config,
463
  std::function<std::string(std::string const&)> const& pathConverter)
464
0
{
465
0
  std::string const lang =
466
0
    this->GeneratorTarget->GetSourceFileLanguage(&source, config);
467
0
  std::string tidy;
468
0
  std::string iwyu;
469
0
  std::string cpplint;
470
0
  std::string cppcheck;
471
0
  std::string icstat;
472
0
  std::string pvs;
473
474
0
  auto evaluateProp = [&](std::string const& prop) -> std::string {
475
0
    cmValue const value = this->GeneratorTarget->GetProperty(prop);
476
0
    if (!value) {
477
0
      return std::string{};
478
0
    }
479
0
    std::string evaluatedProp = cmGeneratorExpression::Evaluate(
480
0
      *value, this->GeneratorTarget->GetLocalGenerator(), config,
481
0
      this->GeneratorTarget, nullptr, this->GeneratorTarget, lang);
482
0
    return evaluatedProp;
483
0
  };
484
0
  std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY");
485
0
  tidy = evaluateProp(tidy_prop);
486
487
0
  if (lang == "C" || lang == "CXX") {
488
0
    std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE");
489
0
    iwyu = evaluateProp(iwyu_prop);
490
491
0
    std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT");
492
0
    cpplint = evaluateProp(cpplint_prop);
493
494
0
    std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK");
495
0
    cppcheck = evaluateProp(cppcheck_prop);
496
497
0
    std::string const icstat_prop = cmStrCat(lang, "_ICSTAT");
498
0
    icstat = evaluateProp(icstat_prop);
499
500
0
    std::string const pvs_prop = cmStrCat(lang, "_PVS_STUDIO");
501
0
    pvs = evaluateProp(pvs_prop);
502
0
  }
503
504
0
  if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
505
0
      cmNonempty(cppcheck) || cmNonempty(icstat) || cmNonempty(pvs)) {
506
0
    std::string code_check = cmakeCmd + " -E __run_co_compile";
507
0
    if (!compilerLauncher.empty()) {
508
      // In __run_co_compile case the launcher command is supplied
509
      // via --launcher=<list> and consumed
510
0
      for (std::string& arg : compilerLauncher) {
511
0
        cmSystemTools::ReplaceString(arg, ";", "\\;");
512
0
      }
513
0
      code_check =
514
0
        cmStrCat(std::move(code_check), " --launcher=",
515
0
                 this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
516
0
                   cmJoin(compilerLauncher, ";")));
517
0
      compilerLauncher.clear();
518
0
    }
519
0
    if (cmNonempty(iwyu)) {
520
      // Only add --driver-mode if it is not already specified, as adding
521
      // it unconditionally might override a user-specified driver-mode
522
0
      if (iwyu.find("--driver-mode=") == std::string::npos) {
523
0
        cmValue const p = this->Makefile->GetDefinition(
524
0
          cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE"));
525
0
        std::string driverMode;
526
527
0
        if (cmNonempty(p)) {
528
0
          driverMode = *p;
529
0
        } else {
530
0
          driverMode = lang == "C" ? "gcc" : "g++";
531
0
        }
532
533
0
        code_check =
534
0
          cmStrCat(std::move(code_check), " --iwyu=",
535
0
                   this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
536
0
                     cmStrCat(iwyu, ";--driver-mode=", driverMode)));
537
0
      } else {
538
0
        code_check = cmStrCat(
539
0
          std::move(code_check), " --iwyu=",
540
0
          this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(iwyu));
541
0
      }
542
0
    }
543
0
    if (cmNonempty(tidy)) {
544
0
      code_check += " --tidy=";
545
0
      cmValue const p = this->Makefile->GetDefinition(
546
0
        cmStrCat("CMAKE_", lang, "_CLANG_TIDY_DRIVER_MODE"));
547
0
      std::string driverMode;
548
0
      if (cmNonempty(p)) {
549
0
        driverMode = *p;
550
0
      } else {
551
0
        driverMode = lang == "C" ? "gcc" : "g++";
552
0
      }
553
554
0
      std::string const generatorName =
555
0
        this->GeneratorTarget->GetLocalGenerator()
556
0
          ->GetGlobalGenerator()
557
0
          ->GetName();
558
0
      std::string const clangTidyExportFixedDir =
559
0
        this->GeneratorTarget->GetClangTidyExportFixesDirectory(lang);
560
0
      std::string fixesFile = this->GetClangTidyReplacementsFilePath(
561
0
        clangTidyExportFixedDir, source, config);
562
0
      std::string exportFixes;
563
0
      if (!clangTidyExportFixedDir.empty()) {
564
0
        this->GlobalCommonGenerator->AddClangTidyExportFixesDir(
565
0
          clangTidyExportFixedDir);
566
0
      }
567
0
      if (generatorName.find("Make") != std::string::npos) {
568
0
        if (!clangTidyExportFixedDir.empty()) {
569
0
          this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile);
570
0
          cmSystemTools::MakeDirectory(
571
0
            cmSystemTools::GetFilenamePath(fixesFile));
572
0
          fixesFile = this->GeneratorTarget->GetLocalGenerator()
573
0
                        ->MaybeRelativeToCurBinDir(fixesFile);
574
0
          exportFixes = cmStrCat(";--export-fixes=", fixesFile);
575
0
        }
576
0
        code_check +=
577
0
          this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
578
0
            cmStrCat(tidy, ";--extra-arg-before=--driver-mode=", driverMode,
579
0
                     exportFixes));
580
0
      } else if (generatorName.find("Ninja") != std::string::npos ||
581
0
                 generatorName.find("FASTBuild") != std::string::npos) {
582
0
        if (!clangTidyExportFixedDir.empty()) {
583
0
          this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile);
584
0
          cmSystemTools::MakeDirectory(
585
0
            cmSystemTools::GetFilenamePath(fixesFile));
586
0
          if (pathConverter) {
587
0
            fixesFile = pathConverter(fixesFile);
588
0
          }
589
0
          exportFixes = cmStrCat(";--export-fixes=", fixesFile);
590
0
        }
591
0
        code_check +=
592
0
          this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
593
0
            cmStrCat(tidy, ";--extra-arg-before=--driver-mode=", driverMode,
594
0
                     exportFixes));
595
0
      }
596
0
    }
597
0
    if (cmNonempty(pvs)) {
598
0
      cmMakefile* mf =
599
0
        this->GeneratorTarget->GetLocalGenerator()->GetMakefile();
600
0
      std::string extraPvsArgs;
601
0
      if (lang == "CXX") {
602
0
        extraPvsArgs =
603
0
          cmStrCat(";--cxx;", mf->GetDefinition("CMAKE_CXX_COMPILER"));
604
0
      } else if (lang == "C") {
605
0
        extraPvsArgs =
606
0
          cmStrCat(";--cc;", mf->GetDefinition("CMAKE_C_COMPILER"));
607
0
      }
608
      // cocompile args
609
0
      code_check = cmStrCat(
610
0
        std::move(code_check), " --pvs-studio=",
611
0
        this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
612
0
          cmStrCat(pvs, extraPvsArgs)),
613
0
        " --object=",
614
0
        this->GeneratorTarget->GetLocalGenerator()->ConvertToOutputFormat(
615
0
          cmSystemTools::CollapseFullPath(
616
0
            cmStrCat(this->GeneratorTarget->GetObjectDirectory(config), '/',
617
0
                     this->GeneratorTarget->GetObjectName(&source))),
618
0
          cmOutputConverter::SHELL));
619
0
    }
620
0
    if (cmNonempty(cpplint)) {
621
0
      code_check = cmStrCat(
622
0
        std::move(code_check), " --cpplint=",
623
0
        this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(cpplint));
624
0
    }
625
0
    if (cmNonempty(cppcheck)) {
626
0
      code_check = cmStrCat(
627
0
        std::move(code_check), " --cppcheck=",
628
0
        this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(cppcheck));
629
0
    }
630
0
    if (cmNonempty(icstat)) {
631
      // Unless specified otherwise via CMAKE_<LANG>_ICSTAT,
632
      // populate the icstat command line using default options
633
      // for its mandatory parameters.
634
0
      std::string checksParam{};
635
0
      if (icstat.find("--checks=") == std::string::npos) {
636
0
        std::string const checksFile{ "cstat_sel_checks.txt" };
637
0
        checksParam = cmStrCat(";--checks=", checksFile);
638
0
      }
639
0
      std::string dbParam{};
640
0
      if (icstat.find("--db=") == std::string::npos) {
641
0
        std::string const dbFile{ "cstat.db" };
642
0
        dbParam = cmStrCat(";--db=", dbFile);
643
0
      }
644
0
      cm::string_view const analyzeCmd{ ";analyze" };
645
0
      code_check =
646
0
        cmStrCat(std::move(code_check), " --icstat=",
647
0
                 this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
648
0
                   cmStrCat(icstat, checksParam, dbParam, analyzeCmd)));
649
0
    }
650
0
    if (cmNonempty(tidy) || (cmNonempty(cpplint)) || (cmNonempty(cppcheck)) ||
651
0
        cmNonempty(pvs) || cmNonempty(icstat)) {
652
0
      code_check = cmStrCat(
653
0
        std::move(code_check), " --source=",
654
0
        this->GeneratorTarget->GetLocalGenerator()->ConvertToOutputFormat(
655
0
          source.GetFullPath(), cmOutputConverter::SHELL));
656
0
    }
657
0
    code_check += " -- ";
658
0
    return code_check;
659
0
  }
660
0
  return "";
661
0
}
662
663
std::vector<std::string> cmCommonTargetGenerator::GetLinkerLauncher(
664
  std::string const& config)
665
0
{
666
0
  std::vector<std::string> linkLauncher;
667
0
  std::string const lang = this->GeneratorTarget->GetLinkerLanguage(config);
668
669
  // FIXME(#27402,#28110): The FASTBuild generator's limitations w.r.t.
670
  // shell escaping and command line ordering means we can't support
671
  // wrappers for now.
672
0
  std::string const generatorName = this->GeneratorTarget->GetLocalGenerator()
673
0
                                      ->GetGlobalGenerator()
674
0
                                      ->GetName();
675
0
  if (generatorName.find("FASTBuild") == std::string::npos) {
676
    // Wrappers go outside of the launcher.
677
0
    linkLauncher =
678
0
      this->ParseWrappers(this->GeneratorTarget->GetLinkWrappers(config, lang),
679
0
                          "CMAKE_LINK_WRAPPER_"_s, "LINK_WRAPPERS"_s,
680
0
                          this->GeneratorTarget->GetName(), config, lang);
681
0
  }
682
683
0
  std::string const propName = cmStrCat(lang, "_LINKER_LAUNCHER");
684
0
  cmValue launcherProp = this->GeneratorTarget->GetProperty(propName);
685
0
  if (cmNonempty(launcherProp)) {
686
0
    cm::GenEx::Context context(this->LocalCommonGenerator, config, lang);
687
0
    cmGeneratorExpressionDAGChecker dagChecker{ this->GeneratorTarget,
688
0
                                                propName, nullptr, nullptr,
689
0
                                                context };
690
691
0
    std::string const evaluatedLinkLauncher = cmGeneratorExpression::Evaluate(
692
0
      *launcherProp, context.LG, context.Config, this->GeneratorTarget,
693
0
      &dagChecker, this->GeneratorTarget, context.Language);
694
0
    if (!evaluatedLinkLauncher.empty()) {
695
0
      cm::append(linkLauncher,
696
0
                 cmList{ evaluatedLinkLauncher, cmList::EmptyElements::Yes });
697
0
    }
698
0
  }
699
0
  return linkLauncher;
700
0
}
701
702
std::string cmCommonTargetGenerator::ConvertLauncherToShell(
703
  std::vector<std::string> const& launcher) const
704
0
{
705
0
  if (launcher.empty()) {
706
0
    return std::string{};
707
0
  }
708
709
0
  std::vector<std::string> args = launcher;
710
0
  args[0] = this->LocalCommonGenerator->ConvertToOutputFormat(
711
0
    args[0], cmOutputConverter::SHELL);
712
0
  for (std::string& arg : cmMakeRange(args.begin() + 1, args.end())) {
713
0
    arg = this->LocalCommonGenerator->EscapeForShell(arg);
714
0
  }
715
0
  return cmJoin(args, " ");
716
0
}
717
718
bool cmCommonTargetGenerator::HaveRequiredLanguages(
719
  std::vector<cmSourceFile const*> const& sources, std::string const& config,
720
  std::set<std::string>& languagesNeeded) const
721
0
{
722
0
  for (cmSourceFile const* sf : sources) {
723
0
    languagesNeeded.insert(
724
0
      this->GeneratorTarget->GetSourceFileLanguage(sf, config));
725
0
  }
726
727
0
  auto* makefile = this->Makefile;
728
0
  auto* state = makefile->GetState();
729
0
  auto unary = [&state, &makefile](std::string const& lang) -> bool {
730
0
    bool const valid = state->GetLanguageEnabled(lang);
731
0
    if (!valid) {
732
0
      makefile->IssueMessage(
733
0
        MessageType::FATAL_ERROR,
734
0
        cmStrCat("The language ", lang,
735
0
                 " was requested for compilation but was not enabled."
736
0
                 " To enable a language it needs to be specified in a"
737
0
                 " 'project' or 'enable_language' command in the root"
738
0
                 " CMakeLists.txt"));
739
0
    }
740
0
    return valid;
741
0
  };
742
0
  return std::all_of(languagesNeeded.cbegin(), languagesNeeded.cend(), unary);
743
0
}
744
745
void cmCommonTargetGenerator::ComputeRustFlagsForObjects(
746
  std::string& linkCrates, std::string& nativeObjects,
747
  std::vector<std::string> const& objects)
748
0
{
749
0
  auto const processObject = [&](std::string const& obj) {
750
0
    cm::filesystem::path const objPath(obj);
751
0
    if (objPath.extension() == ".rlib") {
752
      // Drop the "lib..." prefix and the ".rs" suffix. The prefix is required
753
      // by Rust on the crate rlib file, but is hidden from the user when using
754
      // the crate from Rust source code, so we drop it to be consistent with
755
      // common usage in Rust.
756
0
      std::string objStem = objPath.stem().string();
757
0
      objStem = objStem.substr(3, objStem.length() - 6);
758
0
      linkCrates = cmStrCat(std::move(linkCrates), " --extern=", objStem, '=',
759
0
                            this->LocalCommonGenerator->ConvertToOutputFormat(
760
0
                              obj, cmOutputConverter::SHELL));
761
0
    } else {
762
0
      nativeObjects =
763
0
        cmStrCat(std::move(nativeObjects), " -Clink-arg=",
764
0
                 this->LocalCommonGenerator->ConvertToOutputFormat(
765
0
                   obj, cmOutputConverter::SHELL));
766
0
    }
767
0
  };
768
0
  for (std::string const& obj : objects) {
769
0
    processObject(obj);
770
0
  }
771
0
}
772
773
std::vector<std::string> cmCommonTargetGenerator::ParseWrappers(
774
  std::vector<BT<std::string>> const& wrappers, cm::string_view prefix,
775
  cm::string_view targetPropName, std::string const& targetName,
776
  std::string const& config, std::string const& lang) const
777
0
{
778
0
  std::vector<std::string> wrapperCommandLine;
779
0
  if (wrappers.empty()) {
780
0
    return wrapperCommandLine;
781
0
  }
782
783
0
  cm::GenEx::Context context(this->LocalCommonGenerator, config, lang);
784
785
0
  for (BT<std::string> const& wrapper : wrappers) {
786
0
    std::string const wrapperPropName = cmStrCat(prefix, wrapper.Value);
787
0
    cmValue globalPropValue =
788
0
      this->Makefile->GetState()->GetGlobalProperty(wrapperPropName);
789
0
    if (!globalPropValue.IsSet()) {
790
0
      this->Makefile->IssueMessage(
791
0
        MessageType::FATAL_ERROR,
792
0
        cmStrCat("Specified wrapper \'", wrapper.Value, "\' for target \'",
793
0
                 targetName, "\' does not exist. Set the GLOBAL property \'",
794
0
                 wrapperPropName, "\'."),
795
0
        wrapper.Backtrace);
796
0
      return {};
797
0
    }
798
799
0
    cmGeneratorExpressionDAGChecker dagChecker{ this->GeneratorTarget,
800
0
                                                std::string(targetPropName),
801
0
                                                nullptr, nullptr, context };
802
0
    std::string const evaluatedWrapper = cmGeneratorExpression::Evaluate(
803
0
      *globalPropValue, context.LG, context.Config, this->GeneratorTarget,
804
0
      &dagChecker, this->GeneratorTarget, context.Language);
805
    // An empty definition is a no-op wrapper, but empty arguments within a
806
    // non-empty definition must reach the command line.
807
0
    if (!evaluatedWrapper.empty()) {
808
0
      cm::append(wrapperCommandLine,
809
0
                 cmList{ evaluatedWrapper, cmList::EmptyElements::Yes });
810
0
    }
811
0
  }
812
813
0
  return wrapperCommandLine;
814
0
}