Coverage Report

Created: 2026-03-12 06:35

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