Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmSourceFileLocation.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 "cmSourceFileLocation.h"
4
5
#include <cassert>
6
7
#include <cm/string_view>
8
9
#include "cmGlobalGenerator.h"
10
#include "cmMakefile.h"
11
#include "cmMessageType.h"
12
#include "cmPolicies.h"
13
#include "cmStringAlgorithms.h"
14
#include "cmSystemTools.h"
15
#include "cmake.h"
16
17
// if CMP0187 and CMP0115 are NEW, then we assume that source files that do not
18
// include a file extension are not ambiguous but intentionally do not have an
19
// extension.
20
bool NoAmbiguousExtensions(cmMakefile const& makefile)
21
130k
{
22
130k
  return makefile.GetPolicyStatus(cmPolicies::CMP0115) == cmPolicies::NEW &&
23
130k
    makefile.GetPolicyStatus(cmPolicies::CMP0187) == cmPolicies::NEW;
24
130k
}
25
26
cmSourceFileLocation::cmSourceFileLocation(cmSourceFileLocation const& loc)
27
0
  : Makefile(loc.Makefile)
28
0
{
29
0
  this->AmbiguousDirectory = loc.AmbiguousDirectory;
30
0
  this->AmbiguousExtension = loc.AmbiguousExtension;
31
0
  this->Directory = loc.Directory;
32
0
  this->Name = loc.Name;
33
0
}
34
35
cmSourceFileLocation::cmSourceFileLocation(cmMakefile const* mf,
36
                                           std::string const& name,
37
                                           cmSourceFileLocationKind kind)
38
130k
  : Makefile(mf)
