/src/CMake/Source/cmGeneratorFileSets.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 "cmGeneratorFileSets.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <iterator> |
7 | | #include <map> |
8 | | #include <unordered_set> |
9 | | #include <utility> |
10 | | #include <vector> |
11 | | |
12 | | #include <cm/memory> |
13 | | #include <cm/optional> |
14 | | #include <cmext/algorithm> |
15 | | |
16 | | #include "cmFileSet.h" |
17 | | #include "cmFileSetMetadata.h" |
18 | | #include "cmGenExContext.h" |
19 | | #include "cmGenExEvaluation.h" |
20 | | #include "cmGeneratorExpression.h" |
21 | | #include "cmGeneratorExpressionDAGChecker.h" |
22 | | #include "cmGeneratorExpressionNode.h" |
23 | | #include "cmGeneratorFileSet.h" |
24 | | #include "cmGeneratorTarget.h" |
25 | | #include "cmLinkItem.h" |
26 | | #include "cmList.h" |
27 | | #include "cmListFileCache.h" |
28 | | #include "cmMakefile.h" |
29 | | #include "cmMessageType.h" |
30 | | #include "cmSourceFile.h" |
31 | | #include "cmStringAlgorithms.h" |
32 | | #include "cmSystemTools.h" |
33 | | #include "cmTarget.h" |
34 | | #include "cmValue.h" |
35 | | |
36 | | cmGeneratorFileSets::cmGeneratorFileSets(cmGeneratorTarget* target, |
37 | | cmLocalGenerator* lg) |
38 | 0 | : Target(target) |
39 | 0 | , LocalGenerator(lg) |
40 | 0 | { |
41 | 0 | bool isFramework = target->IsFrameworkOnApple(); |
42 | 0 | auto issueMessage = [&target](cmFileSet const* fileSet) { |
43 | 0 | target->Makefile->IssueMessage( |
44 | 0 | MessageType::FATAL_ERROR, |
45 | 0 | cmStrCat(R"(The file set ")", fileSet->GetName(), R"(", of type ")", |
46 | 0 | fileSet->GetType(), |
47 | 0 | R"(", is incompatible with the "FRAMEWORK" target ")", |
48 | 0 | target->GetName(), R"(".)")); |
49 | 0 | }; |
50 | |
|
51 | 0 | for (auto const& name : target->Target->GetAllPrivateFileSets()) { |
52 | 0 | cmFileSet const* fileSet = target->Target->GetFileSet(name); |
53 | 0 | if (isFramework && |
54 | 0 | !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) { |
55 | 0 | issueMessage(fileSet); |
56 | 0 | continue; |
57 | 0 | } |
58 | 0 | auto entry = this->FileSets.emplace( |
59 | 0 | name, cm::make_unique<cmGeneratorFileSet>(target, fileSet)); |
60 | 0 | auto const* genFileSet = entry.first->second.get(); |
61 | 0 | this->AllFileSets.push_back(genFileSet); |
62 | 0 | this->SelfFileSets[genFileSet->GetType()].push_back(genFileSet); |
63 | 0 | } |
64 | 0 | for (auto const& name : target->Target->GetAllInterfaceFileSets()) { |
65 | 0 | auto it = this->FileSets.find(name); |
66 | 0 | cmGeneratorFileSet const* genFileSet = nullptr; |
67 | 0 | if (it == this->FileSets.end()) { |
68 | 0 | cmFileSet const* fileSet = target->Target->GetFileSet(name); |
69 | 0 | if (isFramework && |
70 | 0 | !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) { |
71 | 0 | issueMessage(fileSet); |
72 | 0 | continue; |
73 | 0 | } |
74 | 0 | auto entry = this->FileSets.emplace( |
75 | 0 | name, cm::make_unique<cmGeneratorFileSet>(target, fileSet)); |
76 | 0 | genFileSet = entry.first->second.get(); |
77 | 0 | this->AllFileSets.push_back(genFileSet); |
78 | 0 | } else { |
79 | 0 | genFileSet = it->second.get(); |
80 | 0 | } |
81 | 0 | this->InterfaceFileSets[genFileSet->GetType()].push_back(genFileSet); |
82 | 0 | } |
83 | 0 | } |
84 | 0 | cmGeneratorFileSets::~cmGeneratorFileSets() = default; |
85 | | |
86 | | std::vector<cm::string_view> cmGeneratorFileSets::GetFileSetTypes() const |
87 | 0 | { |
88 | 0 | return cm::keys(this->SelfFileSets); |
89 | 0 | } |
90 | | |
91 | | std::vector<cm::string_view> cmGeneratorFileSets::GetInterfaceFileSetTypes() |
92 | | const |
93 | 0 | { |
94 | 0 | return cm::keys(this->InterfaceFileSets); |
95 | 0 | } |
96 | | |
97 | | std::vector<cmGeneratorFileSet const*> const& |
98 | | cmGeneratorFileSets::GetAllFileSets() const |
99 | 0 | { |
100 | 0 | return this->AllFileSets; |
101 | 0 | } |
102 | | |
103 | | namespace { |
104 | | std::vector<cmGeneratorFileSet const*> NoFileSets; |
105 | | } |
106 | | |
107 | | std::vector<cmGeneratorFileSet const*> const& cmGeneratorFileSets::GetFileSets( |
108 | | cm::string_view type) const |
109 | 0 | { |
110 | 0 | auto it = this->SelfFileSets.find(type); |
111 | 0 | if (it != this->SelfFileSets.end()) { |
112 | 0 | return it->second; |
113 | 0 | } |
114 | 0 | return NoFileSets; |
115 | 0 | } |
116 | | std::vector<cmGeneratorFileSet const*> const& |
117 | | cmGeneratorFileSets::GetInterfaceFileSets(cm::string_view type) const |
118 | 0 | { |
119 | 0 | auto it = this->InterfaceFileSets.find(type); |
120 | 0 | if (it != this->InterfaceFileSets.end()) { |
121 | 0 | return it->second; |
122 | 0 | } |
123 | 0 | return NoFileSets; |
124 | 0 | } |
125 | | |
126 | | cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSet( |
127 | | std::string const& name) const |
128 | 0 | { |
129 | 0 | auto const it = this->FileSets.find(name); |
130 | 0 | if (it != this->FileSets.end()) { |
131 | 0 | return it->second.get(); |
132 | 0 | } |
133 | 0 | return nullptr; |
134 | 0 | } |
135 | | |
136 | | cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource( |
137 | | std::string const& config, std::string const& path) const |
138 | 0 | { |
139 | 0 | using Lookup = cm::FileSetMetadata::FileSetLookup; |
140 | |
|
141 | 0 | this->BuildInfoCache(config); |
142 | |
|
143 | 0 | auto const& info = this->Configs[config]; |
144 | |
|
145 | 0 | auto const it = info.FileSetCache.find(path); |
146 | 0 | if (it != info.FileSetCache.end()) { |
147 | 0 | return it->second; |
148 | 0 | } |
149 | | |
150 | | // search in all the dependents |
151 | 0 | auto const it2 = info.InterfaceFileSetCache.find(path); |
152 | 0 | if (it2 != info.InterfaceFileSetCache.end() && |
153 | 0 | cm::FileSetMetadata::GetFileSetDescriptor(it2->second->GetType()) |
154 | 0 | .value_or(cm::FileSetMetadata::FileSetDescriptor{ Lookup::Target }) |
155 | 0 | .Lookup == Lookup::Dependencies) { |
156 | 0 | return it2->second; |
157 | 0 | } |
158 | | |
159 | 0 | return nullptr; |
160 | 0 | } |
161 | | cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource( |
162 | | std::string const& config, cmSourceFile const* sf) const |
163 | 0 | { |
164 | 0 | return this->GetFileSetForSource(config, sf->GetFullPath()); |
165 | 0 | } |
166 | | |
167 | | std::vector<std::unique_ptr<cm::TargetPropertyEntry>> |
168 | | cmGeneratorFileSets::GetSources( |
169 | | std::function<bool(cmGeneratorFileSet const*)> include, |
170 | | cm::GenEx::Context const& context, cmGeneratorTarget const* target, |
171 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
172 | 0 | { |
173 | 0 | std::vector<std::unique_ptr<TargetPropertyEntry>> entries; |
174 | |
|
175 | 0 | for (auto const& entry : this->FileSets) { |
176 | 0 | auto const* fileSet = entry.second.get(); |
177 | 0 | if (include(fileSet)) { |
178 | 0 | auto sources = fileSet->GetSources(context, target, dagChecker); |
179 | 0 | std::move(sources.begin(), sources.end(), std::back_inserter(entries)); |
180 | 0 | } |
181 | 0 | } |
182 | |
|
183 | 0 | return entries; |
184 | 0 | } |
185 | | |
186 | | std::vector<std::unique_ptr<cm::TargetPropertyEntry>> |
187 | | cmGeneratorFileSets::GetSources( |
188 | | cm::GenEx::Context const& context, cmGeneratorTarget const* target, |
189 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
190 | 0 | { |
191 | 0 | return this->GetSources( |
192 | 0 | [](cmGeneratorFileSet const* fileSet) -> bool { |
193 | 0 | return fileSet->IsForSelf(); |
194 | 0 | }, |
195 | 0 | context, target, dagChecker); |
196 | 0 | } |
197 | | std::vector<std::unique_ptr<cm::TargetPropertyEntry>> |
198 | | cmGeneratorFileSets::GetSources( |
199 | | std::string type, cm::GenEx::Context const& context, |
200 | | cmGeneratorTarget const* target, |
201 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
202 | 0 | { |
203 | 0 | return this->GetSources( |
204 | 0 | [&type](cmGeneratorFileSet const* fileSet) -> bool { |
205 | 0 | return fileSet->IsForSelf() && fileSet->GetType() == type; |
206 | 0 | }, |
207 | 0 | context, target, dagChecker); |
208 | 0 | } |
209 | | |
210 | | std::vector<std::unique_ptr<cm::TargetPropertyEntry>> |
211 | | cmGeneratorFileSets::GetInterfaceSources( |
212 | | cm::GenEx::Context const& context, cmGeneratorTarget const* target, |
213 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
214 | 0 | { |
215 | 0 | return this->GetSources( |
216 | 0 | [](cmGeneratorFileSet const* fileSet) -> bool { |
217 | 0 | return fileSet->IsForInterface(); |
218 | 0 | }, |
219 | 0 | context, target, dagChecker); |
220 | 0 | } |
221 | | std::vector<std::unique_ptr<cm::TargetPropertyEntry>> |
222 | | cmGeneratorFileSets::GetInterfaceSources( |
223 | | std::string type, cm::GenEx::Context const& context, |
224 | | cmGeneratorTarget const* target, |
225 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
226 | 0 | { |
227 | 0 | return this->GetSources( |
228 | 0 | [&type](cmGeneratorFileSet const* fileSet) -> bool { |
229 | 0 | return fileSet->IsForInterface() && fileSet->GetType() == type; |
230 | 0 | }, |
231 | 0 | context, target, dagChecker); |
232 | 0 | } |
233 | | |
234 | | bool cmGeneratorFileSets::MaybeHaveInterfaceProperty( |
235 | | cm::string_view type, std::string const& prop, |
236 | | cm::GenEx::Evaluation* eval) const |
237 | 0 | { |
238 | 0 | std::string const key = |
239 | 0 | cmStrCat(type, "::", prop, '@', eval->Context.Config); |
240 | 0 | auto i = this->MaybeInterfacePropertyExists.find(key); |
241 | 0 | if (i == this->MaybeInterfacePropertyExists.end()) { |
242 | | // Insert an entry now in case there is a cycle. |
243 | 0 | i = this->MaybeInterfacePropertyExists.emplace(key, false).first; |
244 | 0 | bool& maybeInterfaceProp = i->second; |
245 | |
|
246 | 0 | for (auto const* fileSet : this->GetInterfaceFileSets(type)) { |
247 | | // If this file set itself has a non-empty property value, we are done. |
248 | 0 | maybeInterfaceProp = !fileSet->GetProperty(prop).IsEmpty(); |
249 | 0 | if (maybeInterfaceProp) { |
250 | 0 | break; |
251 | 0 | } |
252 | 0 | } |
253 | | |
254 | | // Otherwise, recurse to interface dependencies. |
255 | 0 | if (!maybeInterfaceProp) { |
256 | 0 | cmGeneratorTarget const* headTarget = |
257 | 0 | eval->HeadTarget ? eval->HeadTarget : this->Target; |
258 | 0 | if (cmLinkInterfaceLibraries const* iface = |
259 | 0 | this->Target->GetLinkInterfaceLibraries( |
260 | 0 | eval->Context.Config, headTarget, |
261 | 0 | cmGeneratorTarget::UseTo::Compile)) { |
262 | 0 | if (iface->HadHeadSensitiveCondition) { |
263 | | // With a different head target we may get to a library with |
264 | | // this interface property. |
265 | 0 | maybeInterfaceProp = true; |
266 | 0 | } else { |
267 | | // The transitive interface libraries do not depend on the |
268 | | // head target, so we can follow them. |
269 | 0 | for (cmLinkItem const& lib : iface->Libraries) { |
270 | 0 | if (lib.Target && |
271 | 0 | lib.Target->GetGeneratorFileSets()->MaybeHaveInterfaceProperty( |
272 | 0 | type, prop, eval)) { |
273 | 0 | maybeInterfaceProp = true; |
274 | 0 | break; |
275 | 0 | } |
276 | 0 | } |
277 | 0 | } |
278 | 0 | } |
279 | 0 | } |
280 | 0 | } |
281 | 0 | return i->second; |
282 | 0 | } |
283 | | |
284 | | std::string cmGeneratorFileSets::EvaluateInterfaceProperty( |
285 | | cm::string_view type, std::string const& prop, cm::GenEx::Evaluation* eval, |
286 | | cmGeneratorExpressionDAGChecker* dagCheckerParent) const |
287 | 0 | { |
288 | | // If the property does not appear transitively at all, we are done. |
289 | 0 | if (!this->MaybeHaveInterfaceProperty(type, prop, eval)) { |
290 | 0 | return std::string{}; |
291 | 0 | } |
292 | | |
293 | 0 | cmList result; |
294 | 0 | cmGeneratorExpressionDAGChecker dagChecker{ |
295 | 0 | this->Target, prop, nullptr, |
296 | 0 | dagCheckerParent, eval->Context, eval->Backtrace, |
297 | 0 | }; |
298 | 0 | switch (dagChecker.Check()) { |
299 | 0 | case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: |
300 | 0 | dagChecker.ReportError( |
301 | 0 | eval, |
302 | 0 | cmStrCat("$<FILE_SET_PROPERTY:*,TARGET:", this->Target->GetName(), ',', |
303 | 0 | prop, '>')); |
304 | 0 | return std::string{}; |
305 | 0 | case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: |
306 | | // No error. We just skip cyclic references. |
307 | 0 | case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: |
308 | | // No error. We have already seen this transitive property. |
309 | 0 | return std::string{}; |
310 | 0 | case cmGeneratorExpressionDAGChecker::DAG: |
311 | 0 | break; |
312 | 0 | } |
313 | | |
314 | 0 | cmGeneratorTarget const* headTarget = |
315 | 0 | eval->HeadTarget ? eval->HeadTarget : this->Target; |
316 | |
|
317 | 0 | for (auto const* fileSet : this->GetInterfaceFileSets(type)) { |
318 | 0 | if (cmValue p = fileSet->GetProperty(prop)) { |
319 | 0 | result.append(cmGeneratorExpressionNode::EvaluateDependentExpression( |
320 | 0 | *p, eval, headTarget, &dagChecker, this->Target)); |
321 | 0 | } |
322 | 0 | } |
323 | |
|
324 | 0 | if (cmLinkInterfaceLibraries const* iface = |
325 | 0 | this->Target->GetLinkInterfaceLibraries( |
326 | 0 | eval->Context.Config, headTarget, |
327 | 0 | cmGeneratorTarget::UseTo::Compile)) { |
328 | 0 | eval->HadContextSensitiveCondition = eval->HadContextSensitiveCondition || |
329 | 0 | iface->HadContextSensitiveCondition; |
330 | 0 | for (cmLinkItem const& lib : iface->Libraries) { |
331 | | // Broken code can have a target in its own link interface. |
332 | | // Don't follow such link interface entries so as not to create a |
333 | | // self-referencing loop. |
334 | 0 | if (lib.Target && lib.Target != this->Target) { |
335 | | // Pretend $<FILE_SET__PROPERTY:fileSet,TARGET:lib.Target,prop> |
336 | | // appeared in the above property and hand-evaluate it as if it were |
337 | | // compiled. |
338 | | // Create a context as cmCompiledGeneratorExpression::Evaluate does. |
339 | 0 | cm::GenEx::Evaluation libEval( |
340 | 0 | eval->Context, eval->Quiet, headTarget, this->Target, |
341 | 0 | eval->EvaluateForBuildsystem, eval->Backtrace); |
342 | 0 | std::string libResult = cmGeneratorExpression::StripEmptyListElements( |
343 | 0 | lib.Target->GetGeneratorFileSets()->EvaluateInterfaceProperty( |
344 | 0 | type, prop, &libEval, &dagChecker)); |
345 | 0 | if (!libResult.empty()) { |
346 | 0 | result.append(libResult); |
347 | 0 | } |
348 | 0 | eval->HadContextSensitiveCondition = |
349 | 0 | eval->HadContextSensitiveCondition || |
350 | 0 | libEval.HadContextSensitiveCondition; |
351 | 0 | eval->HadHeadSensitiveCondition = |
352 | 0 | eval->HadHeadSensitiveCondition || libEval.HadHeadSensitiveCondition; |
353 | 0 | } |
354 | 0 | } |
355 | 0 | } |
356 | |
|
357 | 0 | return result.to_string(); |
358 | 0 | } |
359 | | |
360 | | namespace { |
361 | | void GetInterfaceFiles(cmGeneratorTarget const* target, |
362 | | cm::GenEx::Context const& context, |
363 | | std::unordered_set<cmGeneratorTarget const*>& targets, |
364 | | std::map<std::string, cmGeneratorFileSet const*>& cache) |
365 | 0 | { |
366 | 0 | namespace Metadata = cm::FileSetMetadata; |
367 | |
|
368 | 0 | for (auto const& type : Metadata::GetKnownTypes()) { |
369 | 0 | auto fileSetDescriptor = Metadata::GetFileSetDescriptor(type); |
370 | 0 | if (fileSetDescriptor && |
371 | 0 | fileSetDescriptor->Lookup == Metadata::FileSetLookup::Dependencies) { |
372 | 0 | for (auto const* fileSet : target->GetInterfaceFileSets(type)) { |
373 | 0 | auto files = fileSet->GetFiles(context, target); |
374 | |
|
375 | 0 | for (auto const& it : files.first) { |
376 | 0 | for (auto const& filename : it.second) { |
377 | 0 | auto collapsedFile = cmSystemTools::CollapseFullPath(filename); |
378 | 0 | cache[collapsedFile] = fileSet; |
379 | 0 | } |
380 | 0 | } |
381 | 0 | } |
382 | 0 | } |
383 | 0 | } |
384 | |
|
385 | 0 | if (cmLinkInterfaceLibraries const* iface = |
386 | 0 | target->GetLinkInterfaceLibraries(context.Config, target, |
387 | 0 | cmGeneratorTarget::UseTo::Compile)) { |
388 | 0 | for (cmLinkItem const& lib : iface->Libraries) { |
389 | 0 | if (lib.Target && lib.Target != target && |
390 | 0 | targets.insert(lib.Target).second) { |
391 | 0 | GetInterfaceFiles(lib.Target, context, targets, cache); |
392 | 0 | } |
393 | 0 | } |
394 | 0 | } |
395 | 0 | } |
396 | | } |
397 | | |
398 | | void cmGeneratorFileSets::BuildInfoCache(std::string const& config) const |
399 | 0 | { |
400 | 0 | auto& info = this->Configs[config]; |
401 | |
|
402 | 0 | if (info.BuiltCache) { |
403 | 0 | return; |
404 | 0 | } |
405 | | |
406 | 0 | cm::GenEx::Context context(this->LocalGenerator, config); |
407 | |
|
408 | 0 | for (auto const& item : this->FileSets) { |
409 | 0 | auto const* fileSet = item.second.get(); |
410 | |
|
411 | 0 | auto files = fileSet->GetFiles(context, this->Target); |
412 | |
|
413 | 0 | for (auto const& it : files.first) { |
414 | 0 | for (auto const& filename : it.second) { |
415 | 0 | auto collapsedFile = cmSystemTools::CollapseFullPath(filename); |
416 | 0 | info.FileSetCache[collapsedFile] = fileSet; |
417 | 0 | } |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | | // retrieve all files inherited from dependent targets |
422 | 0 | std::unordered_set<cmGeneratorTarget const*> targets; |
423 | |
|
424 | 0 | if (cmLinkImplementationLibraries const* impl = |
425 | 0 | this->Target->GetLinkImplementationLibraries( |
426 | 0 | config, cmGeneratorTarget::UseTo::Compile)) { |
427 | 0 | for (cmLinkItem const& lib : impl->Libraries) { |
428 | 0 | if (lib.Target) { |
429 | 0 | GetInterfaceFiles(lib.Target, context, targets, |
430 | 0 | info.InterfaceFileSetCache); |
431 | 0 | } |
432 | 0 | } |
433 | 0 | } |
434 | |
|
435 | 0 | info.BuiltCache = true; |
436 | 0 | } |