Coverage Report

Created: 2026-03-12 06:35

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 =
204
0
                cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0115),
205
0
                         "\nFile:\n  ", extPath);
206
0
              if (cmp0115Warning) {
207
0
                *cmp0115Warning = std::move(warning);
208
0
              } else {
209
0
                makefile->GetCMakeInstance()->IssueMessage(
210
0
                  MessageType::AUTHOR_WARNING, warning);
211
0
              }
212
0
            }
213
0
            return true;
214
0
          }
215
0
        }
216
0
      }
217
0
    }
218
    // File not found
219
0
    return false;
220
0
  };
221
222
  // Try to find the file in various directories
223
0
  if (this->Location.DirectoryIsAmbiguous()) {
224
0
    if (findInDir(makefile->GetCurrentSourceDirectory()) ||
225
0
        findInDir(makefile->GetCurrentBinaryDirectory())) {
226
0
      return true;
227
0
    }
228
0
  } else {
229
0
    if (findInDir({})) {
230
0
      return true;
231
0
    }
232
0
  }
233
234
  // Compose error
235
0
  std::string err = cmStrCat("Cannot find source file:\n  ", lPath);
236
0
  switch (cmp0115) {
237
0
    case cmPolicies::OLD:
238
0
    case cmPolicies::WARN:
239
0
      err = cmStrCat(err, "\nTried extensions");
240
0
      for (auto const& ext : exts) {
241
0
        err = cmStrCat(err, " .", ext);
242
0
      }
243
0
      break;
244
0
    case cmPolicies::NEW:
245
0
      break;
246
0
  }
247
0
  if (lPath == "FILE_SET"_s) {
248
0
    err += "\nHint: the FILE_SET keyword may only appear after a visibility "
249
0
           "specifier or another FILE_SET within the target_sources() "
250
0
           "command.";
251
0
  }
252
0
  if (error) {
253
0
    *error = std::move(err);
254
0
  } else {
255
0
    makefile->IssueMessage(MessageType::FATAL_ERROR, err);
256
0
  }
257
0
  this->FindFullPathFailed = true;
258
259
  // File not found
260
0
  return false;
261
0
}
262
263
void cmSourceFile::CheckExtension()
264
0
{
265
  // Compute the extension.
266
0
  cm::string_view realExt =
267
0
    cmSystemTools::GetFilenameLastExtensionView(this->FullPath);
268
0
  if (!realExt.empty()) {
269
    // Store the extension without the leading '.'.
270
0
    this->Extension = std::string(realExt.substr(1));
271
0
  }
272
273
  // Look for object files.
274
0
  if (this->Extension == "obj" || this->Extension == "o" ||
275
0
      this->Extension == "lo") {
276
0
    this->SetProperty("EXTERNAL_OBJECT", "1");
277
0
  }
278
279
  // Try to identify the source file language from the extension.
280
0
  if (this->Language.empty()) {
281
0
    this->CheckLanguage(this->Extension);
282
0
  }
283
0
}
284
285
void cmSourceFile::CheckLanguage(cm::string_view ext)
286
0
{
287
  // Try to identify the source file language from the extension.
288
0
  cmMakefile const* mf = this->Location.GetMakefile();
289
0
  cmGlobalGenerator* gg = mf->GetGlobalGenerator();
290
0
  cm::string_view l = gg->GetLanguageFromExtension(ext);
291
0
  if (!l.empty()) {
292
0
    this->Language = std::string(l);
293
0
  }
294
0
}
295
296
bool cmSourceFile::Matches(cmSourceFileLocation const& loc)
297
0
{
298
0
  return this->Location.Matches(loc);
299
0
}
300
301
void cmSourceFile::SetProperty(std::string const& prop, cmValue value)
302
0
{
303
0
  if (prop == propINCLUDE_DIRECTORIES) {
304
0
    this->IncludeDirectories.clear();
305
0
    if (value) {
306
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
307
0
      this->IncludeDirectories.emplace_back(value, lfbt);
308
0
    }
309
0
  } else if (prop == propCOMPILE_OPTIONS) {
310
0
    this->CompileOptions.clear();
311
0
    if (value) {
312
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
313
0
      this->CompileOptions.emplace_back(value, lfbt);
314
0
    }
315
0
  } else if (prop == propCOMPILE_DEFINITIONS) {
316
0
    this->CompileDefinitions.clear();
317
0
    if (value) {
318
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
319
0
      this->CompileDefinitions.emplace_back(value, lfbt);
320
0
    }
321
0
  } else {
322
0
    this->Properties.SetProperty(prop, value);
323
0
  }
324
0
}
325
326
void cmSourceFile::AppendProperty(std::string const& prop,
327
                                  std::string const& value, bool asString)
