Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmFileSet.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 "cmFileSet.h"
4
5
#include <sstream>
6
#include <string>
7
#include <unordered_map>
8
#include <utility>
9
#include <vector>
10
11
#include <cm/optional>
12
#include <cmext/algorithm>
13
#include <cmext/string_view>
14
15
#include "cmsys/RegularExpression.hxx"
16
17
#include "cmGenExContext.h"
18
#include "cmGeneratorExpression.h"
19
#include "cmList.h"
20
#include "cmListFileCache.h"
21
#include "cmLocalGenerator.h"
22
#include "cmMakefile.h"
23
#include "cmMessageType.h"
24
#include "cmStringAlgorithms.h"
25
#include "cmSystemTools.h"
26
#include "cmake.h"
27
28
cm::static_string_view cmFileSetVisibilityToName(cmFileSetVisibility vis)
29
0
{
30
0
  switch (vis) {
31
0
    case cmFileSetVisibility::Interface:
32
0
      return "INTERFACE"_s;
33
0
    case cmFileSetVisibility::Public:
34
0
      return "PUBLIC"_s;
35
0
    case cmFileSetVisibility::Private:
36
0
      return "PRIVATE"_s;
37
0
  }
38
0
  return ""_s;
39
0
}
40
41
cmFileSetVisibility cmFileSetVisibilityFromName(cm::string_view name,
42
                                                cmMakefile* mf)
43
0
{
44
0
  if (name == "INTERFACE"_s) {
45
0
    return cmFileSetVisibility::Interface;
46
0
  }
47
0
  if (name == "PUBLIC"_s) {
48
0
    return cmFileSetVisibility::Public;
49
0
  }
50
0
  if (name == "PRIVATE"_s) {
51
0
    return cmFileSetVisibility::Private;
52
0
  }
53
0
  auto msg = cmStrCat("File set visibility \"", name, "\" is not valid.");
54
0
  if (mf) {
55
0
    mf->IssueMessage(MessageType::FATAL_ERROR, msg);
56
0
  } else {
57
0
    cmSystemTools::Error(msg);
58
0
  }
59
0
  return cmFileSetVisibility::Private;
60
0
}
61
62
bool cmFileSetVisibilityIsForSelf(cmFileSetVisibility vis)
63
0
{
64
0
  switch (vis) {
65
0
    case cmFileSetVisibility::Interface:
66
0
      return false;
67
0
    case cmFileSetVisibility::Public:
68
0
    case cmFileSetVisibility::Private:
69
0
      return true;
70
0
  }
71
0
  return false;
72
0
}
73
74
bool cmFileSetVisibilityIsForInterface(cmFileSetVisibility vis)
75
0
{
76
0
  switch (vis) {
77
0
    case cmFileSetVisibility::Interface:
78
0
    case cmFileSetVisibility::Public:
79
0
      return true;
80
0
    case cmFileSetVisibility::Private:
81
0
      return false;
82
0
  }
83
0
  return false;
84
0
}
85
86
bool cmFileSetTypeCanBeIncluded(std::string const& type)
87
0
{
88
0
  return type == "HEADERS"_s;
89
0
}
90
91
cmFileSet::cmFileSet(cmMakefile* makefile, std::string name, std::string type,
92
                     cmFileSetVisibility visibility)
93
0
  : Makefile(makefile)
94
0
  , Name(std::move(name))
95
0
  , Type(std::move(type))
96
0
  , Visibility(visibility)
97
0
{
98
0
}
99
100
void cmFileSet::CopyEntries(cmFileSet const* fs)
101
0
{
102
0
  cm::append(this->DirectoryEntries, fs->DirectoryEntries);
103
0
  cm::append(this->FileEntries, fs->FileEntries);
104
0
}
105
106
void cmFileSet::ClearDirectoryEntries()
107
0
{
108
0
  this->DirectoryEntries.clear();
109
0
}
110
111
void cmFileSet::AddDirectoryEntry(BT<std::string> directories)
112
0
{
113
0
  this->DirectoryEntries.push_back(std::move(directories));
114
0
}
115
116
void cmFileSet::ClearFileEntries()
117
0
{
118
0
  this->FileEntries.clear();
119
0
}
120
121
void cmFileSet::AddFileEntry(BT<std::string> files)
122
0
{
123
0
  this->FileEntries.push_back(std::move(files));
124
0
}
125
126
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
127
cmFileSet::CompileFileEntries() const
128
0
{
129
0
  std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> result;
130
131
0
  for (auto const& entry : this->FileEntries) {
132
0
    for (auto const& ex : cmList{ entry.Value }) {
133
0
      cmGeneratorExpression ge(*this->GetMakefile()->GetCMakeInstance(),
134
0
                               entry.Backtrace);
135
0
      auto cge = ge.Parse(ex);
136
0
      result.push_back(std::move(cge));
137
0
    }
138
0
  }
139
140
0
  return result;
141
0
}
142
143
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
144
cmFileSet::CompileDirectoryEntries() const
145
0
{
146
0
  std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> result;
147
148
0
  for (auto const& entry : this->DirectoryEntries) {
149
0
    for (auto const& ex : cmList{ entry.Value }) {
150
0
      cmGeneratorExpression ge(*this->GetMakefile()->GetCMakeInstance(),
151
0
                               entry.Backtrace);
152
0
      auto cge = ge.Parse(ex);
153
0
      result.push_back(std::move(cge));
154
0
    }
155
0
  }
156
157
0
  return result;
158
0
}
159
160
std::vector<std::string> cmFileSet::EvaluateDirectoryEntries(
161
  std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& cges,
162
  cm::GenEx::Context const& context, cmGeneratorTarget const* target,
163
  cmGeneratorExpressionDAGChecker* dagChecker) const
