Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmSourceFile.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 "cmSourceFile.h"
4
5
#include <utility>
6
7
#include <cm/string_view>
8
#include <cmext/string_view>
9
10
#include "cmGlobalGenerator.h"
11
#include "cmList.h"
12
#include "cmListFileCache.h"
13
#include "cmMakefile.h"
14
#include "cmMessageType.h"
15
#include "cmPolicies.h"
16
#include "cmProperty.h"
17
#include "cmState.h"
18
#include "cmStringAlgorithms.h"
19
#include "cmSystemTools.h"
20
#include "cmValue.h"
21
#include "cmake.h"
22
23
cmSourceFile::cmSourceFile(cmMakefile* mf, std::string const& name,
24
                           bool generated, cmSourceFileLocationKind kind)
25
24.0k
  : Location(mf, name, (!generated) ? kind : cmSourceFileLocationKind::Known)
26
24.0k
{
27
24.0k
  if (generated) {
28
12.2k
    this->MarkAsGenerated();
29
12.2k
  }
30
24.0k
}
31
32
void cmSourceFile::SetSpecialSourceType(cmSourceFile::SpecialSourceType type)
33
1.12k
{
34
1.12k
  this->SpecialSource = type;
35
1.12k
}
36
37
bool cmSourceFile::IsPchHeader() const
38
0
{
39
0
  return this->SpecialSource == SpecialSourceType::PchHeader;
40
0
}
41
42
bool cmSourceFile::IsPchSource() const
43
0
{
44
0
  return this->SpecialSource == SpecialSourceType::PchSource;
45
0
}
46
47
std::string const& cmSourceFile::GetExtension() const
48
1.31k
{
49
1.31k
  return this->Extension;
50
1.31k
}
51
52
std::string const cmSourceFile::propLANGUAGE = "LANGUAGE";
53
std::string const cmSourceFile::propLOCATION = "LOCATION";
54
std::string const cmSourceFile::propGENERATED = "GENERATED";
55
std::string const cmSourceFile::propCOMPILE_DEFINITIONS =
56
  "COMPILE_DEFINITIONS";
57
std::string const cmSourceFile::propCOMPILE_OPTIONS = "COMPILE_OPTIONS";
58
std::string const cmSourceFile::propINCLUDE_DIRECTORIES =
59
  "INCLUDE_DIRECTORIES";
60
61
void cmSourceFile::SetObjectLibrary(std::string const& objlib)
62
1.12k
{
63
1.12k
  this->ObjectLibrary = objlib;
64
1.12k
}
65
66
std::string cmSourceFile::GetObjectLibrary() const
67
0
{
68
0
  return this->ObjectLibrary;
69
0
}
70
71
std::string const& cmSourceFile::GetOrDetermineLanguage()
72
1.31k
{
73
  // If the language was set explicitly by the user then use it.
74
1.31k
  if (cmValue lang = this->GetProperty(propLANGUAGE)) {
75
    // Assign to member in order to return a reference.
76
0
    this->Language = *lang;
77
0
    return this->Language;
78
0
  }
79
80
  // Perform computation needed to get the language if necessary.
81
1.31k
  if (this->Language.empty()) {
82
    // If a known extension is given or a known full path is given then trust
83
    // that the current extension is sufficient to determine the language. This
84
    // will fail only if the user specifies a full path to the source but
85
    // leaves off the extension, which is kind of weird.
86
0
    if (this->FullPath.empty() && this->Location.ExtensionIsAmbiguous() &&
87
0
        this->Location.DirectoryIsAmbiguous()) {
88
      // Finalize the file location to get the extension and set the language.
89
0
      this->ResolveFullPath();
90
0
    } else {
91
      // Use the known extension to get the language if possible.
92
0
      cm::string_view ext =
93
0
        cmSystemTools::GetFilenameLastExtensionView(this->Location.GetName());
94
0
      this->CheckLanguage(ext);
95
0
    }
96
0
  }
97
98
  // Use the language determined from the file extension.
99
1.31k
  return this->Language;
100
1.31k
}
101
102
std::string cmSourceFile::GetLanguage() const
103
1.91k
{
104
  // If the language was set explicitly by the user then use it.
105
1.91k
  if (cmValue lang = this->GetProperty(propLANGUAGE)) {
106
0
    return *lang;
107
0
  }
108
109
  // Use the language determined from the file extension.
110
1.91k
  return this->Language;
111
1.91k
}
112
113
cmSourceFileLocation const& cmSourceFile::GetLocation() const
114
24.0k
{
115
24.0k
  return this->Location;
116
24.0k
}
117
118
std::string const& cmSourceFile::ResolveFullPath(std::string* error,
119
                                                 std::string* cmp0115Warning)