328
0
{
329
0
  if (prop == propINCLUDE_DIRECTORIES) {
330
0
    if (!value.empty()) {
331
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
332
0
      this->IncludeDirectories.emplace_back(value, lfbt);
333
0
    }
334
0
  } else if (prop == propCOMPILE_OPTIONS) {
335
0
    if (!value.empty()) {
336
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
337
0
      this->CompileOptions.emplace_back(value, lfbt);
338
0
    }
339
0
  } else if (prop == propCOMPILE_DEFINITIONS) {
340
0
    if (!value.empty()) {
341
0
      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
342
0
      this->CompileDefinitions.emplace_back(value, lfbt);
343
0
    }
344
0
  } else {
345
0
    this->Properties.AppendProperty(prop, value, asString);
346
0
  }
347
0
}
348
349
cmValue cmSourceFile::GetPropertyForUser(std::string const& prop)
350
0
{
351
  // This method is a consequence of design history and backwards
352
  // compatibility.  GetProperty is (and should be) a const method.
353
  // Computed properties should not be stored back in the property map
354
  // but instead reference information already known.  If they need to
355
  // cache information in a mutable ivar to provide the return string
356
  // safely then so be it.
357
  //
358
  // The LOCATION property is particularly problematic.  The CMake
359
  // language has very loose restrictions on the names that will match
360
  // a given source file (for historical reasons).  Implementing
361
  // lookups correctly with such loose naming requires the
362
  // cmSourceFileLocation class to commit to a particular full path to
363
  // the source file as late as possible.  If the users requests the
364
  // LOCATION property we must commit now.
365
0
  if (prop == propLOCATION) {
366
    // Commit to a location.
367
0
    this->ResolveFullPath();
368
0
  }
369
370
  // Similarly, LANGUAGE can be determined by the file extension
371
  // if it is requested by the user.
372
0
  if (prop == propLANGUAGE) {
373
    // The pointer is valid until `this->Language` is modified.
374
0
    return cmValue(this->GetOrDetermineLanguage());
375
0
  }
376
377
  // Special handling for GENERATED property.
378
0
  if (prop == propGENERATED) {
379
    // We need to check policy CMP0163 and CMP0118 in order to determine if we
380
    // need to possibly consider the value of a locally set GENERATED property,
381
    // too.
382
0
    auto cmp0163 =
383
0
      this->Location.GetMakefile()->GetPolicyStatus(cmPolicies::CMP0163);
384
0
    auto cmp0118 =
385
0
      this->Location.GetMakefile()->GetPolicyStatus(cmPolicies::CMP0118);
386
0
    bool const cmp0163new =
387
0
      cmp0163 != cmPolicies::OLD && cmp0163 != cmPolicies::WARN;
388
0
    bool const cmp0118new = cmp0163new ||
389
0
      (cmp0118 != cmPolicies::OLD && cmp0118 != cmPolicies::WARN);
390
0
    if (this->GetIsGenerated((!cmp0118new) ? CheckScope::GlobalAndLocal
391
0
                                           : CheckScope::Global)) {
392
0
      return cmValue(propTRUE);
393
0
    }
394
0
    return cmValue(propFALSE);
395
0
  }
396
397
  // Perform the normal property lookup.
398
0
  return this->GetProperty(prop);
399
0
}
400
401
cmValue cmSourceFile::GetProperty(std::string const& prop) const
402
0
{
403
  // Check for computed properties.
404
0
  if (prop == propLOCATION) {
405
0
    if (this->FullPath.empty()) {
406
0
      return nullptr;
407
0
    }
408
0
    return cmValue(this->FullPath);
409
0
  }
410
411
  // Check for the properties with backtraces.
412
0
  if (prop == propINCLUDE_DIRECTORIES) {
413
0
    if (this->IncludeDirectories.empty()) {
414
0
      return nullptr;
415
0
    }
416
417
0
    static std::string output;
418
0
    output = cmList::to_string(this->IncludeDirectories);
419
0
    return cmValue(output);
420
0
  }
421
422
0
  if (prop == propCOMPILE_OPTIONS) {
423
0
    if (this->CompileOptions.empty()) {
424
0
      return nullptr;
425
0
    }
426
427
0
    static std::string output;
428
0
    output = cmList::to_string(this->CompileOptions);
429
0
    return cmValue(output);
430
0
  }
431
432
0
  if (prop == propCOMPILE_DEFINITIONS) {
433
0
    if (this->CompileDefinitions.empty()) {
434
0
      return nullptr;
435
0
    }
436
437
0
    static std::string output;
438
0
    output = cmList::to_string(this->CompileDefinitions);
439
0
    return cmValue(output);
440
0
  }
441
442
0
  cmValue retVal = this->Properties.GetPropertyValue(prop);
443
0
  if (!retVal) {
444
0
    cmMakefile const* mf = this->Location.GetMakefile();
445
0
    bool const chain =
446
0
      mf->GetState()->IsPropertyChained(prop, cmProperty::SOURCE_FILE);
447
0
    if (chain) {
448
0
      return mf->GetProperty(prop, chain);
449
0
    }
450
0
    return nullptr;
451
0
  }
452
453
0
  return retVal;
454
0
}
455
456
std::string const& cmSourceFile::GetSafeProperty(std::string const& prop) const
457
0
{
458
0
  cmValue ret = this->GetProperty(prop);
459
0
  if (ret) {
460
0
    return *ret;
461
0
  }
462
463
0
  static std::string const s_empty;
464
0
  return s_empty;
465
0
}
466
467
bool cmSourceFile::GetPropertyAsBool(std::string const& prop) const
468
0
{
469
0
  return this->GetProperty(prop).IsOn();
470
0
}
471
472
void cmSourceFile::MarkAsGenerated()
473
0
{
474
0
  this->IsGenerated = true;
475
0
  auto const& mf = *this->Location.GetMakefile();
476
0
  mf.GetGlobalGenerator()->MarkAsGeneratedFile(this->ResolveFullPath());
477
0
}
478
479
bool cmSourceFile::GetIsGenerated(CheckScope checkScope) const
480
0
{
481
0
  if (this->IsGenerated) {
482
    // Globally marked as generated!
483
0
    return true;
484
0
  }
485
0
  if (checkScope == CheckScope::GlobalAndLocal) {
486
    // Check locally stored properties.
487
0
    return this->GetPropertyAsBool(propGENERATED);
488
0
  }
489
0
  return false;
490
0
}
491
492
void cmSourceFile::SetProperties(cmPropertyMap properties)
493
0
{
494
0
  this->Properties = std::move(properties);
495
0
}
496
497
cmCustomCommand* cmSourceFile::GetCustomCommand() const
498
0
{
499
0
  return this->CustomCommand.get();
500
0
}
501
502
void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
503
0
{
504
0
  this->CustomCommand = std::move(cc);
505
0
}