39
130k
{
40
130k
  this->AmbiguousDirectory = !cmSystemTools::FileIsFullPath(name);
41
  // If ambiguous extensions are allowed then the extension is assumed to be
42
  // ambiguous unless the name has an extension, in which case
43
  // `UpdateExtension` will update this. If ambiguous extensions are not
44
  // allowed, then set this to false as the file extension must be provided or
45
  // the file doesn't have an extension.
46
130k
  this->AmbiguousExtension = !NoAmbiguousExtensions(*mf);
47
130k
  this->Directory = cmSystemTools::GetFilenamePath(name);
48
130k
  if (cmSystemTools::FileIsFullPath(this->Directory)) {
49
84.0k
    this->Directory = cmSystemTools::CollapseFullPath(this->Directory);
50
84.0k
  }
51
130k
  this->Name = cmSystemTools::GetFilenameName(name);
52
130k
  if (kind == cmSourceFileLocationKind::Known) {
53
84.5k
    this->DirectoryUseSource();
54
84.5k
    this->AmbiguousExtension = false;
55
84.5k
  } else {
56
46.4k
    this->UpdateExtension(name);
57
46.4k
  }
58
130k
}
59
60
std::string cmSourceFileLocation::GetFullPath() const
61
12.9k
{
62
12.9k
  std::string path = this->GetDirectory();
63
12.9k
  if (!path.empty()) {
64
12.2k
    path += '/';
65
12.2k
  }
66
12.9k
  path += this->GetName();
67
12.9k
  return path;
68
12.9k
}
69
70
void cmSourceFileLocation::Update(cmSourceFileLocation const& loc)
71
20.6k
{
72
20.6k
  if (this->AmbiguousDirectory && !loc.AmbiguousDirectory) {
73
725
    this->Directory = loc.Directory;
74
725
    this->AmbiguousDirectory = false;
75
725
  }
76
20.6k
  if (this->AmbiguousExtension && !loc.AmbiguousExtension) {
77
0
    this->Name = loc.Name;
78
0
    this->AmbiguousExtension = false;
79
0
  }
80
20.6k
}
81
82
void cmSourceFileLocation::DirectoryUseSource()
83
84.5k
{
84
84.5k
  assert(this->Makefile);
85
84.5k
  if (this->AmbiguousDirectory) {
86
696
    this->Directory = cmSystemTools::CollapseFullPath(
87
696
      this->Directory, this->Makefile->GetCurrentSourceDirectory());
88
696
    this->AmbiguousDirectory = false;
89
696
  }
90
84.5k
}
91
92
void cmSourceFileLocation::DirectoryUseBinary()
93
12.2k
{
94
12.2k
  assert(this->Makefile);
95
12.2k
  if (this->AmbiguousDirectory) {
96
0
    this->Directory = cmSystemTools::CollapseFullPath(
97
0
      this->Directory, this->Makefile->GetCurrentBinaryDirectory());
98
0
    this->AmbiguousDirectory = false;
99
0
  }
100
12.2k
}
101
102
void cmSourceFileLocation::UpdateExtension(std::string const& name)
103
46.4k
{
104
46.4k
  assert(this->Makefile);
105
  // Check the extension.
106
46.4k
  cm::string_view ext = cmSystemTools::GetFilenameLastExtensionView(name);
107
46.4k
  if (!ext.empty()) {
108
44.3k
    ext = ext.substr(1);
109
44.3k
  }
110
111
  // The global generator checks extensions of enabled languages.
112
46.4k
  cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
113
46.4k
  cmMakefile const* mf = this->Makefile;
114
46.4k
  auto* cm = mf->GetCMakeInstance();
115
46.4k
  if (!gg->GetLanguageFromExtension(ext).empty() ||
116
44.3k
      cm->IsAKnownExtension(ext)) {
117
    // This is a known extension.  Use the given filename with extension.
118
44.3k
    this->Name = cmSystemTools::GetFilenameName(name);
119
44.3k
    this->AmbiguousExtension = false;
120
44.3k
  } else if (this->AmbiguousExtension || this->AmbiguousDirectory) {
121
    // This is not a known extension.  See if the file exists on disk as
122
    // named.  This is not needed if neither the extension nor the
123
    // directory is ambiguous, because finding the file would not change
124
    // the location.
125
2.12k
    std::string tryPath;
126
2.12k
    if (this->AmbiguousDirectory) {
127
      // Check the source tree only because a file in the build tree should
128
      // be specified by full path at least once.  We do not want this
129
      // detection to depend on whether the project has already been built.
130
2.12k
      tryPath = cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/');
131
2.12k
    }
132
2.12k
    if (!this->Directory.empty()) {
133
635
      tryPath += this->Directory;
134
635
      tryPath += "/";
135
635
    }
136
2.12k
    tryPath += this->Name;
137
2.12k
    if (gg->SourceFileExists(tryPath)) {
138
      // We found a source file named by the user on disk.  Trust it's
139
      // extension.
140
0
      this->Name = cmSystemTools::GetFilenameName(name);
141
0
      this->AmbiguousExtension = false;
142
143
      // If the directory was ambiguous, it isn't anymore.
144
0
      if (this->AmbiguousDirectory) {
145
0
        this->DirectoryUseSource();
146
0
      }
147
0
    }
148
2.12k
  }
149
46.4k
}
150
151
bool cmSourceFileLocation::MatchesAmbiguousExtension(
152
  cmSourceFileLocation const& loc) const