120
21.8k
{
121
21.8k
  if (this->FullPath.empty()) {
122
12.9k
    if (this->FindFullPath(error, cmp0115Warning)) {
123
12.9k
      this->CheckExtension();
124
12.9k
    }
125
12.9k
  }
126
21.8k
  return this->FullPath;
127
21.8k
}
128
129
std::string const& cmSourceFile::GetFullPath() const
130
3.22k
{
131
3.22k
  return this->FullPath;
132
3.22k
}
133
134
bool cmSourceFile::FindFullPath(std::string* error,
135
                                std::string* cmp0115Warning)
136
12.9k
{
137
  // If the file is generated compute the location without checking on disk.
138
  // Note: We also check for a locally set GENERATED property, because
139
  //       it might have been set before policy CMP0118 (or CMP0163) was set
140
  //       to NEW.
141
12.9k
  if (this->GetIsGenerated(CheckScope::GlobalAndLocal)) {
142
    // The file is either already a full path or is relative to the
143
    // build directory for the target.
144
12.2k
    this->Location.DirectoryUseBinary();
145
12.2k
    this->FullPath = this->Location.GetFullPath();
146
12.2k
    this->FindFullPathFailed = false;
147
12.2k
    return true;
148
12.2k
  }
149
150
  // If this method has already failed once do not try again.
151
725
  if (this->FindFullPathFailed) {
152
0
    return false;
153
0
  }
154
155
  // The file is not generated.  It must exist on disk.
156
725
  cmMakefile const* makefile = this->Location.GetMakefile();
157
  // Location path
158
725
  std::string const& lPath = this->Location.GetFullPath();
159
  // List of extension lists
160
725
  std::vector<std::string> exts =
161
725
    makefile->GetCMakeInstance()->GetAllExtensions();
162
725
  auto cmp0115 = makefile->GetPolicyStatus(cmPolicies::CMP0115);
163
725
  auto cmp0163 = makefile->GetPolicyStatus(cmPolicies::CMP0163);
164
725
  auto cmp0118 = makefile->GetPolicyStatus(cmPolicies::CMP0118);
165
725
  bool const cmp0163new =
166
725
    cmp0163 != cmPolicies::OLD && cmp0163 != cmPolicies::WARN;
167
725
  bool const cmp0118new =
168
725
    cmp0163new || (cmp0118 != cmPolicies::OLD && cmp0118 != cmPolicies::WARN);
169
170
  // Tries to find the file in a given directory
171
725
  auto findInDir = [this, &exts, &lPath, cmp0115, cmp0115Warning, cmp0118new,
172
725
                    makefile](std::string const& dir) -> bool {
173
    // Compute full path
174
725
    std::string const fullPath = cmSystemTools::CollapseFullPath(lPath, dir);
175
    // Try full path
176
    // Is this file globally marked as generated? Then mark so locally.
177
725
    if (cmp0118new &&
178
725
        makefile->GetGlobalGenerator()->IsGeneratedFile(fullPath)) {
179
0
      this->IsGenerated = true;
180
0
    }
181
725
    if (this->IsGenerated || cmSystemTools::FileExists(fullPath)) {
182
725
      this->FullPath = fullPath;
183
725
      return true;
184
725
    }
185
    // This has to be an if statement due to a bug in Oracle Developer Studio.
186
    // See https://community.oracle.com/tech/developers/discussion/4476246/
187
    // for details.
188
0
    if (cmp0115 == cmPolicies::OLD || cmp0115 == cmPolicies::WARN) {
189
      // Try full path with extension
190
0
      for (std::string const& ext : exts) {
191
0
        if (!ext.empty()) {
192
0
          std::string extPath = cmStrCat(fullPath, '.', ext);
193
          // Is this file globally marked as generated? Then mark so locally.
194
0
          if (cmp0118new &&
195
0
              makefile->GetGlobalGenerator()->IsGeneratedFile(extPath)) {
196
0
            this->IsGenerated = true;
197
0
          }
198
0
          if (this->IsGenerated || cmSystemTools::FileExists(extPath)) {
199
0
            this->FullPath = extPath;
200
0
            if (cmp0115 == cmPolicies::WARN) {
201
0
              std::string warning = cmStrCat("File:\n  "_s, extPath);
202
0
              if (cmp0115Warning) {
203
0
                *cmp0115Warning = std::move(warning);
204
0
              } else {
205
0
                makefile->IssuePolicyWarning(cmPolicies::CMP0115, {}, warning,
206
0
                                             cmListFileBacktrace{});
207
0
              }
208
0
            }
209
0
            return true;
210
0
          }
211
0
        }
212
0
      }
213
0
    }
214
    // File not found
215
0
    return false;
216
0
  };
217
218
  // Try to find the file in various directories
219
725
  if (this->Location.DirectoryIsAmbiguous()) {
220
725
    if (findInDir(makefile->GetCurrentSourceDirectory()) ||
221
725
        findInDir(makefile->GetCurrentBinaryDirectory())) {
222
725
      return true;
223
725
    }
224
725
  } else {
225
0
    if (findInDir({})) {
226
0
      return true;
227
0
    }
228
0
  }
229
230
  // Compose error
231
0
  std::string err = cmStrCat("Cannot find source file:\n  ", lPath);
232
0
  switch (cmp0115) {
233
0
    case cmPolicies::OLD:
234
0
    case cmPolicies::WARN:
235
0
      err = cmStrCat(std::move(err), "\nTried extensions");
236
0
      for (auto const& ext : exts) {
237
0
        err = cmStrCat(std::move(err), " .", ext);
238
0
      }
239
0
      break;
240
0
    case cmPolicies::NEW:
241
0
      break;
242
0
  }
243
0
  if (lPath == "FILE_SET"_s) {
244
0
    err += "\nHint: the FILE_SET keyword may only appear after a visibility "
245
0
           "specifier or another FILE_SET within the target_sources() "
246
0
           "command.";
247
0
  }
248
0
  if (error) {
249
0
    *error = std::move(err);
250
0
  } else {
251
0
    makefile->IssueMessage(MessageType::FATAL_ERROR, err);
252
0
  }
253
0
  this->FindFullPathFailed = true;
254
255
  // File not found
256
0
  return false;
257
0
}
258
259
void cmSourceFile::CheckExtension()
260
12.9k
{
261
  // Compute the extension.
262
12.9k
  cm::string_view realExt =
263
12.9k
    cmSystemTools::GetFilenameLastExtensionView(this->FullPath);
264
12.9k
  if (!realExt.empty()) {
265
    // Store the extension without the leading '.'.
266
6.66k
    this->Extension = std::string(realExt.substr(1));
267
6.66k
  }
268
269
  // Look for object files.
270
12.9k
  if (this->Extension == "obj" || this->Extension == "o" ||
271
12.9k
      this->Extension == "lo") {
272
0
    this->SetProperty("EXTERNAL_OBJECT", "1");
273
0
  }
274
275
  // Try to identify the source file language from the extension.
276
12.9k
  if (this->Language.empty()) {
277
12.9k
    this->CheckLanguage(this->Extension);
278
12.9k
  }
279
12.9k
}
280
281
void cmSourceFile::CheckLanguage(cm::string_view ext)
282
12.9k
{
283
  // Try to identify the source file language from the extension.
284
12.9k
  cmMakefile const* mf = this->Location.GetMakefile();
285
12.9k
  cmGlobalGenerator* gg = mf->GetGlobalGenerator();
286
12.9k
  cm::string_view l = gg->GetLanguageFromExtension(ext);
287
12.9k
  if (!l.empty()) {
288
725
    this->Language = std::string(l);
289
725
  }
290
12.9k
}
291
292
bool cmSourceFile::Matches(cmSourceFileLocation const& loc)
293
34.0k
{
294
34.0k
  return this->Location.Matches(loc);
295
34.0k
}
296
297
void cmSourceFile::SetProperty(std::string const& prop, cmValue value)
298
24.8k
{
299
24.8k
  if (prop == propINCLUDE_DIRECTORIES) {
300
0
    this->IncludeDirectories.clear();
301
0
    if (value) {
302
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
303
0
      this->IncludeDirectories.emplace_back(value, lfbt);
304
0
    }
305
24.8k
  } else if (prop == propCOMPILE_OPTIONS) {
306
0
    this->CompileOptions.clear();
307
0
    if (value) {
308
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
309
0
      this->CompileOptions.emplace_back(value, lfbt);
310
0
    }
311
24.8k
  } else if (prop == propCOMPILE_DEFINITIONS) {
312
5.92k
    this->CompileDefinitions.clear();
313
5.92k
    if (value) {
314
5.92k
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
315
5.92k
      this->CompileDefinitions.emplace_back(value, lfbt);
316
5.92k
    }
317
18.9k
  } else {
318
18.9k
    this->Properties.SetProperty(prop, value);
319
18.9k
  }
320
24.8k
}
321
322
void cmSourceFile::AppendProperty(std::string const& prop,
323
                                  std::string const& value, bool asString)
