Coverage Report

Created: 2026-10-28 06:38

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 <string>
6
#include <unordered_map>
7
#include <utility>
8
#include <vector>
9
10
#include <cm/optional>
11
#include <cmext/algorithm>
12
#include <cmext/string_view>
13
14
#include "cmExperimental.h"
15
#include "cmGeneratorExpression.h"
16
#include "cmList.h"
17
#include "cmListFileCache.h"
18
#include "cmMakefile.h"
19
#include "cmMessageType.h"
20
#include "cmPolicies.h"
21
#include "cmRange.h"
22
#include "cmStringAlgorithms.h"
23
#include "cmTarget.h"
24
25
namespace Metadata = cm::FileSetMetadata;
26
27
cmFileSet::cmFileSet(cmMakefile* makefile, cmTarget* target, std::string name,
28
                     std::string type, Metadata::Visibility visibility)
29
9.76k
  : Makefile(makefile)
30
9.76k
  , Target(target)
31
9.76k
  , Name(std::move(name))
32
9.76k
  , Type(std::move(type))
33
9.76k
  , Domain(Metadata::IsKnownType(this->Type) ? Metadata::FileSetDomain::NATIVE
34
9.76k
                                             : Metadata::FileSetDomain::RULE)
35
9.76k
  , Visibility(visibility)