164
0
{
165
0
  struct DirCacheEntry
166
0
  {
167
0
    std::string collapsedDir;
168
0
    cm::optional<cmSystemTools::FileId> fileId;
169
0
  };
170
171
0
  std::unordered_map<std::string, DirCacheEntry> dirCache;
172
0
  std::vector<std::string> result;
173
0
  for (auto const& cge : cges) {
174
0
    auto entry = cge->Evaluate(context, dagChecker, target);
175
0
    cmList dirs{ entry };
176
0
    for (std::string dir : dirs) {
177
0
      if (!cmSystemTools::FileIsFullPath(dir)) {
178
0
        dir = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', dir);
179
0
      }
180
181
0
      auto dirCacheResult = dirCache.emplace(dir, DirCacheEntry());
182
0
      auto& dirCacheEntry = dirCacheResult.first->second;
183
0
      auto const isNewCacheEntry = dirCacheResult.second;
184
185
0
      if (isNewCacheEntry) {
186
0
        cmSystemTools::FileId fileId;
187
0
        auto isFileIdValid = cmSystemTools::GetFileId(dir, fileId);
188
0
        dirCacheEntry.collapsedDir = cmSystemTools::CollapseFullPath(dir);
189
0
        dirCacheEntry.fileId =
190
0
          isFileIdValid ? cm::optional<decltype(fileId)>(fileId) : cm::nullopt;
191
0
      }
192
193
0
      for (auto const& priorDir : result) {
194
0
        auto priorDirCacheEntry = dirCache.at(priorDir);
195
0
        bool sameFile = dirCacheEntry.fileId.has_value() &&
196
0
          priorDirCacheEntry.fileId.has_value() &&
197
0
          (*dirCacheEntry.fileId == *priorDirCacheEntry.fileId);
198
0
        if (!sameFile &&
199
0
            (cmSystemTools::IsSubDirectory(dirCacheEntry.collapsedDir,
200
0
                                           priorDirCacheEntry.collapsedDir) ||
201
0
             cmSystemTools::IsSubDirectory(priorDirCacheEntry.collapsedDir,
202
0
                                           dirCacheEntry.collapsedDir))) {
203
0
          context.LG->GetCMakeInstance()->IssueMessage(
204
0
            MessageType::FATAL_ERROR,
205
0
            cmStrCat(
206
0
              "Base directories in file set cannot be subdirectories of each "
207
0
              "other:\n  ",
208
0
              priorDir, "\n  ", dir),
209
0
            cge->GetBacktrace());
210
0
          return {};
211
0
        }
212
0
      }
213
0
      result.push_back(dir);
214
0
    }
215
0
  }
216
0
  return result;
217
0
}
218
219
void cmFileSet::EvaluateFileEntry(
220
  std::vector<std::string> const& dirs,
221
  std::map<std::string, std::vector<std::string>>& filesPerDir,
222
  std::unique_ptr<cmCompiledGeneratorExpression> const& cge,
223
  cm::GenEx::Context const& context, cmGeneratorTarget const* target,
224
  cmGeneratorExpressionDAGChecker* dagChecker) const
