Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGeneratorFileSet.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 "cmGeneratorFileSet.h"
4
5
#include <algorithm>
6
#include <map>
7
#include <sstream>
8
#include <unordered_map>
9
#include <utility>
10
#include <vector>
11
12
#include <cm/memory>
13
#include <cm/optional>
14
15
#include "cmFileSet.h"
16
#include "cmGenExContext.h"
17
#include "cmGeneratorExpression.h"
18
#include "cmList.h"
19
#include "cmListFileCache.h"
20
#include "cmLocalGenerator.h"
21
#include "cmMakefile.h"
22
#include "cmMessageType.h"
23
#include "cmStringAlgorithms.h"
24
#include "cmSystemTools.h"
25
#include "cmake.h"
26
27
class cmLinkItem;
28
29
class FileSetPropertyEntry : public cm::TargetPropertyEntry
30
{
31
public:
32
  FileSetPropertyEntry(
33
    std::vector<std::string> dirs, bool contextSensitiveDirs,
34
    std::unique_ptr<cmCompiledGeneratorExpression> const& cge,
35
    cmGeneratorFileSet const* fileSet, cmLinkItem const& item = NoLinkItem)
36
0
    : cm::TargetPropertyEntry(item)
37
0
    , BaseDirs(std::move(dirs))
38
0
    , ContextSensitiveDirs(contextSensitiveDirs)
39
0
    , Cge(cge)
40
0
    , FileSet(fileSet)
41
0
  {
42
0
  }
43
44
  static std::unique_ptr<cm::TargetPropertyEntry> CreateFileSetEntry(
45
    std::vector<std::string> dirs, bool contextSensitiveDirs,
46
    std::unique_ptr<cmCompiledGeneratorExpression> const& cge,
47
    cmGeneratorFileSet const* fileSet, cmLinkItem const& item = NoLinkItem)
48
0
  {
49
0
    return cm::make_unique<FileSetPropertyEntry>(
50
0
      std::move(dirs), contextSensitiveDirs, cge, fileSet, item);
51
0
  }
52
53
  std::string const& Evaluate(
54
    cm::GenEx::Context const& context, cmGeneratorTarget const* headTarget,
55
    cmGeneratorExpressionDAGChecker* dagChecker) const override
56
0
  {
57
0
    std::map<std::string, std::vector<std::string>> filesPerDir;
58
0
    this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir, this->Cge,
59
0
                                     context, headTarget, dagChecker);
60
61
0
    std::vector<std::string> files;
62
0
    for (auto const& it : filesPerDir) {
63
0
      files.insert(files.end(), it.second.begin(), it.second.end());
64
0
    }
65
66
0
    this->Value = cmList::to_string(files);
67
0
    return Value;
68
0
  }
69
70
  cmListFileBacktrace GetBacktrace() const override
71
0
  {
72
0
    return this->Cge->GetBacktrace();
73
0
  }
74
75
  std::string const& GetInput() const override
76
0
  {
77
0
    return this->Cge->GetInput();
78
0
  }
79
80
  bool GetHadContextSensitiveCondition() const override
81
0
  {
82
0
    return this->ContextSensitiveDirs ||
83
0
      this->Cge->GetHadContextSensitiveCondition();
84
0
  }
85
86
private:
87
  std::vector<std::string> const BaseDirs;
88
  bool const ContextSensitiveDirs;
89
  std::unique_ptr<cmCompiledGeneratorExpression> const& Cge;
90
  cmGeneratorFileSet const* FileSet;
91
  mutable std::string Value;
92
};
93
94
//
95
// Class cmGeneratorFileSet
96
//
97
cmGeneratorFileSet::cmGeneratorFileSet(cmFileSet const* fileSet)
98
0
  : FileSet(fileSet)
99
0
{
100
0
}
101
102
std::string const& cmGeneratorFileSet::GetName() const
103
0
{
104
0
  return this->FileSet->GetName();
105
0
}
106
std::string const& cmGeneratorFileSet::GetType() const
107
0
{
108
0
  return this->FileSet->GetType();
109
0
}
110
cm::FileSetMetadata::Visibility cmGeneratorFileSet::GetVisibility() const
111
0
{
112
0
  return this->FileSet->GetVisibility();
113
0
}
114
115
bool cmGeneratorFileSet::IsForSelf() const
116
0
{
117
0
  return this->FileSet->IsForSelf();
118
0
}
119
bool cmGeneratorFileSet::IsForInterface() const
120
0
{
121
0
  return this->FileSet->IsForInterface();
122
0
}
123
bool cmGeneratorFileSet::CanBeIncluded() const
124
0
{
125
0
  return this->FileSet->CanBeIncluded();
126
0
}
127
128
cmValue cmGeneratorFileSet::GetProperty(std::string const& prop) const
129
0
{
130
0
  return this->FileSet->GetProperty(prop);
131
0
}
132
133
std::vector<BT<std::string>> const& cmGeneratorFileSet::GetDirectoryEntries()
134
  const