153
2
{
154
2
  assert(this->Makefile);
155
  // This location's extension is not ambiguous but loc's extension
156
  // is.  See if the names match as-is.
157
2
  if (this->Name == loc.Name) {
158
0
    return true;
159
0
  }
160
161
  // Check if loc's name could possibly be extended to our name by
162
  // adding an extension.
163
2
  if (!(this->Name.size() > loc.Name.size() &&
164
2
        this->Name[loc.Name.size()] == '.' &&
165
2
        cmHasPrefix(this->Name, loc.Name))) {
166
0
    return false;
167
0
  }
168
169
  // Only a fixed set of extensions will be tried to match a file on
170
  // disk.  One of these must match if loc refers to this source file.
171
2
  auto ext = cm::string_view(this->Name).substr(loc.Name.size() + 1);
172
2
  cmMakefile const* mf = this->Makefile;
173
2
  auto* cm = mf->GetCMakeInstance();
174
2
  return cm->IsAKnownExtension(ext);
175
2
}
176
177
bool cmSourceFileLocation::Matches(cmSourceFileLocation const& loc)
178
34.0k
{
179
34.0k
  assert(this->Makefile);
180
34.0k
  if (this->AmbiguousExtension == loc.AmbiguousExtension) {
181
    // Both extensions are similarly ambiguous.  Since only the old fixed set
182
    // of extensions will be tried, the names must match at this point to be
183
    // the same file.
184
34.0k
    if (this->Name.size() != loc.Name.size() ||
185
21.2k
        !cmSystemTools::ComparePath(this->Name, loc.Name)) {
186
12.8k
      return false;
187
12.8k
    }
188
34.0k
  } else {
189
2
    cmSourceFileLocation const* loc1;
190
2
    cmSourceFileLocation const* loc2;
191
2
    if (this->AmbiguousExtension) {
192
      // Only "this" extension is ambiguous.
193
0
      loc1 = &loc;
194
0
      loc2 = this;
195
2
    } else {
196
      // Only "loc" extension is ambiguous.
197
2
      loc1 = this;
198
2
      loc2 = &loc;
199
2
    }
200
2
    if (!loc1->MatchesAmbiguousExtension(*loc2)) {
201
0
      return false;
202
0
    }
203
2
  }
204
205
21.2k
  if (!this->AmbiguousDirectory && !loc.AmbiguousDirectory) {
206
    // Both sides have absolute directories.
207
1.19k
    if (this->Directory != loc.Directory) {
208
603
      return false;
209
603
    }
210
20.0k
  } else if (this->AmbiguousDirectory && loc.AmbiguousDirectory) {
211
19.0k
    if (this->Makefile == loc.Makefile) {
212
      // Both sides have directories relative to the same location.
213
19.0k
      if (this->Directory != loc.Directory) {
214
0
        return false;
215
0
      }
216
19.0k
    } else {
217
      // Each side has a directory relative to a different location.
218
      // This can occur when referencing a source file from a different
219
      // directory.  This is not yet allowed.
220
0
      this->Makefile->IssueMessage(
221
0
        MessageType::INTERNAL_ERROR,
222
0
        "Matches error: Each side has a directory relative to a different "
223
0
        "location. This can occur when referencing a source file from a "
224
0
        "different directory.  This is not yet allowed.");
225
0
      return false;
226
0
    }
227
19.0k
  } else if (this->AmbiguousDirectory) {
228
    // Compare possible directory combinations.
229
757
    std::string const srcDir = cmSystemTools::CollapseFullPath(
230
757
      this->Directory, this->Makefile->GetCurrentSourceDirectory());
231
757
    std::string const binDir = cmSystemTools::CollapseFullPath(
232
757
      this->Directory, this->Makefile->GetCurrentBinaryDirectory());
233
757
    if (srcDir != loc.Directory && binDir != loc.Directory) {
234
32
      return false;
235
32
    }
236
757
  } else if (loc.AmbiguousDirectory) {
237
    // Compare possible directory combinations.
238
244
    std::string const srcDir = cmSystemTools::CollapseFullPath(
239
244
      loc.Directory, loc.Makefile->GetCurrentSourceDirectory());
240
244
    std::string const binDir = cmSystemTools::CollapseFullPath(
241
244
      loc.Directory, loc.Makefile->GetCurrentBinaryDirectory());
242
244
    if (srcDir != this->Directory && binDir != this->Directory) {
243
0
      return false;
244
0
    }
245
244
  }
246
247
  // File locations match.
248
20.6k
  this->Update(loc);
249
20.6k
  return true;
250
21.2k
}