Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGeneratorTarget_Sources.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
/* clang-format off */
4
#include "cmGeneratorTarget.h"
5
/* clang-format on */
6
7
#include <cstddef>
8
#include <map>
9
#include <memory>
10
#include <set>
11
#include <sstream>
12
#include <string>
13
#include <unordered_set>
14
#include <utility>
15
#include <vector>
16
17
#include <cm/string_view>
18
#include <cmext/algorithm>
19
#include <cmext/string_view>
20
21
#include "cmsys/RegularExpression.hxx"
22
23
#include "cmEvaluatedTargetProperty.h"
24
#include "cmFileSet.h"
25
#include "cmGenExContext.h"
26
#include "cmGeneratorExpression.h"
27
#include "cmGeneratorExpressionDAGChecker.h"
28
#include "cmGlobalGenerator.h"
29
#include "cmLinkItem.h"
30
#include "cmList.h"
31
#include "cmListFileCache.h"
32
#include "cmLocalGenerator.h"
33
#include "cmMakefile.h"
34
#include "cmMessageType.h"
35
#include "cmSourceFile.h"
36
#include "cmSourceFileLocation.h"
37
#include "cmSourceGroup.h"
38
#include "cmStateTypes.h"
39
#include "cmStringAlgorithms.h"
40
#include "cmSystemTools.h"
41
#include "cmTarget.h"
42
#include "cmValue.h"
43
#include "cmake.h"
44
45
namespace {
46
using UseTo = cmGeneratorTarget::UseTo;
47
48
void AddObjectEntries(cmGeneratorTarget const* headTarget,
49
                      cm::GenEx::Context const& context,
50
                      cmGeneratorExpressionDAGChecker* dagChecker,
51
                      EvaluatedTargetPropertyEntries& entries)
52
0
{
53
0
  if (cmLinkImplementationLibraries const* impl =
54
0
        headTarget->GetLinkImplementationLibraries(context.Config,
55
0
                                                   UseTo::Link)) {
56
0
    entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition;
57
0
    for (cmLinkItem const& lib : impl->Libraries) {
58
0
      if (lib.Target &&
59
0
          lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
60
0
        std::string uniqueName =
61
0
          headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
62
0
            lib.Target);
63
0
        std::string genex =
64
0
          cmStrCat("$<TARGET_OBJECTS:", std::move(uniqueName), '>');
65
0
        cmGeneratorExpression ge(*headTarget->Makefile->GetCMakeInstance(),
66
0
                                 lib.Backtrace);
67
0
        std::unique_ptr<cmCompiledGeneratorExpression> cge =
68
0
          ge.Parse(std::move(genex));
69
0
        cge->SetEvaluateForBuildsystem(true);
70
71
0
        EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
72
0
        cmExpandList(cge->Evaluate(context, dagChecker, headTarget),
73
0
                     ee.Values);
74
0
        if (cge->GetHadContextSensitiveCondition()) {
75
0
          ee.ContextDependent = true;
76
0
        }
77
0
        entries.Entries.emplace_back(std::move(ee));
78
0
      }
79
0
    }
80
0
  }
81
0
}
82
83
void addFileSetEntry(cmGeneratorTarget const* headTarget,
84
                     cm::GenEx::Context const& context,
85
                     cmGeneratorExpressionDAGChecker* dagChecker,
86
                     cmFileSet const* fileSet,
87
                     EvaluatedTargetPropertyEntries& entries)
88
0
{
89
0
  auto dirCges = fileSet->CompileDirectoryEntries();
90
0
  auto dirs = fileSet->EvaluateDirectoryEntries(dirCges, context, headTarget,
91
0
                                                dagChecker);
92
0
  bool contextSensitiveDirs = false;
93
0
  for (auto const& dirCge : dirCges) {
94
0
    if (dirCge->GetHadContextSensitiveCondition()) {
95
0
      contextSensitiveDirs = true;
96
0
      break;
97
0
    }
98
0
  }
99
0
  cmake* cm = headTarget->GetLocalGenerator()->GetCMakeInstance();
100
0
  for (auto& entryCge : fileSet->CompileFileEntries()) {
101
0
    auto targetPropEntry =
102
0
      cmGeneratorTarget::TargetPropertyEntry::CreateFileSet(
103
0
        dirs, contextSensitiveDirs, std::move(entryCge), fileSet);
104
0
    entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
105
0
      headTarget, context, dagChecker, *targetPropEntry));