135
0
{
136
0
  return this->FileSet->GetDirectoryEntries();
137
0
}
138
139
std::vector<BT<std::string>> const& cmGeneratorFileSet::GetFileEntries() const
140
0
{
141
0
  return this->FileSet->GetFileEntries();
142
0
}
143
144
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const&
145
cmGeneratorFileSet::CompileFileEntries() const
146
0
{
147
0
  if (this->CompiledFileEntries.empty() &&
148
0
      !this->FileSet->GetFileEntries().empty()) {
149
0
    for (auto const& entry : this->FileSet->GetFileEntries()) {
150
0
      for (auto const& ex : cmList{ entry.Value }) {
151
0
        cmGeneratorExpression ge(
152
0
          *this->FileSet->GetMakefile()->GetCMakeInstance(), entry.Backtrace);
153
0
        auto cge = ge.Parse(ex);
154
0
        this->CompiledFileEntries.push_back(std::move(cge));
155
0
      }
156
0
    }
157
0
  }
158
159
0
  return this->CompiledFileEntries;
160
0
}
161
162
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const&
163
cmGeneratorFileSet::CompileDirectoryEntries() const
164
0
{
165
0
  if (this->CompiledDirectoryEntries.empty() &&
166
0
      !this->FileSet->GetDirectoryEntries().empty()) {
167
0
    for (auto const& entry : this->FileSet->GetDirectoryEntries()) {
168
0
      for (auto const& ex : cmList{ entry.Value }) {
169
0
        cmGeneratorExpression ge(
170
0
          *this->FileSet->GetMakefile()->GetCMakeInstance(), entry.Backtrace);
171
0
        auto cge = ge.Parse(ex);
172
0
        this->CompiledDirectoryEntries.push_back(std::move(cge));
173
0
      }
174
0
    }
175
0
  }
176
177
0
  return this->CompiledDirectoryEntries;
178
0
}
179
180
std::vector<std::string> cmGeneratorFileSet::EvaluateDirectoryEntries(
181
  std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& cges,
182
  cm::GenEx::Context const& context, cmGeneratorTarget const* target,
183
  cmGeneratorExpressionDAGChecker* dagChecker) const
184
0
{
185
0
  struct DirCacheEntry
186
0
  {
187
0
    std::string collapsedDir;
188
0
    cm::optional<cmSystemTools::FileId> fileId;
189
0
  };
190
191
0
  std::unordered_map<std::string, DirCacheEntry> dirCache;
192
0
  std::vector<std::string> result;
193
0
  for (auto const& cge : cges) {
194
0
    auto entry = cge->Evaluate(context, dagChecker, target);
195
0
    cmList dirs{ entry };
196
0
    for (std::string dir : dirs) {
197
0
      if (!cmSystemTools::FileIsFullPath(dir)) {
198
0
        dir = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', dir);
199
0
      }
200
201
0
      auto dirCacheResult = dirCache.emplace(dir, DirCacheEntry());
202
0
      auto& dirCacheEntry = dirCacheResult.first->second;
203
0
      auto const isNewCacheEntry = dirCacheResult.second;
204
205
0
      if (isNewCacheEntry) {
206
0
        cmSystemTools::FileId fileId;
207
0
        auto isFileIdValid = cmSystemTools::GetFileId(dir, fileId);
208
0
        dirCacheEntry.collapsedDir = cmSystemTools::CollapseFullPath(dir);
209
0
        dirCacheEntry.fileId =
210
0
          isFileIdValid ? cm::optional<decltype(fileId)>(fileId) : cm::nullopt;
211
0
      }
212
213
0
      for (auto const& priorDir : result) {
214
0
        auto priorDirCacheEntry = dirCache.at(priorDir);
215
0
        bool sameFile = dirCacheEntry.fileId.has_value() &&
216
0
          priorDirCacheEntry.fileId.has_value() &&
217
0
          (*dirCacheEntry.fileId == *priorDirCacheEntry.fileId);
218
0
        if (!sameFile &&
219
0
            (cmSystemTools::IsSubDirectory(dirCacheEntry.collapsedDir,
220
0
                                           priorDirCacheEntry.collapsedDir) ||
221
0
             cmSystemTools::IsSubDirectory(priorDirCacheEntry.collapsedDir,
222
0
                                           dirCacheEntry.collapsedDir))) {
223
0
          context.LG->GetCMakeInstance()->IssueMessage(
224
0
            MessageType::FATAL_ERROR,
225
0
            cmStrCat(
226
0
              "Base directories in file set cannot be subdirectories of each "
227
0
              "other:\n  ",
228
0
              priorDir, "\n  ", dir),
229
0
            cge->GetBacktrace());
230
0
          return {};
231
0
        }
232
0
      }
233
0
      result.push_back(dir);
234
0
    }
235
0
  }
236
0
  return result;
237
0
}
238
239
void cmGeneratorFileSet::EvaluateFileEntry(
240
  std::vector<std::string> const& dirs,
241
  std::map<std::string, std::vector<std::string>>& filesPerDir,
242
  std::unique_ptr<cmCompiledGeneratorExpression> const& cge,
243
  cm::GenEx::Context const& context, cmGeneratorTarget const* target,
244
  cmGeneratorExpressionDAGChecker* dagChecker) const
