Coverage Report

Created: 2026-03-12 06:35

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