106
0
    EvaluatedTargetPropertyEntry const& entry = entries.Entries.back();
107
0
    for (auto const& file : entry.Values) {
108
0
      auto* sf = headTarget->Makefile->GetOrCreateSource(file);
109
0
      if (fileSet->GetType() == "HEADERS"_s) {
110
0
        sf->SetProperty("HEADER_FILE_ONLY", "TRUE");
111
0
      }
112
113
0
#ifndef CMAKE_BOOTSTRAP
114
0
      std::string e;
115
0
      std::string w;
116
0
      auto path = sf->ResolveFullPath(&e, &w);
117
0
      if (!w.empty()) {
118
0
        cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
119
0
      }
120
0
      if (path.empty()) {
121
0
        if (!e.empty()) {
122
0
          cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
123
0
        }
124
0
        return;
125
0
      }
126
0
      bool found = false;
127
0
      for (auto const& sg : headTarget->Makefile->GetSourceGroups()) {
128
0
        if (sg->MatchChildrenFiles(path)) {
129
0
          found = true;
130
0
          break;
131
0
        }
132
0
      }
133
0
      if (!found) {
134
0
        if (fileSet->GetType() == "HEADERS"_s) {
135
0
          headTarget->Makefile->GetOrCreateSourceGroup("Header Files")
136
0
            ->AddGroupFile(path);
137
0
        }
138
0
      }
139
0
#endif
140
0
    }
141
0
  }
142
0
}
143
144
void AddFileSetEntries(cmGeneratorTarget const* headTarget,
145
                       cm::GenEx::Context const& context,
146
                       cmGeneratorExpressionDAGChecker* dagChecker,
147
                       EvaluatedTargetPropertyEntries& entries)
148
0
{
149
0
  for (auto const& entry : headTarget->Target->GetHeaderSetsEntries()) {
150
0
    for (auto const& name : cmList{ entry.Value }) {
151
0
      auto const* headerSet = headTarget->Target->GetFileSet(name);
152
0
      addFileSetEntry(headTarget, context, dagChecker, headerSet, entries);
153
0
    }
154
0
  }
155
0
  for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) {
156
0
    for (auto const& name : cmList{ entry.Value }) {
157
0
      auto const* cxxModuleSet = headTarget->Target->GetFileSet(name);
158
0
      addFileSetEntry(headTarget, context, dagChecker, cxxModuleSet, entries);
159
0
    }
160
0
  }
161
0
}
162
163
bool processSources(cmGeneratorTarget const* tgt,
164
                    EvaluatedTargetPropertyEntries& entries,
165
                    std::vector<BT<std::string>>& srcs,
166
                    std::unordered_set<std::string>& uniqueSrcs,
167
                    bool debugSources)
168
0
{
169
0
  cmMakefile* mf = tgt->Target->GetMakefile();
170
171
0
  bool contextDependent = entries.HadContextSensitiveCondition;
172
173
0
  for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
174
0
    if (entry.ContextDependent) {
175
0
      contextDependent = true;
176
0
    }
177
178
0
    cmLinkItem const& item = entry.LinkItem;
179
0
    std::string const& targetName = item.AsStr();
180
181
0
    for (std::string& src : entry.Values) {
182
0
      cmSourceFile* sf = mf->GetOrCreateSource(src);
183
0
      std::string e;
184
0
      std::string w;
185
0
      std::string fullPath = sf->ResolveFullPath(&e, &w);
186
0
      cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance();
187
0
      if (!w.empty()) {
188
0
        cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
189
0
      }
190
0
      if (fullPath.empty()) {
191
0
        if (!e.empty()) {
192
0
          cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
193
0
        }
194
0
        return contextDependent;
195
0
      }
196
197
0
      if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) {
198
0
        std::ostringstream err;
199
0
        if (!targetName.empty()) {
200
0
          err << "Target \"" << targetName
201
0
              << "\" contains relative path in its INTERFACE_SOURCES:\n  \""
202
0
              << src << "\"";
203
0
        } else {
204
0
          err << "Found relative path while evaluating sources of \""
205
0
              << tgt->GetName() << "\":\n  \"" << src << "\"\n";
206
0
        }
207
0
        tgt->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
208
0
                                               err.str());
209
0
        return contextDependent;
210
0
      }
211
0
      src = fullPath;
212
0
    }
213
0
    std::string usedSources;