324
0
{
325
0
  if (prop == propINCLUDE_DIRECTORIES) {
326
0
    if (!value.empty()) {
327
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
328
0
      this->IncludeDirectories.emplace_back(value, lfbt);
329
0
    }
330
0
  } else if (prop == propCOMPILE_OPTIONS) {
331
0
    if (!value.empty()) {
332
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
333
0
      this->CompileOptions.emplace_back(value, lfbt);
334
0
    }
335
0
  } else if (prop == propCOMPILE_DEFINITIONS) {
336
0
    if (!value.empty()) {
337
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
338
0
      this->CompileDefinitions.emplace_back(value, lfbt);
339
0
    }
340
0
  } else {
341
0
    this->Properties.AppendProperty(prop, value, asString);
342
0
  }
343
0
}
344
345
cmValue cmSourceFile::GetPropertyForUser(std::string const& prop)
346
179
{
347
  // This method is a consequence of design history and backwards
348
  // compatibility.  GetProperty is (and should be) a const method.
349
  // Computed properties should not be stored back in the property map
350
  // but instead reference information already known.  If they need to
351
  // cache information in a mutable ivar to provide the return string
352
  // safely then so be it.
353
  //
354
  // The LOCATION property is particularly problematic.  The CMake
355
  // language has very loose restrictions on the names that will match
356
  // a given source file (for historical reasons).  Implementing
357
  // lookups correctly with such loose naming requires the
358
  // cmSourceFileLocation class to commit to a particular full path to
359
  // the source file as late as possible.  If the users requests the
360
  // LOCATION property we must commit now.
361
179
  if (prop == propLOCATION) {
362
    // Commit to a location.
363
0
    this->ResolveFullPath();
364
0
  }
365
366
  // Similarly, LANGUAGE can be determined by the file extension
367
  // if it is requested by the user.
368
179
  if (prop == propLANGUAGE) {
369
    // The pointer is valid until `this->Language` is modified.
370
0
    return cmValue(this->GetOrDetermineLanguage());
371
0
  }
372
373
  // Special handling for GENERATED property.
374
179
  if (prop == propGENERATED) {
375
    // We need to check policy CMP0163 and CMP0118 in order to determine if we
376
    // need to possibly consider the value of a locally set GENERATED property,
377
    // too.
378
0
    auto cmp0163 =
379
0
      this->Location.GetMakefile()->GetPolicyStatus(cmPolicies::CMP0163);
380
0
    auto cmp0118 =
381
0
      this->Location.GetMakefile()->GetPolicyStatus(cmPolicies::CMP0118);
382
0
    bool const cmp0163new =
383
0
      cmp0163 != cmPolicies::OLD && cmp0163 != cmPolicies::WARN;
384
0
    bool const cmp0118new = cmp0163new ||
385
0
      (cmp0118 != cmPolicies::OLD && cmp0118 != cmPolicies::WARN);
386
0
    if (this->GetIsGenerated((!cmp0118new) ? CheckScope::GlobalAndLocal
387
0
                                           : CheckScope::Global)) {
388
0
      return cmValue::True;
389
0
    }
390
0
    return cmValue::False;
391
0
  }
392
393
  // Perform the normal property lookup.
394
179
  return this->GetProperty(prop);
395
179
}
396
397
cmValue cmSourceFile::GetProperty(std::string const& prop) const
398
6.75k
{
399
  // Check for computed properties.
400
6.75k
  if (prop == propLOCATION) {
401
0
    if (this->FullPath.empty()) {
402
0
      return nullptr;
403
0
    }
404
0
    return cmValue(this->FullPath);
405
0
  }
406
407
  // Check for the properties with backtraces.
408
6.75k
  if (prop == propINCLUDE_DIRECTORIES) {
409
0
    if (this->IncludeDirectories.empty()) {
410
0
      return nullptr;
411
0
    }
412
413
0
    static std::string output;
414
0
    output = cmList::to_string(this->IncludeDirectories);
415
0
    return cmValue(output);
416
0
  }
417
418
6.75k
  if (prop == propCOMPILE_OPTIONS) {
419
0
    if (this->CompileOptions.empty()) {
420
0
      return nullptr;
421
0
    }
422
423
0
    static std::string output;
424
0
    output = cmList::to_string(this->CompileOptions);
425
0
    return cmValue(output);
426
0
  }
427
428
6.75k
  if (prop == propCOMPILE_DEFINITIONS) {
429
100
    if (this->CompileDefinitions.empty()) {
430
0
      return nullptr;
431
0
    }
432
433
100
    static std::string output;
434
100
    output = cmList::to_string(this->CompileDefinitions);
435
100
    return cmValue(output);
436
100
  }
437
438
6.65k
  cmValue retVal = this->Properties.GetPropertyValue(prop);
439
6.65k
  if (!retVal) {
440
6.65k
    cmMakefile const* mf = this->Location.GetMakefile();
441
6.65k
    bool const chain =
442
6.65k
      mf->GetState()->IsPropertyChained(prop, cmProperty::SOURCE_FILE);
443
6.65k
    if (chain) {
444
0
      return mf->GetProperty(prop, chain);
445
0
    }
446
6.65k
    return nullptr;
447
6.65k
  }
448
449
0
  return retVal;
450
6.65k
}
451
452
std::string const& cmSourceFile::GetSafeProperty(std::string const& prop) const
453
0
{
454
0
  cmValue ret = this->GetProperty(prop);
455
0
  if (ret) {
456
0
    return *ret;
457
0
  }
458
459
0
  static std::string const s_empty;
460
0
  return s_empty;
461
0
}
462
463
bool cmSourceFile::GetPropertyAsBool(std::string const& prop) const
464
3.34k
{
465
3.34k
  return this->GetProperty(prop).IsOn();
466
3.34k
}
467
468
void cmSourceFile::MarkAsGenerated()
469
18.1k
{
470
18.1k
  this->IsGenerated = true;
471
18.1k
  auto const& mf = *this->Location.GetMakefile();
472
18.1k
  mf.GetGlobalGenerator()->MarkAsGeneratedFile(this->ResolveFullPath());
473
18.1k
}
474
475
bool cmSourceFile::GetIsGenerated(CheckScope checkScope) const
476
12.9k
{
477
12.9k
  if (this->IsGenerated) {
478
    // Globally marked as generated!
479
12.2k
    return true;
480
12.2k
  }
481
725
  if (checkScope == CheckScope::GlobalAndLocal) {
482
    // Check locally stored properties.
483
725
    return this->GetPropertyAsBool(propGENERATED);
484
725
  }
485
0
  return false;
486
725
}
487
488
void cmSourceFile::SetProperties(cmPropertyMap properties)
489
0
{
490
0
  this->Properties = std::move(properties);
491
0
}
492
493
cmCustomCommand* cmSourceFile::GetCustomCommand() const
494
1.31k
{
495
1.31k
  return this->CustomCommand.get();
496
1.31k
}
497
498
void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
499
5.92k
{
500
5.92k
  this->CustomCommand = std::move(cc);
501
5.92k
}
502
503
cmValue cmSourceFile::GetRustEmitProperty() const
504
0
{
505
0
  static std::string const s_default = "link";
506
0
  cmValue const value = this->GetProperty("Rust_EMIT");
507
0
  if (!value || value->empty()) {
508
0
    return cmValue(s_default);
509
0
  }
510
0
  return value;
511
0
}