/src/CMake/Source/cmGeneratorFileSet.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 "cmGeneratorFileSet.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <map> |
7 | | #include <sstream> |
8 | | #include <unordered_map> |
9 | | #include <utility> |
10 | | #include <vector> |
11 | | |
12 | | #include <cm/memory> |
13 | | #include <cm/optional> |
14 | | |
15 | | #include "cmFileSet.h" |
16 | | #include "cmGenExContext.h" |
17 | | #include "cmGeneratorExpression.h" |
18 | | #include "cmList.h" |
19 | | #include "cmListFileCache.h" |
20 | | #include "cmLocalGenerator.h" |
21 | | #include "cmMakefile.h" |
22 | | #include "cmMessageType.h" |
23 | | #include "cmStringAlgorithms.h" |
24 | | #include "cmSystemTools.h" |
25 | | #include "cmake.h" |
26 | | |
27 | | class cmLinkItem; |
28 | | |
29 | | class FileSetPropertyEntry : public cm::TargetPropertyEntry |
30 | | { |
31 | | public: |
32 | | FileSetPropertyEntry( |
33 | | std::vector<std::string> dirs, bool contextSensitiveDirs, |
34 | | std::unique_ptr<cmCompiledGeneratorExpression> const& cge, |
35 | | cmGeneratorFileSet const* fileSet, cmLinkItem const& item = NoLinkItem) |
36 | 0 | : cm::TargetPropertyEntry(item) |
37 | 0 | , BaseDirs(std::move(dirs)) |
38 | 0 | , ContextSensitiveDirs(contextSensitiveDirs) |
39 | 0 | , Cge(cge) |
40 | 0 | , FileSet(fileSet) |
41 | 0 | { |
42 | 0 | } |
43 | | |
44 | | static std::unique_ptr<cm::TargetPropertyEntry> CreateFileSetEntry( |
45 | | std::vector<std::string> dirs, bool contextSensitiveDirs, |
46 | | std::unique_ptr<cmCompiledGeneratorExpression> const& cge, |
47 | | cmGeneratorFileSet const* fileSet, cmLinkItem const& item = NoLinkItem) |
48 | 0 | { |
49 | 0 | return cm::make_unique<FileSetPropertyEntry>( |
50 | 0 | std::move(dirs), contextSensitiveDirs, cge, fileSet, item); |
51 | 0 | } |
52 | | |
53 | | std::string const& Evaluate( |
54 | | cm::GenEx::Context const& context, cmGeneratorTarget const* headTarget, |
55 | | cmGeneratorExpressionDAGChecker* dagChecker) const override |
56 | 0 | { |
57 | 0 | std::map<std::string, std::vector<std::string>> filesPerDir; |
58 | 0 | this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir, this->Cge, |
59 | 0 | context, headTarget, dagChecker); |
60 | |
|
61 | 0 | std::vector<std::string> files; |
62 | 0 | for (auto const& it : filesPerDir) { |
63 | 0 | files.insert(files.end(), it.second.begin(), it.second.end()); |
64 | 0 | } |
65 | |
|
66 | 0 | this->Value = cmList::to_string(files); |
67 | 0 | return Value; |
68 | 0 | } |
69 | | |
70 | | cmListFileBacktrace GetBacktrace() const override |
71 | 0 | { |
72 | 0 | return this->Cge->GetBacktrace(); |
73 | 0 | } |
74 | | |
75 | | std::string const& GetInput() const override |
76 | 0 | { |
77 | 0 | return this->Cge->GetInput(); |
78 | 0 | } |
79 | | |
80 | | bool GetHadContextSensitiveCondition() const override |
81 | 0 | { |
82 | 0 | return this->ContextSensitiveDirs || |
83 | 0 | this->Cge->GetHadContextSensitiveCondition(); |
84 | 0 | } |
85 | | |
86 | | private: |
87 | | std::vector<std::string> const BaseDirs; |
88 | | bool const ContextSensitiveDirs; |
89 | | std::unique_ptr<cmCompiledGeneratorExpression> const& Cge; |
90 | | cmGeneratorFileSet const* FileSet; |
91 | | mutable std::string Value; |
92 | | }; |
93 | | |
94 | | // |
95 | | // Class cmGeneratorFileSet |
96 | | // |
97 | | cmGeneratorFileSet::cmGeneratorFileSet(cmFileSet const* fileSet) |
98 | 0 | : FileSet(fileSet) |
99 | 0 | { |
100 | 0 | } |
101 | | |
102 | | std::string const& cmGeneratorFileSet::GetName() const |
103 | 0 | { |
104 | 0 | return this->FileSet->GetName(); |
105 | 0 | } |
106 | | std::string const& cmGeneratorFileSet::GetType() const |
107 | 0 | { |
108 | 0 | return this->FileSet->GetType(); |
109 | 0 | } |
110 | | cm::FileSetMetadata::Visibility cmGeneratorFileSet::GetVisibility() const |
111 | 0 | { |
112 | 0 | return this->FileSet->GetVisibility(); |
113 | 0 | } |
114 | | |
115 | | bool cmGeneratorFileSet::IsForSelf() const |
116 | 0 | { |
117 | 0 | return this->FileSet->IsForSelf(); |
118 | 0 | } |
119 | | bool cmGeneratorFileSet::IsForInterface() const |
120 | 0 | { |
121 | 0 | return this->FileSet->IsForInterface(); |
122 | 0 | } |
123 | | bool cmGeneratorFileSet::CanBeIncluded() const |
124 | 0 | { |
125 | 0 | return this->FileSet->CanBeIncluded(); |
126 | 0 | } |
127 | | |
128 | | cmValue cmGeneratorFileSet::GetProperty(std::string const& prop) const |
129 | 0 | { |
130 | 0 | return this->FileSet->GetProperty(prop); |
131 | 0 | } |
132 | | |
133 | | std::vector<BT<std::string>> const& cmGeneratorFileSet::GetDirectoryEntries() |
134 | | const |
135 | 0 | { |
136 | 0 | return this->FileSet->GetDirectoryEntries(); |
137 | 0 | } |
138 | | |
139 | | std::vector<BT<std::string>> const& cmGeneratorFileSet::GetFileEntries() const |
140 | 0 | { |
141 | 0 | return this->FileSet->GetFileEntries(); |
142 | 0 | } |
143 | | |
144 | | std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& |
145 | | cmGeneratorFileSet::CompileFileEntries() const |
146 | 0 | { |
147 | 0 | if (this->CompiledFileEntries.empty() && |
148 | 0 | !this->FileSet->GetFileEntries().empty()) { |
149 | 0 | for (auto const& entry : this->FileSet->GetFileEntries()) { |
150 | 0 | for (auto const& ex : cmList{ entry.Value }) { |
151 | 0 | cmGeneratorExpression ge( |
152 | 0 | *this->FileSet->GetMakefile()->GetCMakeInstance(), entry.Backtrace); |
153 | 0 | auto cge = ge.Parse(ex); |
154 | 0 | this->CompiledFileEntries.push_back(std::move(cge)); |
155 | 0 | } |
156 | 0 | } |
157 | 0 | } |
158 | |
|
159 | 0 | return this->CompiledFileEntries; |
160 | 0 | } |
161 | | |
162 | | std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& |
163 | | cmGeneratorFileSet::CompileDirectoryEntries() const |
164 | 0 | { |
165 | 0 | if (this->CompiledDirectoryEntries.empty() && |
166 | 0 | !this->FileSet->GetDirectoryEntries().empty()) { |
167 | 0 | for (auto const& entry : this->FileSet->GetDirectoryEntries()) { |
168 | 0 | for (auto const& ex : cmList{ entry.Value }) { |
169 | 0 | cmGeneratorExpression ge( |
170 | 0 | *this->FileSet->GetMakefile()->GetCMakeInstance(), entry.Backtrace); |
171 | 0 | auto cge = ge.Parse(ex); |
172 | 0 | this->CompiledDirectoryEntries.push_back(std::move(cge)); |
173 | 0 | } |
174 | 0 | } |
175 | 0 | } |
176 | |
|
177 | 0 | return this->CompiledDirectoryEntries; |
178 | 0 | } |
179 | | |
180 | | std::vector<std::string> cmGeneratorFileSet::EvaluateDirectoryEntries( |
181 | | std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> const& cges, |
182 | | cm::GenEx::Context const& context, cmGeneratorTarget const* target, |
183 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
184 | 0 | { |
185 | 0 | struct DirCacheEntry |
186 | 0 | { |
187 | 0 | std::string collapsedDir; |
188 | 0 | cm::optional<cmSystemTools::FileId> fileId; |
189 | 0 | }; |
190 | |
|
191 | 0 | std::unordered_map<std::string, DirCacheEntry> dirCache; |
192 | 0 | std::vector<std::string> result; |
193 | 0 | for (auto const& cge : cges) { |
194 | 0 | auto entry = cge->Evaluate(context, dagChecker, target); |
195 | 0 | cmList dirs{ entry }; |
196 | 0 | for (std::string dir : dirs) { |
197 | 0 | if (!cmSystemTools::FileIsFullPath(dir)) { |
198 | 0 | dir = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', dir); |
199 | 0 | } |
200 | |
|
201 | 0 | auto dirCacheResult = dirCache.emplace(dir, DirCacheEntry()); |
202 | 0 | auto& dirCacheEntry = dirCacheResult.first->second; |
203 | 0 | auto const isNewCacheEntry = dirCacheResult.second; |
204 | |
|
205 | 0 | if (isNewCacheEntry) { |
206 | 0 | cmSystemTools::FileId fileId; |
207 | 0 | auto isFileIdValid = cmSystemTools::GetFileId(dir, fileId); |
208 | 0 | dirCacheEntry.collapsedDir = cmSystemTools::CollapseFullPath(dir); |
209 | 0 | dirCacheEntry.fileId = |
210 | 0 | isFileIdValid ? cm::optional<decltype(fileId)>(fileId) : cm::nullopt; |
211 | 0 | } |
212 | |
|
213 | 0 | for (auto const& priorDir : result) { |
214 | 0 | auto priorDirCacheEntry = dirCache.at(priorDir); |
215 | 0 | bool sameFile = dirCacheEntry.fileId.has_value() && |
216 | 0 | priorDirCacheEntry.fileId.has_value() && |
217 | 0 | (*dirCacheEntry.fileId == *priorDirCacheEntry.fileId); |
218 | 0 | if (!sameFile && |
219 | 0 | (cmSystemTools::IsSubDirectory(dirCacheEntry.collapsedDir, |
220 | 0 | priorDirCacheEntry.collapsedDir) || |
221 | 0 | cmSystemTools::IsSubDirectory(priorDirCacheEntry.collapsedDir, |
222 | 0 | dirCacheEntry.collapsedDir))) { |
223 | 0 | context.LG->GetCMakeInstance()->IssueMessage( |
224 | 0 | MessageType::FATAL_ERROR, |
225 | 0 | cmStrCat( |
226 | 0 | "Base directories in file set cannot be subdirectories of each " |
227 | 0 | "other:\n ", |
228 | 0 | priorDir, "\n ", dir), |
229 | 0 | cge->GetBacktrace()); |
230 | 0 | return {}; |
231 | 0 | } |
232 | 0 | } |
233 | 0 | result.push_back(dir); |
234 | 0 | } |
235 | 0 | } |
236 | 0 | return result; |
237 | 0 | } |
238 | | |
239 | | void cmGeneratorFileSet::EvaluateFileEntry( |
240 | | std::vector<std::string> const& dirs, |
241 | | std::map<std::string, std::vector<std::string>>& filesPerDir, |
242 | | std::unique_ptr<cmCompiledGeneratorExpression> const& cge, |
243 | | cm::GenEx::Context const& context, cmGeneratorTarget const* target, |
244 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
245 | 0 | { |
246 | 0 | auto files = cge->Evaluate(context, dagChecker, target); |
247 | 0 | for (std::string file : cmList{ files }) { |
248 | 0 | if (!cmSystemTools::FileIsFullPath(file)) { |
249 | 0 | file = cmStrCat(context.LG->GetCurrentSourceDirectory(), '/', file); |
250 | 0 | } |
251 | 0 | auto collapsedFile = cmSystemTools::CollapseFullPath(file); |
252 | 0 | bool found = false; |
253 | 0 | std::string relDir; |
254 | 0 | for (auto const& dir : dirs) { |
255 | 0 | auto collapsedDir = cmSystemTools::CollapseFullPath(dir); |
256 | 0 | if (cmSystemTools::IsSubDirectory(collapsedFile, collapsedDir)) { |
257 | 0 | found = true; |
258 | 0 | relDir = cmSystemTools::GetParentDirectory( |
259 | 0 | cmSystemTools::RelativePath(collapsedDir, collapsedFile)); |
260 | 0 | break; |
261 | 0 | } |
262 | 0 | } |
263 | 0 | if (!found) { |
264 | 0 | std::ostringstream e; |
265 | 0 | e << "File:\n " << file |
266 | 0 | << "\nmust be in one of the file set's base directories:"; |
267 | 0 | for (auto const& dir : dirs) { |
268 | 0 | e << "\n " << dir; |
269 | 0 | } |
270 | 0 | context.LG->GetCMakeInstance()->IssueMessage( |
271 | 0 | MessageType::FATAL_ERROR, e.str(), cge->GetBacktrace()); |
272 | 0 | return; |
273 | 0 | } |
274 | | |
275 | 0 | filesPerDir[relDir].push_back(file); |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | namespace { |
280 | | bool EntryIsContextSensitive( |
281 | | std::unique_ptr<cmCompiledGeneratorExpression> const& cge) |
282 | 0 | { |
283 | 0 | return cge->GetHadContextSensitiveCondition(); |
284 | 0 | } |
285 | | } |
286 | | |
287 | | std::vector<std::unique_ptr<cm::TargetPropertyEntry>> |
288 | | cmGeneratorFileSet::GetSources( |
289 | | cm::GenEx::Context const& context, cmGeneratorTarget const* target, |
290 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
291 | 0 | { |
292 | 0 | std::vector<std::unique_ptr<TargetPropertyEntry>> entries; |
293 | |
|
294 | 0 | auto directories = this->GetDirectories(context, target, dagChecker); |
295 | 0 | bool contextSensitive = directories.second; |
296 | |
|
297 | 0 | for (auto const& entry : this->CompileFileEntries()) { |
298 | 0 | auto propEntry = FileSetPropertyEntry::CreateFileSetEntry( |
299 | 0 | directories.first, contextSensitive, entry, this); |
300 | 0 | entries.push_back(std::move(propEntry)); |
301 | 0 | } |
302 | |
|
303 | 0 | return entries; |
304 | 0 | } |
305 | | |
306 | | std::pair<std::vector<std::string>, bool> cmGeneratorFileSet::GetDirectories( |
307 | | cm::GenEx::Context const& context, cmGeneratorTarget const* target, |
308 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
309 | 0 | { |
310 | 0 | auto const& directoryEntries = this->CompileDirectoryEntries(); |
311 | 0 | auto directories = this->EvaluateDirectoryEntries(directoryEntries, context, |
312 | 0 | target, dagChecker); |
313 | 0 | bool contextSensitive = std::any_of( |
314 | 0 | directoryEntries.begin(), directoryEntries.end(), EntryIsContextSensitive); |
315 | |
|
316 | 0 | return std::make_pair(std::move(directories), contextSensitive); |
317 | 0 | } |
318 | | |
319 | | std::pair<std::map<std::string, std::vector<std::string>>, bool> |
320 | | cmGeneratorFileSet::GetFiles(cm::GenEx::Context const& context, |
321 | | cmGeneratorTarget const* target, |
322 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
323 | 0 | { |
324 | 0 | auto directories = this->GetDirectories(context, target, dagChecker); |
325 | |
|
326 | 0 | auto const& fileEntries = this->CompileFileEntries(); |
327 | 0 | std::map<std::string, std::vector<std::string>> files; |
328 | 0 | for (auto const& entry : fileEntries) { |
329 | 0 | this->EvaluateFileEntry(directories.first, files, entry, context, target, |
330 | 0 | dagChecker); |
331 | 0 | } |
332 | 0 | bool contextSensitive = directories.second || |
333 | 0 | std::any_of(fileEntries.begin(), fileEntries.end(), |
334 | 0 | EntryIsContextSensitive); |
335 | |
|
336 | 0 | return std::make_pair(std::move(files), contextSensitive); |
337 | 0 | } |