214
0
    for (std::string const& src : entry.Values) {
215
0
      if (uniqueSrcs.insert(src).second) {
216
0
        srcs.emplace_back(src, entry.Backtrace);
217
0
        if (debugSources) {
218
0
          usedSources += cmStrCat(" * ", src, '\n');
219
0
        }
220
0
      }
221
0
    }
222
0
    if (!usedSources.empty()) {
223
0
      tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
224
0
        MessageType::LOG,
225
0
        cmStrCat("Used sources for target ", tgt->GetName(), ":\n",
226
0
                 usedSources),
227
0
        entry.Backtrace);
228
0
    }
229
0
  }
230
0
  return contextDependent;
231
0
}
232
}
233
234
std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
235
  std::string const& config) const
236
0
{
237
0
  std::vector<BT<std::string>> files;
238
239
0
  cmList debugProperties{ this->Makefile->GetDefinition(
240
0
    "CMAKE_DEBUG_TARGET_PROPERTIES") };
241
0
  bool debugSources =
242
0
    !this->DebugSourcesDone && cm::contains(debugProperties, "SOURCES");
243
244
0
  this->DebugSourcesDone = true;
245
246
0
  cm::GenEx::Context context(this->LocalGenerator, config,
247
0
                             /*language=*/std::string());
248
249
0
  cmGeneratorExpressionDAGChecker dagChecker{
250
0
    this, "SOURCES", nullptr, nullptr, context,
251
0
  };
252
253
0
  EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
254
0
    this, context, &dagChecker, this->SourceEntries);
255
256
0
  std::unordered_set<std::string> uniqueSrcs;
257
0
  bool contextDependentDirectSources =
258
0
    processSources(this, entries, files, uniqueSrcs, debugSources);
259
260
  // Collect INTERFACE_SOURCES of all direct link-dependencies.
261
0
  EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries;
262
0
  AddInterfaceEntries(this, "INTERFACE_SOURCES", context, &dagChecker,
263
0
                      linkInterfaceSourcesEntries, IncludeRuntimeInterface::No,
264
0
                      UseTo::Compile);
265
0
  bool contextDependentInterfaceSources = processSources(
266
0
    this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources);
267
268
  // Collect TARGET_OBJECTS of direct object link-dependencies.
269
0
  bool contextDependentObjects = false;
270
0
  if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
271
0
    EvaluatedTargetPropertyEntries linkObjectsEntries;
272
0
    AddObjectEntries(this, context, &dagChecker, linkObjectsEntries);
273
0
    contextDependentObjects = processSources(this, linkObjectsEntries, files,
274
0
                                             uniqueSrcs, debugSources);
275
    // Note that for imported targets or multi-config generators supporting
276
    // cross-config builds the paths to the object files must be per-config,
277
    // so contextDependentObjects will be true here even if object libraries
278
    // are specified without per-config generator expressions.
279
0
  }
280
281
  // Collect this target's file sets.
282
0
  EvaluatedTargetPropertyEntries fileSetEntries;
283
0
  AddFileSetEntries(this, context, &dagChecker, fileSetEntries);
284
0
  bool contextDependentFileSets =
285
0
    processSources(this, fileSetEntries, files, uniqueSrcs, debugSources);
286
287
  // Determine if sources are context-dependent or not.
288
0
  if (!contextDependentDirectSources && !contextDependentInterfaceSources &&
289
0
      !contextDependentObjects && !contextDependentFileSets) {
290
0
    this->SourcesAreContextDependent = Tribool::False;
291
0
  } else {
292
0
    this->SourcesAreContextDependent = Tribool::True;
293
0
  }
294
295
0
  return files;
296
0
}
297
298
void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files,
299
                                       std::string const& config) const
300
0
{
301
0
  std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config);
302
0
  files.reserve(tmp.size());
303
0
  for (BT<cmSourceFile*>& v : tmp) {
304
0
    files.push_back(v.Value);
305
0
  }
306
0
}
307
308
std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles(
309
  std::string const& config) const
310
0
{
311
0
  std::vector<BT<cmSourceFile*>> files;
312
0
  KindedSources const& kinded = this->GetKindedSources(config);
313
0
  files.reserve(kinded.Sources.size());
314
0
  for (SourceAndKind const& si : kinded.Sources) {
315
0
    files.push_back(si.Source);
316
0
  }
317
0
  return files;
318
0
}
319
320
void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
321
  std::vector<cmSourceFile*>& files, std::string const& config) const