245
0
{
246
0
  auto files = cge->Evaluate(context, dagChecker, target);
247
0
  for (std::string file : cmList{ files }) {
248
0
    if (!cmSystemTools::FileIsFullPath(file)) {
249
0
      file = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', file);
250
0
    }
251
0
    auto collapsedFile = cmSystemTools::CollapseFullPath(file);
252
0
    bool found = false;
253
0
    std::string relDir;
254
0
    for (auto const& dir : dirs) {
255
0
      auto collapsedDir = cmSystemTools::CollapseFullPath(dir);
256
0
      if (cmSystemTools::IsSubDirectory(collapsedFile, collapsedDir)) {
257
0
        found = true;
258
0
        relDir = cmSystemTools::GetParentDirectory(
259
0
          cmSystemTools::RelativePath(collapsedDir, collapsedFile));
260
0
        break;
261
0
      }
262
0
    }
263
0
    if (!found) {
264
0
      std::ostringstream e;
265
0
      e << "File:\n  " << file
266
0
        << "\nmust be in one of the file set's base directories:";
267
0
      for (auto const& dir : dirs) {
268
0
        e << "\n  " << dir;
269
0
      }
270
0
      context.LG->GetCMakeInstance()->IssueMessage(
271
0
        MessageType::FATAL_ERROR, e.str(), cge->GetBacktrace());
272
0
      return;
273
0
    }
274
275
0
    filesPerDir[relDir].push_back(file);
276
0
  }
277
0
}
278
279
namespace {
280
bool EntryIsContextSensitive(
281
  std::unique_ptr<cmCompiledGeneratorExpression> const& cge)
282
0
{
283
0
  return cge->GetHadContextSensitiveCondition();
284
0
}
285
}
286
287
std::vector<std::unique_ptr<cm::TargetPropertyEntry>>
288
cmGeneratorFileSet::GetSources(
289
  cm::GenEx::Context const& context, cmGeneratorTarget const* target,
290
  cmGeneratorExpressionDAGChecker* dagChecker) const
291
0
{
292
0
  std::vector<std::unique_ptr<TargetPropertyEntry>> entries;
293
294
0
  auto directories = this->GetDirectories(context, target, dagChecker);
295
0
  bool contextSensitive = directories.second;
296
297
0
  for (auto const& entry : this->CompileFileEntries()) {
298
0
    auto propEntry = FileSetPropertyEntry::CreateFileSetEntry(
299
0
      directories.first, contextSensitive, entry, this);
300
0
    entries.push_back(std::move(propEntry));
301
0
  }
302
303
0
  return entries;
304
0
}
305
306
std::pair<std::vector<std::string>, bool> cmGeneratorFileSet::GetDirectories(
307
  cm::GenEx::Context const& context, cmGeneratorTarget const* target,
308
  cmGeneratorExpressionDAGChecker* dagChecker) const
309
0
{
310
0
  auto const& directoryEntries = this->CompileDirectoryEntries();
311
0
  auto directories = this->EvaluateDirectoryEntries(directoryEntries, context,
312
0
                                                    target, dagChecker);
313
0
  bool contextSensitive = std::any_of(
314
0
    directoryEntries.begin(), directoryEntries.end(), EntryIsContextSensitive);
315
316
0
  return std::make_pair(std::move(directories), contextSensitive);
317
0
}
318
319
std::pair<std::map<std::string, std::vector<std::string>>, bool>
320
cmGeneratorFileSet::GetFiles(cm::GenEx::Context const& context,
321
                             cmGeneratorTarget const* target,
322
                             cmGeneratorExpressionDAGChecker* dagChecker) const
323
0
{
324
0
  auto directories = this->GetDirectories(context, target, dagChecker);
325
326
0
  auto const& fileEntries = this->CompileFileEntries();
327
0
  std::map<std::string, std::vector<std::string>> files;
328
0
  for (auto const& entry : fileEntries) {
329
0
    this->EvaluateFileEntry(directories.first, files, entry, context, target,
330
0
                            dagChecker);
331
0
  }
332
0
  bool contextSensitive = directories.second ||
333
0
    std::any_of(fileEntries.begin(), fileEntries.end(),
334
0
                EntryIsContextSensitive);
335
336
0
  return std::make_pair(std::move(files), contextSensitive);
337
0
}