225
0
{
226
0
  auto files = cge->Evaluate(context, dagChecker, target);
227
0
  for (std::string file : cmList{ files }) {
228
0
    if (!cmSystemTools::FileIsFullPath(file)) {
229
0
      file = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', file);
230
0
    }
231
0
    auto collapsedFile = cmSystemTools::CollapseFullPath(file);
232
0
    bool found = false;
233
0
    std::string relDir;
234
0
    for (auto const& dir : dirs) {
235
0
      auto collapsedDir = cmSystemTools::CollapseFullPath(dir);
236
0
      if (cmSystemTools::IsSubDirectory(collapsedFile, collapsedDir)) {
237
0
        found = true;
238
0
        relDir = cmSystemTools::GetParentDirectory(
239
0
          cmSystemTools::RelativePath(collapsedDir, collapsedFile));
240
0
        break;
241
0
      }
242
0
    }
243
0
    if (!found) {
244
0
      std::ostringstream e;
245
0
      e << "File:\n  " << file
246
0
        << "\nmust be in one of the file set's base directories:";
247
0
      for (auto const& dir : dirs) {
248
0
        e << "\n  " << dir;
249
0
      }
250
0
      context.LG->GetCMakeInstance()->IssueMessage(
251
0
        MessageType::FATAL_ERROR, e.str(), cge->GetBacktrace());
252
0
      return;
253
0
    }
254
255
0
    filesPerDir[relDir].push_back(file);
256
0
  }
257
0
}
258
259
bool cmFileSet::IsValidName(std::string const& name)
260
0
{
261
0
  static cmsys::RegularExpression const regex("^[a-z0-9][a-zA-Z0-9_]*$");
262
263
0
  cmsys::RegularExpressionMatch match;
264
0
  return regex.find(name.c_str(), match);
265
0
}
266
267
std::string const cmFileSet::propCOMPILE_DEFINITIONS = "COMPILE_DEFINITIONS";
268
std::string const cmFileSet::propCOMPILE_OPTIONS = "COMPILE_OPTIONS";
269
std::string const cmFileSet::propINCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES";
270
271
void cmFileSet::SetProperty(std::string const& prop, cmValue value)
272
0
{
273
0
  if (prop == propINCLUDE_DIRECTORIES) {
274
0
    this->IncludeDirectories.clear();
275
0
    if (value) {
276
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
277
0
      this->IncludeDirectories.emplace_back(value, lfbt);
278
0
    }
279
0
  } else if (prop == propCOMPILE_OPTIONS) {
280
0
    this->CompileOptions.clear();
281
0
    if (value) {
282
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
283
0
      this->CompileOptions.emplace_back(value, lfbt);
284
0
    }
285
0
  } else if (prop == propCOMPILE_DEFINITIONS) {
286
0
    this->CompileDefinitions.clear();
287
0
    if (value) {
288
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
289
0
      this->CompileDefinitions.emplace_back(value, lfbt);
290
0
    }
291
0
  } else {
292
0
    this->Properties.SetProperty(prop, value);
293
0
  }
294
0
}
295
296
void cmFileSet::AppendProperty(std::string const& prop,
297
                               std::string const& value, bool asString)
298
0
{
299
0
  if (prop == propINCLUDE_DIRECTORIES) {
300
0
    if (!value.empty()) {
301
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
302
0
      this->IncludeDirectories.emplace_back(value, lfbt);
303
0
    }
304
0
  } else if (prop == propCOMPILE_OPTIONS) {
305
0
    if (!value.empty()) {
306
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
307
0
      this->CompileOptions.emplace_back(value, lfbt);
308
0
    }
309
0
  } else if (prop == propCOMPILE_DEFINITIONS) {
310
0
    if (!value.empty()) {
311
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
312
0
      this->CompileDefinitions.emplace_back(value, lfbt);
313
0
    }
314
0
  } else {
315
0
    this->Properties.AppendProperty(prop, value, asString);
316
0
  }
317
0
}
318
319
cmValue cmFileSet::GetProperty(std::string const& prop) const
320
0
{
321
  // Check for the properties with backtraces.
322
0
  if (prop == propINCLUDE_DIRECTORIES) {
323
0
    if (this->IncludeDirectories.empty()) {
324
0
      return nullptr;
325
0
    }
326
327
0
    static std::string output;
328
0
    output = cmList::to_string(this->IncludeDirectories);
329
0
    return cmValue(output);
330
0
  }
331
332
0
  if (prop == propCOMPILE_OPTIONS) {
333
0
    if (this->CompileOptions.empty()) {
334
0
      return nullptr;
335
0
    }
336
337
0
    static std::string output;
338
0
    output = cmList::to_string(this->CompileOptions);
339
0
    return cmValue(output);
340
0
  }
341
342
0
  if (prop == propCOMPILE_DEFINITIONS) {
343
0
    if (this->CompileDefinitions.empty()) {
344
0
      return nullptr;
345
0
    }
346
347
0
    static std::string output;
348
0
    output = cmList::to_string(this->CompileDefinitions);
349
0
    return cmValue(output);
350
0
  }
351
352
0
  return this->Properties.GetPropertyValue(prop);
353
0
}