322
0
{
323
0
  std::vector<BT<cmSourceFile*>> tmp =
324
0
    this->GetSourceFilesWithoutObjectLibraries(config);
325
0
  files.reserve(tmp.size());
326
0
  for (BT<cmSourceFile*>& v : tmp) {
327
0
    files.push_back(v.Value);
328
0
  }
329
0
}
330
331
std::vector<BT<cmSourceFile*>>
332
cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
333
  std::string const& config) const
334
0
{
335
0
  std::vector<BT<cmSourceFile*>> files;
336
0
  KindedSources const& kinded = this->GetKindedSources(config);
337
0
  files.reserve(kinded.Sources.size());
338
0
  for (SourceAndKind const& si : kinded.Sources) {
339
0
    if (si.Source.Value->GetObjectLibrary().empty()) {
340
0
      files.push_back(si.Source);
341
0
    }
342
0
  }
343
0
  return files;
344
0
}
345
346
cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources(
347
  std::string const& config) const
348
0
{
349
  // If we already processed one configuration and found no dependency
350
  // on configuration then always use the one result.
351
0
  if (this->SourcesAreContextDependent == Tribool::False) {
352
0
    return this->KindedSourcesMap.begin()->second;
353
0
  }
354
355
  // Lookup any existing link implementation for this configuration.
356
0
  std::string const key = cmSystemTools::UpperCase(config);
357
0
  auto it = this->KindedSourcesMap.find(key);
358
0
  if (it != this->KindedSourcesMap.end()) {
359
0
    if (!it->second.Initialized) {
360
0
      std::ostringstream e;
361
0
      e << "The SOURCES of \"" << this->GetName()
362
0
        << "\" use a generator expression that depends on the "
363
0
           "SOURCES themselves.";
364
0
      this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
365
0
        MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
366
0
      static KindedSources empty;
367
0
      return empty;
368
0
    }
369
0
    return it->second;
370
0
  }
371
372
  // Add an entry to the map for this configuration.
373
0
  KindedSources& files = this->KindedSourcesMap[key];
374
0
  this->ComputeKindedSources(files, config);
375
0
  files.Initialized = true;
376
0
  return files;
377
0
}
378
379
void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
380
                                             std::string const& config) const
381
0
{
382
  // Get the source file paths by string.
383
0
  std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config);
384
385
0
  cmsys::RegularExpression header_regex(CM_HEADER_REGEX);
386
0
  std::vector<cmSourceFile*> badObjLib;
387
388
0
  std::set<cmSourceFile*> emitted;
389
0
  for (BT<std::string> const& s : srcs) {
390
    // Create each source at most once.
391
0
    cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value);
392
0
    if (!emitted.insert(sf).second) {
393
0
      continue;
394
0
    }
395
396
    // Compute the kind (classification) of this source file.
397
0
    SourceKind kind;
398
0
    std::string ext = cmSystemTools::LowerCase(sf->GetExtension());
399
0
    cmFileSet const* fs = this->GetFileSetForSource(config, sf);
400
0
    if (sf->GetCustomCommand()) {
401
0
      kind = SourceKindCustomCommand;
402
0
    } else if (!this->Target->IsNormal() && !this->Target->IsImported() &&
403
0
               fs && (fs->GetType() == "CXX_MODULES"_s)) {
404
0
      kind = SourceKindCxxModuleSource;
405
0
    } else if (this->Target->GetType() == cmStateEnums::UTILITY ||
406
0
               this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY
407
               // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
408
               // NOLINTNEXTLINE(bugprone-branch-clone)
409
0
    ) {
410
0
      kind = SourceKindExtra;
411
0
    } else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) {
412
0
      kind = SourceKindUnityBatched;
413
      // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
414
      // NOLINTNEXTLINE(bugprone-branch-clone)
415
0
    } else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) {
416
0
      kind = SourceKindHeader;
417
0
    } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
418
0
      kind = SourceKindExternalObject;
419
0
    } else if (!sf->GetOrDetermineLanguage().empty()) {
420
0
      kind = SourceKindObjectSource;
421
0
    } else if (ext == "def") {
422
0
      kind = SourceKindModuleDefinition;
423
0
      if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
424
0
        badObjLib.push_back(sf);
425
0
      }
426
0
    } else if (ext == "idl") {
427
0
      kind = SourceKindIDL;
428
0
      if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
429
0
        badObjLib.push_back(sf);
430
0
      }
