Coverage Report

Created: 2026-04-29 07:01

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