Coverage Report

Created: 2026-06-15 07:03

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