431
0
    } else if (ext == "resx") {
432
0
      kind = SourceKindResx;
433
0
    } else if (ext == "appxmanifest") {
434
0
      kind = SourceKindAppManifest;
435
0
    } else if (ext == "manifest") {
436
0
      if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) {
437
0
        kind = SourceKindExtra;
438
0
      } else {
439
0
        kind = SourceKindManifest;
440
0
      }
441
0
    } else if (ext == "pfx") {
442
0
      kind = SourceKindCertificate;
443
0
    } else if (ext == "xaml") {
444
0
      kind = SourceKindXaml;
445
0
    } else if (header_regex.find(sf->ResolveFullPath())) {
446
0
      kind = SourceKindHeader;
447
0
    } else {
448
0
      kind = SourceKindExtra;
449
0
    }
450
451
    // Save this classified source file in the result vector.
452
0
    files.Sources.push_back({ BT<cmSourceFile*>(sf, s.Backtrace), kind });
453
0
  }
454
455
0
  if (!badObjLib.empty()) {
456
0
    std::ostringstream e;
457
0
    e << "OBJECT library \"" << this->GetName() << "\" contains:\n";
458
0
    for (cmSourceFile* i : badObjLib) {
459
0
      e << "  " << i->GetLocation().GetName() << "\n";
460
0
    }
461
0
    e << "but may contain only sources that compile, header files, and "
462
0
         "other files that would not affect linking of a normal library.";
463
0
    this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
464
0
      MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
465
0
  }
466
0
}
467
468
std::vector<cmGeneratorTarget::AllConfigSource> const&
469
cmGeneratorTarget::GetAllConfigSources() const
470
0
{
471
0
  if (this->AllConfigSources.empty()) {
472
0
    this->ComputeAllConfigSources();
473
0
  }
474
0
  return this->AllConfigSources;
475
0
}
476
477
void cmGeneratorTarget::ComputeAllConfigSources() const
478
0
{
479
0
  std::vector<std::string> configs =
480
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
481
482
0
  std::map<cmSourceFile const*, size_t> index;
483
484
0
  for (size_t ci = 0; ci < configs.size(); ++ci) {
485
0
    KindedSources const& sources = this->GetKindedSources(configs[ci]);
486
0
    for (SourceAndKind const& src : sources.Sources) {
487
0
      auto mi = index.find(src.Source.Value);
488
0
      if (mi == index.end()) {
489
0
        AllConfigSource acs;
490
0
        acs.Source = src.Source.Value;
491
0
        acs.Kind = src.Kind;
492
0
        this->AllConfigSources.push_back(std::move(acs));
493
0
        std::map<cmSourceFile const*, size_t>::value_type entry(
494
0
          src.Source.Value, this->AllConfigSources.size() - 1);
495
0
        mi = index.insert(entry).first;
496
0
      }
497
0
      this->AllConfigSources[mi->second].Configs.push_back(ci);
498
0
    }
499
0
  }
500
0
}
501
502
std::vector<cmGeneratorTarget::AllConfigSource>
503
cmGeneratorTarget::GetAllConfigSources(SourceKind kind) const
504
0
{
505
0
  std::vector<AllConfigSource> result;
506
0
  for (AllConfigSource const& source : this->GetAllConfigSources()) {
507
0
    if (source.Kind == kind) {
508
0
      result.push_back(source);
509
0
    }
510
0
  }
511
0
  return result;
512
0
}
513
514
void cmGeneratorTarget::ComputeAllConfigCompileLanguages() const
515
0
{
516
0
  std::set<std::string> languages;
517
0
  std::vector<AllConfigSource> const& sources = this->GetAllConfigSources();
518
0
  for (AllConfigSource const& si : sources) {
519
0
    std::string const& lang = si.Source->GetOrDetermineLanguage();
520
0
    if (!lang.empty()) {
521
0
      languages.emplace(lang);
522
0
    }
523
0
  }
524
0
  this->AllConfigCompileLanguages = languages;
525
0
}
526
527
std::set<std::string> cmGeneratorTarget::GetAllConfigCompileLanguages() const
528
0
{
529
0
  if (this->AllConfigCompileLanguages.empty()) {
530
0
    this->ComputeAllConfigCompileLanguages();
531
0
  }
532
0
  return this->AllConfigCompileLanguages;
533
0
}