36
9.76k
{
37
9.76k
}
38
39
void cmFileSet::CopyEntries(cmFileSet const* fs)
40
0
{
41
0
  cm::append(this->DirectoryEntries, fs->DirectoryEntries);
42
0
  cm::append(this->FileEntries, fs->FileEntries);
43
0
}
44
45
void cmFileSet::ClearDirectoryEntries()
46
0
{
47
0
  this->DirectoryEntries.clear();
48
0
}
49
50
void cmFileSet::AddDirectoryEntry(BT<std::string> directories)
51
9.76k
{
52
9.76k
  this->DirectoryEntries.push_back(std::move(directories));
53
9.76k
}
54
55
void cmFileSet::ClearFileEntries()
56
0
{
57
0
  this->FileEntries.clear();
58
0
}
59
60
void cmFileSet::AddFileEntry(BT<std::string> files)
61
9.76k
{
62
9.76k
  this->FileEntries.push_back(std::move(files));
63
9.76k
}
64
65
cmBTStringRange cmFileSet::GetIncludeDirectories() const
66
5.92k
{
67
5.92k
  return cmMakeRange(this->IncludeDirectories);
68
5.92k
}
69
cmBTStringRange cmFileSet::GetInterfaceIncludeDirectories() const
70
5.92k
{
71
5.92k
  return cmMakeRange(this->InterfaceIncludeDirectories);
72
5.92k
}
73
74
cmBTStringRange cmFileSet::GetCompileOptions() const
75
5.92k
{
76
5.92k
  return cmMakeRange(this->CompileOptions);
77
5.92k
}
78
cmBTStringRange cmFileSet::GetInterfaceCompileOptions() const
79
5.92k
{
80
5.92k
  return cmMakeRange(this->InterfaceCompileOptions);
81
5.92k
}
82
83
cmBTStringRange cmFileSet::GetCompileDefinitions() const
84
5.92k
{
85
5.92k
  return cmMakeRange(this->CompileDefinitions);
86
5.92k
}
87
cmBTStringRange cmFileSet::GetInterfaceCompileDefinitions() const
88
5.92k
{
89
5.92k
  return cmMakeRange(this->InterfaceCompileDefinitions);
90
5.92k
}
91
92
namespace {
93
enum class ReadOnlyCondition
94
{
95
  All,
96
  Imported,
97
  NonImported,
98
};
99
100
struct ReadOnlyProperty
101
{
102
  ReadOnlyProperty(ReadOnlyCondition cond)
103
0
    : Condition{ cond }
104
0
  {
105
0
  }
106
  // ReadOnlyProperty(ReadOnlyCondition cond, cmPolicies::PolicyID id)
107
  //   : Condition{ cond }
108
  //   , Policy{ id }
109
  // {
110
  // }
111
112
  ReadOnlyCondition Condition;
113
  cm::optional<cmPolicies::PolicyID> Policy;
114
115
  std::string message(std::string const& prop, cmTarget* target,
116
                      cmFileSet* fileSet) const
117
0
  {
118
0
    std::string msg;
119
0
    if (this->Condition == ReadOnlyCondition::All) {
120
0
      msg = cmStrCat(" property is read-only for the file set \"",
121
0
                     fileSet->GetName(), " of the target \"");
122
0
    } else if (this->Condition == ReadOnlyCondition::Imported) {
123
0
      msg = " property can't be set on a file set attached to the imported "
124
0
            "target \"";
125
0
    } else if (this->Condition == ReadOnlyCondition::NonImported) {
126
0
      msg =
127
0
        " property can't be set on a file set attached to the non-imported "
128
0
        "target \"";
129
0
    }
130
0
    return cmStrCat(prop, msg, target->GetName(), "\"\n");
131
0
  }
132
133
  bool isReadOnly(std::string const& prop, cmMakefile* context,
134
                  cmTarget* target, cmFileSet* fileSet) const
135
0
  {
136
0
    auto importedTarget = target->IsImported();
137
0
    bool matchingCondition = true;
138
0
    if ((!importedTarget && this->Condition == ReadOnlyCondition::Imported) ||
139
0
        (importedTarget &&
140
0
         this->Condition == ReadOnlyCondition::NonImported)) {
141
0
      matchingCondition = false;
142
0
    }
143
0
    if (!matchingCondition) {
144
      // Not read-only in this scenario
145
0
      return false;
146
0
    }
147
148
0
    bool readOnly = true;
149
0
    if (!this->Policy) {
150
      // No policy associated, so is always read-only
151
0
      context->IssueMessage(MessageType::FATAL_ERROR,
152
0
                            this->message(prop, target, fileSet));
153
0
    }
154
0
    return readOnly;
155
0
  }
156
};
157
158
bool IsSettableProperty(cmMakefile* context, cmTarget* target,
159
                        cmFileSet* fileSet, std::string const& prop)
160
0
{
161
0
  using ROC = ReadOnlyCondition;
162
0
  static std::unordered_map<std::string, ReadOnlyProperty> const readOnlyProps{
163
0
    { "TYPE", { ROC::All } }, { "SCOPE", { ROC::All } }
164
0
  };
165
166
0
  auto it = readOnlyProps.find(prop);
167
168
0
  if (it != readOnlyProps.end()) {
169
0
    return !(it->second.isReadOnly(prop, context, target, fileSet));
170
0
  }
171
0
  return true;
172
0
}
173
174
cm::string_view const BASE_DIRS = "BASE_DIRS"_s;
175
cm::string_view const SOURCES = "SOURCES"_s;
176
cm::string_view const INTERFACE_SOURCES = "INTERFACE_SOURCES"_s;
177
cm::string_view const INCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES"_s;
178
cm::string_view const INTERFACE_INCLUDE_DIRECTORIES =
179
  "INTERFACE_INCLUDE_DIRECTORIES"_s;
180
cm::string_view const COMPILE_DEFINITIONS = "COMPILE_DEFINITIONS"_s;
181
cm::string_view const INTERFACE_COMPILE_DEFINITIONS =
182
  "INTERFACE_COMPILE_DEFINITIONS"_s;
183
cm::string_view const COMPILE_OPTIONS = "COMPILE_OPTIONS"_s;
184
cm::string_view const INTERFACE_COMPILE_OPTIONS =
185
  "INTERFACE_COMPILE_OPTIONS"_s;
186
cm::string_view const LANGUAGE = "LANGUAGE"_s;
187
}
188
189
void cmFileSet::SetProperty(std::string const& prop, cmValue value)
190
0
{
191
0
  if (!IsSettableProperty(this->Makefile, this->Target, this, prop)) {
192
0
    return;
193
0
  }
194
195
0
  if (prop == BASE_DIRS) {
196
0
    this->ClearDirectoryEntries();
197
0
    if (value) {
198
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
199
0
      this->AddDirectoryEntry(BT<std::string>{ value, lfbt });
200
0
    }
201
0
  } else if (prop == SOURCES) {
202
0
    if (!this->IsForSelf()) {
203
0
      return;
204
0
    }
205
0
    this->ClearFileEntries();
206
0
    if (value) {
207
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
208
0
      this->AddFileEntry(BT<std::string>{ value, lfbt });
209
0
    }
210
0
  } else if (prop == INTERFACE_SOURCES) {
211
0
    if (!this->IsForInterface()) {
212
0
      return;
213
0
    }
214
0
    this->ClearFileEntries();
215
0
    if (value) {
216
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
217
0
      this->AddFileEntry(BT<std::string>{ value, lfbt });
218
0
    }
219
0
  } else if (prop == INCLUDE_DIRECTORIES) {
220
0
    if (!this->IsForSelf()) {
221
0
      return;
222
0
    }
223
0
    this->IncludeDirectories.clear();
224
0
    if (value) {
225
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
226
0
      this->IncludeDirectories.emplace_back(value, lfbt);
227
0
    }
228
0
  } else if (prop == INTERFACE_INCLUDE_DIRECTORIES) {
229
0
    if (!this->IsForInterface()) {
230
0
      return;
231
0
    }
232
0
    this->InterfaceIncludeDirectories.clear();
233
0
    if (value) {
234
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
235
0
      this->InterfaceIncludeDirectories.emplace_back(value, lfbt);
236
0
    }
237
0
  } else if (prop == COMPILE_OPTIONS) {
238
0
    if (!this->IsForSelf()) {
239
0
      return;
240
0
    }
241
0
    this->CompileOptions.clear();
242
0
    if (value) {
243
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
244
0
      this->CompileOptions.emplace_back(value, lfbt);
245
0
    }
246
0
  } else if (prop == INTERFACE_COMPILE_OPTIONS) {
247
0
    if (!this->IsForInterface()) {
248
0
      return;
249
0
    }
250
0
    this->InterfaceCompileOptions.clear();
251
0
    if (value) {
252
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
253
0
      this->InterfaceCompileOptions.emplace_back(value, lfbt);
254
0
    }
255
0
  } else if (prop == COMPILE_DEFINITIONS) {
256
0
    if (!this->IsForSelf()) {
257
0
      return;
258
0
    }
259
0
    this->CompileDefinitions.clear();
260
0
    if (value) {
261
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
262
0
      this->CompileDefinitions.emplace_back(value, lfbt);
263
0
    }
264
0
  } else if (prop == INTERFACE_COMPILE_DEFINITIONS) {
265
0
    if (!this->IsForInterface()) {
266
0
      return;
267
0
    }
268
0
    this->InterfaceCompileDefinitions.clear();
269
0
    if (value) {
270
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
271
0
      this->InterfaceCompileDefinitions.emplace_back(value, lfbt);
272
0
    }
273
0
  } else if (prop == LANGUAGE) {
274
0
    if (this->GetDomain() != Metadata::FileSetDomain::RULE &&
275
0
        this->GetType() != Metadata::SOURCES &&
276
0
        this->GetType() != Metadata::HEADERS) {
277
0
      this->Makefile->IssueMessage(
278
0
        MessageType::FATAL_ERROR,
279
0
        "LANGUAGE property may only be set on a SOURCES, HEADERS, or custom "
280
0
        "rule file set.");
281
0
      return;
282
0
    }
283
0
    if (this->GetType() == Metadata::HEADERS &&
284
0
        !cmExperimental::HasSupportEnabled(*this->Makefile,
285
0
                                           cmExperimental::Feature::SYCL)) {
286
0
      this->Makefile->IssueMessage(
287
0
        MessageType::FATAL_ERROR,
288
0
        "LANGUAGE property on a HEADERS file set is experimental and must be "
289
0
        "enabled by the CMAKE_EXPERIMENTAL_SYCL gate.");
290
0
      return;
291
0
    }
292
0
    if (value && cmGeneratorExpression::Find(*value) != std::string::npos) {
293
0
      this->Makefile->IssueMessage(
294
0
        MessageType::FATAL_ERROR,
295
0
        "LANGUAGE file set property does not support generator expressions.");
296
0
      return;
297
0
    }
298
0
    this->Properties.SetProperty(prop, value);
299
0
  } else {
300
0
    this->Properties.SetProperty(prop, value);
301
0
  }
302
0
}
303
304
void cmFileSet::AppendProperty(std::string const& prop,
305
                               std::string const& value, bool asString)
