/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 | } |