306
0
{
307
0
  if (!IsSettableProperty(this->Makefile, this->Target, this, prop)) {
308
0
    return;
309
0
  }
310
311
0
  if (prop == BASE_DIRS) {
312
0
    if (!value.empty()) {
313
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
314
0
      this->AddDirectoryEntry(BT<std::string>{ value, lfbt });
315
0
    }
316
0
  } else if (prop == SOURCES) {
317
0
    if (!this->IsForSelf()) {
318
0
      return;
319
0
    }
320
0
    if (!value.empty()) {
321
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
322
0
      this->AddFileEntry(BT<std::string>{ value, lfbt });
323
0
    }
324
0
  } else if (prop == INTERFACE_SOURCES) {
325
0
    if (!this->IsForInterface()) {
326
0
      return;
327
0
    }
328
0
    if (!value.empty()) {
329
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
330
0
      this->AddFileEntry(BT<std::string>{ value, lfbt });
331
0
    }
332
0
  } else if (prop == INCLUDE_DIRECTORIES) {
333
0
    if (!this->IsForSelf()) {
334
0
      return;
335
0
    }
336
0
    if (!value.empty()) {
337
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
338
0
      this->IncludeDirectories.emplace_back(value, lfbt);
339
0
    }
340
0
  } else if (prop == INTERFACE_INCLUDE_DIRECTORIES) {
341
0
    if (!this->IsForInterface()) {
342
0
      return;
343
0
    }
344
0
    if (!value.empty()) {
345
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
346
0
      this->InterfaceIncludeDirectories.emplace_back(value, lfbt);
347
0
    }
348
0
  } else if (prop == COMPILE_OPTIONS) {
349
0
    if (!this->IsForSelf()) {
350
0
      return;
351
0
    }
352
0
    if (!value.empty()) {
353
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
354
0
      this->CompileOptions.emplace_back(value, lfbt);
355
0
    }
356
0
  } else if (prop == INTERFACE_COMPILE_OPTIONS) {
357
0
    if (!this->IsForInterface()) {
358
0
      return;
359
0
    }
360
0
    if (!value.empty()) {
361
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
362
0
      this->InterfaceCompileOptions.emplace_back(value, lfbt);
363
0
    }
364
0
  } else if (prop == COMPILE_DEFINITIONS) {
365
0
    if (!this->IsForSelf()) {
366
0
      return;
367
0
    }
368
0
    if (!value.empty()) {
369
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
370
0
      this->CompileDefinitions.emplace_back(value, lfbt);
371
0
    }
372
0
  } else if (prop == INTERFACE_COMPILE_DEFINITIONS) {
373
0
    if (!this->IsForInterface()) {
374
0
      return;
375
0
    }
376
0
    if (!value.empty()) {
377
0
      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
378
0
      this->InterfaceCompileDefinitions.emplace_back(value, lfbt);
379
0
    }
380
0
  } else if (prop == LANGUAGE) {
381
0
    this->Makefile->IssueMessage(
382
0
      MessageType::FATAL_ERROR,
383
0
      "LANGUAGE file set property does not support appending values.");
384
0
  } else {
385
0
    this->Properties.AppendProperty(prop, value, asString);
386
0
  }
387
0
}
388
389
cmValue cmFileSet::GetProperty(std::string const& prop) const
390
535
{
391
  // Check for the properties with backtraces.
392
535
  if (prop == BASE_DIRS) {
393
394
0
    static std::string output;
395
0
    output = cmList::to_string(this->GetDirectoryEntries());
396
0
    return cmValue(output);
397
0
  }
398
535
  if (prop == SOURCES) {
399
0
    if (!this->IsForSelf() || this->GetFileEntries().empty()) {
400
0
      return nullptr;
401
0
    }
402
403
0
    static std::string output;
404
0
    output = cmList::to_string(this->GetFileEntries());
405
0
    return cmValue(output);
406
0
  }
407
535
  if (prop == INTERFACE_SOURCES) {
408
0
    if (!this->IsForInterface() || this->GetFileEntries().empty()) {
409
0
      return nullptr;
410
0
    }
411
412
0
    static std::string output;
413
0
    output = cmList::to_string(this->GetFileEntries());
414
0
    return cmValue(output);
415
0
  }
416
417
535
  if (prop == INCLUDE_DIRECTORIES) {
418
0
    if (this->IncludeDirectories.empty()) {
419
0
      return nullptr;
420
0
    }
421
422
0
    static std::string output;
423
0
    output = cmList::to_string(this->IncludeDirectories);
424
0
    return cmValue(output);
425
0
  }
426
427
535
  if (prop == INTERFACE_INCLUDE_DIRECTORIES) {
428
0
    if (this->InterfaceIncludeDirectories.empty()) {
429
0
      return nullptr;
430
0
    }
431
432
0
    static std::string output;
433
0
    output = cmList::to_string(this->InterfaceIncludeDirectories);
434
0
    return cmValue(output);
435
0
  }
436
437
535
  if (prop == COMPILE_OPTIONS) {
438
0
    if (this->CompileOptions.empty()) {
439
0
      return nullptr;
440
0
    }
441
442
0
    static std::string output;
443
0
    output = cmList::to_string(this->CompileOptions);
444
0
    return cmValue(output);
445
0
  }
446
447
535
  if (prop == INTERFACE_COMPILE_OPTIONS) {
448
0
    if (this->InterfaceCompileOptions.empty()) {
449
0
      return nullptr;
450
0
    }
451
452
0
    static std::string output;
453
0
    output = cmList::to_string(this->InterfaceCompileOptions);
454
0
    return cmValue(output);
455
0
  }
456
457
535
  if (prop == COMPILE_DEFINITIONS) {
458
0
    if (this->CompileDefinitions.empty()) {
459
0
      return nullptr;
460
0
    }
461
462
0
    static std::string output;
463
0
    output = cmList::to_string(this->CompileDefinitions);
464
0
    return cmValue(output);
465
0
  }
466
467
535
  if (prop == INTERFACE_COMPILE_DEFINITIONS) {
468
0
    if (this->InterfaceCompileDefinitions.empty()) {
469
0
      return nullptr;
470
0
    }
471
472
0
    static std::string output;
473
0
    output = cmList::to_string(this->InterfaceCompileDefinitions);
474
0
    return cmValue(output);
475
0
  }
476
477
535
  if (prop == "TYPE"_s) {
478
0
    return cmValue{ this->GetType() };
479
0
  }
480
535
  if (prop == "SCOPE"_s) {
481
0
    static std::string scope =
482
0
      std::string{ Metadata::VisibilityToName(this->GetVisibility()) };
483
0
    return cmValue{ scope };
484
0
  }
485
486
535
  return this->Properties.GetPropertyValue(prop);
487
535
}