/src/CMake/Source/cmGeneratorTarget_TransitiveProperty.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 | | /* clang-format off */ |
4 | | #include "cmGeneratorTarget.h" |
5 | | /* clang-format on */ |
6 | | |
7 | | #include <map> |
8 | | #include <string> |
9 | | #include <unordered_map> |
10 | | #include <utility> |
11 | | #include <vector> |
12 | | |
13 | | #include <cm/memory> |
14 | | #include <cm/optional> |
15 | | #include <cm/string_view> |
16 | | #include <cmext/string_view> |
17 | | |
18 | | #include "cmGenExContext.h" |
19 | | #include "cmGenExEvaluation.h" |
20 | | #include "cmGeneratorExpression.h" |
21 | | #include "cmGeneratorExpressionDAGChecker.h" |
22 | | #include "cmGeneratorExpressionNode.h" |
23 | | #include "cmLinkItem.h" |
24 | | #include "cmList.h" |
25 | | #include "cmListFileCache.h" |
26 | | #include "cmLocalGenerator.h" |
27 | | #include "cmPolicies.h" |
28 | | #include "cmStringAlgorithms.h" |
29 | | #include "cmValue.h" |
30 | | |
31 | | namespace { |
32 | | using UseTo = cmGeneratorTarget::UseTo; |
33 | | using TransitiveProperty = cmGeneratorTarget::TransitiveProperty; |
34 | | |
35 | | bool ComputingLinkLibraries(cmGeneratorExpressionDAGChecker const* dagChecker) |
36 | 0 | { |
37 | 0 | return dagChecker && dagChecker->IsComputingLinkLibraries(); |
38 | 0 | } |
39 | | } |
40 | | |
41 | | std::map<cm::string_view, TransitiveProperty> const |
42 | | cmGeneratorTarget::BuiltinTransitiveProperties = { |
43 | | { "AUTOMOC_MACRO_NAMES"_s, |
44 | | { "INTERFACE_AUTOMOC_MACRO_NAMES"_s, UseTo::Compile } }, |
45 | | { "AUTOUIC_OPTIONS"_s, { "INTERFACE_AUTOUIC_OPTIONS"_s, UseTo::Compile } }, |
46 | | { "COMPILE_DEFINITIONS"_s, |
47 | | { "INTERFACE_COMPILE_DEFINITIONS"_s, UseTo::Compile } }, |
48 | | { "COMPILE_FEATURES"_s, |
49 | | { "INTERFACE_COMPILE_FEATURES"_s, UseTo::Compile } }, |
50 | | { "COMPILE_OPTIONS"_s, { "INTERFACE_COMPILE_OPTIONS"_s, UseTo::Compile } }, |
51 | | { "INCLUDE_DIRECTORIES"_s, |
52 | | { "INTERFACE_INCLUDE_DIRECTORIES"_s, UseTo::Compile } }, |
53 | | { "LINK_DEPENDS"_s, { "INTERFACE_LINK_DEPENDS"_s, UseTo::Link } }, |
54 | | { "LINK_DIRECTORIES"_s, { "INTERFACE_LINK_DIRECTORIES"_s, UseTo::Link } }, |
55 | | { "LINK_LIBRARIES"_s, { "INTERFACE_LINK_LIBRARIES"_s, UseTo::Link } }, |
56 | | { "LINK_OPTIONS"_s, { "INTERFACE_LINK_OPTIONS"_s, UseTo::Link } }, |
57 | | { "PRECOMPILE_HEADERS"_s, |
58 | | { "INTERFACE_PRECOMPILE_HEADERS"_s, UseTo::Compile } }, |
59 | | { "SOURCES"_s, { "INTERFACE_SOURCES"_s, UseTo::Compile } }, |
60 | | { "SYSTEM_INCLUDE_DIRECTORIES"_s, |
61 | | { "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"_s, UseTo::Compile } }, |
62 | | }; |
63 | | |
64 | | bool cmGeneratorTarget::MaybeHaveInterfaceProperty(std::string const& prop, |
65 | | cm::GenEx::Evaluation* eval, |
66 | | UseTo usage) const |
67 | 0 | { |
68 | 0 | std::string const key = cmStrCat(prop, '@', eval->Context.Config); |
69 | 0 | auto i = this->MaybeInterfacePropertyExists.find(key); |
70 | 0 | if (i == this->MaybeInterfacePropertyExists.end()) { |
71 | | // Insert an entry now in case there is a cycle. |
72 | 0 | i = this->MaybeInterfacePropertyExists.emplace(key, false).first; |
73 | 0 | bool& maybeInterfaceProp = i->second; |
74 | | |
75 | | // If this target itself has a non-empty property value, we are done. |
76 | 0 | maybeInterfaceProp = cmNonempty(this->GetProperty(prop)); |
77 | | |
78 | | // Otherwise, recurse to interface dependencies. |
79 | 0 | if (!maybeInterfaceProp) { |
80 | 0 | cmGeneratorTarget const* headTarget = |
81 | 0 | eval->HeadTarget ? eval->HeadTarget : this; |
82 | 0 | if (cmLinkInterfaceLibraries const* iface = |
83 | 0 | this->GetLinkInterfaceLibraries(eval->Context.Config, headTarget, |
84 | 0 | usage)) { |
85 | 0 | if (iface->HadHeadSensitiveCondition) { |
86 | | // With a different head target we may get to a library with |
87 | | // this interface property. |
88 | 0 | maybeInterfaceProp = true; |
89 | 0 | } else { |
90 | | // The transitive interface libraries do not depend on the |
91 | | // head target, so we can follow them. |
92 | 0 | for (cmLinkItem const& lib : iface->Libraries) { |
93 | 0 | if (lib.Target && |
94 | 0 | lib.Target->MaybeHaveInterfaceProperty(prop, eval, usage)) { |
95 | 0 | maybeInterfaceProp = true; |
96 | 0 | break; |
97 | 0 | } |
98 | 0 | } |
99 | 0 | } |
100 | 0 | } |
101 | 0 | } |
102 | 0 | } |
103 | 0 | return i->second; |
104 | 0 | } |
105 | | |
106 | | std::string cmGeneratorTarget::EvaluateInterfaceProperty( |
107 | | std::string const& prop, cm::GenEx::Evaluation* eval, |
108 | | cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const |
109 | 0 | { |
110 | 0 | std::string result; |
111 | | |
112 | | // If the property does not appear transitively at all, we are done. |
113 | 0 | if (!this->MaybeHaveInterfaceProperty(prop, eval, usage)) { |
114 | 0 | return result; |
115 | 0 | } |
116 | | |
117 | | // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is |
118 | | // a subset of TargetPropertyNode::Evaluate without stringify/parse steps |
119 | | // but sufficient for transitive interface properties. |
120 | 0 | cmGeneratorExpressionDAGChecker dagChecker{ |
121 | 0 | this, prop, nullptr, dagCheckerParent, eval->Context, eval->Backtrace, |
122 | 0 | }; |
123 | 0 | switch (dagChecker.Check()) { |
124 | 0 | case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: |
125 | 0 | dagChecker.ReportError( |
126 | 0 | eval, cmStrCat("$<TARGET_PROPERTY:", this->GetName(), ',', prop, '>')); |
127 | 0 | return result; |
128 | 0 | case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: |
129 | | // No error. We just skip cyclic references. |
130 | 0 | case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: |
131 | | // No error. We have already seen this transitive property. |
132 | 0 | return result; |
133 | 0 | case cmGeneratorExpressionDAGChecker::DAG: |
134 | 0 | break; |
135 | 0 | } |
136 | | |
137 | 0 | cmGeneratorTarget const* headTarget = |
138 | 0 | eval->HeadTarget ? eval->HeadTarget : this; |
139 | |
|
140 | 0 | if (cmValue p = this->GetProperty(prop)) { |
141 | 0 | result = cmGeneratorExpressionNode::EvaluateDependentExpression( |
142 | 0 | *p, eval, headTarget, &dagChecker, this); |
143 | 0 | } |
144 | |
|
145 | 0 | if (cmLinkInterfaceLibraries const* iface = this->GetLinkInterfaceLibraries( |
146 | 0 | eval->Context.Config, headTarget, usage)) { |
147 | 0 | eval->HadContextSensitiveCondition = eval->HadContextSensitiveCondition || |
148 | 0 | iface->HadContextSensitiveCondition; |
149 | 0 | for (cmLinkItem const& lib : iface->Libraries) { |
150 | | // Broken code can have a target in its own link interface. |
151 | | // Don't follow such link interface entries so as not to create a |
152 | | // self-referencing loop. |
153 | 0 | if (lib.Target && lib.Target != this) { |
154 | | // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the |
155 | | // above property and hand-evaluate it as if it were compiled. |
156 | | // Create a context as cmCompiledGeneratorExpression::Evaluate does. |
157 | 0 | cm::GenEx::Evaluation libEval(eval->Context, eval->Quiet, headTarget, |
158 | 0 | this, eval->EvaluateForBuildsystem, |
159 | 0 | eval->Backtrace); |
160 | 0 | std::string libResult = cmGeneratorExpression::StripEmptyListElements( |
161 | 0 | lib.Target->EvaluateInterfaceProperty(prop, &libEval, &dagChecker, |
162 | 0 | usage)); |
163 | 0 | if (!libResult.empty()) { |
164 | 0 | if (result.empty()) { |
165 | 0 | result = std::move(libResult); |
166 | 0 | } else { |
167 | 0 | result.reserve(result.size() + 1 + libResult.size()); |
168 | 0 | result += ";"; |
169 | 0 | result += libResult; |
170 | 0 | } |
171 | 0 | } |
172 | 0 | eval->HadContextSensitiveCondition = |
173 | 0 | eval->HadContextSensitiveCondition || |
174 | 0 | libEval.HadContextSensitiveCondition; |
175 | 0 | eval->HadHeadSensitiveCondition = |
176 | 0 | eval->HadHeadSensitiveCondition || libEval.HadHeadSensitiveCondition; |
177 | 0 | } |
178 | 0 | } |
179 | 0 | } |
180 | |
|
181 | 0 | return result; |
182 | 0 | } |
183 | | |
184 | | cm::optional<cmGeneratorTarget::TransitiveProperty> |
185 | | cmGeneratorTarget::IsTransitiveProperty( |
186 | | cm::string_view prop, cm::GenEx::Context const& context, |
187 | | cmGeneratorExpressionDAGChecker const* dagChecker) const |
188 | 0 | { |
189 | 0 | cm::optional<TransitiveProperty> result; |
190 | 0 | static cm::string_view const kINTERFACE_ = "INTERFACE_"_s; |
191 | 0 | PropertyFor const propertyFor = cmHasPrefix(prop, kINTERFACE_) |
192 | 0 | ? PropertyFor::Interface |
193 | 0 | : PropertyFor::Build; |
194 | 0 | if (propertyFor == PropertyFor::Interface) { |
195 | 0 | prop = prop.substr(kINTERFACE_.length()); |
196 | 0 | } |
197 | 0 | auto i = BuiltinTransitiveProperties.find(prop); |
198 | 0 | if (i != BuiltinTransitiveProperties.end() && |
199 | | // Look up CMP0189 in the context where evaluation occurs, |
200 | | // not where the target was created. |
201 | 0 | context.GetCMP0189() != cmPolicies::NEW && prop == "LINK_LIBRARIES"_s) { |
202 | 0 | i = BuiltinTransitiveProperties.end(); |
203 | 0 | } |
204 | 0 | if (i != BuiltinTransitiveProperties.end()) { |
205 | 0 | result = i->second; |
206 | 0 | if (result->Usage != cmGeneratorTarget::UseTo::Compile) { |
207 | 0 | cmPolicies::PolicyStatus cmp0166 = |
208 | 0 | context.LG->GetPolicyStatus(cmPolicies::CMP0166); |
209 | 0 | if ((cmp0166 == cmPolicies::WARN || cmp0166 == cmPolicies::OLD) && |
210 | 0 | (prop == "LINK_DIRECTORIES"_s || prop == "LINK_DEPENDS"_s || |
211 | 0 | prop == "LINK_OPTIONS"_s)) { |
212 | 0 | result->Usage = cmGeneratorTarget::UseTo::Compile; |
213 | 0 | } |
214 | 0 | } |
215 | 0 | } else if (!ComputingLinkLibraries(dagChecker)) { |
216 | | // Honor TRANSITIVE_COMPILE_PROPERTIES and TRANSITIVE_LINK_PROPERTIES |
217 | | // from the link closure when we are not evaluating the closure itself. |
218 | 0 | CustomTransitiveProperties const& ctp = |
219 | 0 | this->GetCustomTransitiveProperties(context.Config, propertyFor); |
220 | 0 | auto ci = ctp.find(std::string(prop)); |
221 | 0 | if (ci != ctp.end()) { |
222 | 0 | result = ci->second; |
223 | 0 | } |
224 | 0 | } |
225 | 0 | return result; |
226 | 0 | } |
227 | | |
228 | | cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty( |
229 | | std::string interfaceName, UseTo usage) |
230 | 0 | : CustomTransitiveProperty( |
231 | 0 | cm::make_unique<std::string>(std::move(interfaceName)), usage) |
232 | 0 | { |
233 | 0 | } |
234 | | cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty( |
235 | | std::unique_ptr<std::string> interfaceNameBuf, UseTo usage) |
236 | 0 | : TransitiveProperty{ *interfaceNameBuf, usage } |
237 | 0 | , InterfaceNameBuf(std::move(interfaceNameBuf)) |
238 | 0 | { |
239 | 0 | } |
240 | | |
241 | | void cmGeneratorTarget::CustomTransitiveProperties::Add(cmValue props, |
242 | | UseTo usage) |
243 | 0 | { |
244 | 0 | if (props) { |
245 | 0 | cmList propsList(*props); |
246 | 0 | for (std::string p : propsList) { |
247 | 0 | std::string ip; |
248 | 0 | static cm::string_view const kINTERFACE_ = "INTERFACE_"_s; |
249 | 0 | if (cmHasPrefix(p, kINTERFACE_)) { |
250 | 0 | ip = std::move(p); |
251 | 0 | p = ip.substr(kINTERFACE_.length()); |
252 | 0 | } else { |
253 | 0 | ip = cmStrCat(kINTERFACE_, p); |
254 | 0 | } |
255 | 0 | this->emplace(std::move(p), |
256 | 0 | CustomTransitiveProperty(std::move(ip), usage)); |
257 | 0 | } |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | | cmGeneratorTarget::CustomTransitiveProperties const& |
262 | | cmGeneratorTarget::GetCustomTransitiveProperties(std::string const& config, |
263 | | PropertyFor propertyFor) const |
264 | 0 | { |
265 | 0 | std::map<std::string, CustomTransitiveProperties>& ctpm = |
266 | 0 | propertyFor == PropertyFor::Build |
267 | 0 | ? this->CustomTransitiveBuildPropertiesMap |
268 | 0 | : this->CustomTransitiveInterfacePropertiesMap; |
269 | 0 | auto i = ctpm.find(config); |
270 | 0 | if (i == ctpm.end()) { |
271 | 0 | CustomTransitiveProperties ctp; |
272 | 0 | auto addTransitiveProperties = [this, &config, propertyFor, |
273 | 0 | &ctp](std::string const& tp, UseTo usage) { |
274 | | // Add transitive properties named by the target itself. |
275 | 0 | ctp.Add(this->GetProperty(tp), usage); |
276 | | // Add transitive properties named by the target's link dependencies. |
277 | 0 | if (propertyFor == PropertyFor::Build) { |
278 | 0 | for (cmGeneratorTarget const* gt : |
279 | 0 | this->GetLinkImplementationClosure(config, usage)) { |
280 | 0 | ctp.Add(gt->GetProperty(tp), usage); |
281 | 0 | } |
282 | 0 | } else { |
283 | | // The set of custom transitive INTERFACE_ properties does not |
284 | | // depend on the consumer. Use the target as its own head. |
285 | 0 | cmGeneratorTarget const* headTarget = this; |
286 | 0 | for (cmGeneratorTarget const* gt : |
287 | 0 | this->GetLinkInterfaceClosure(config, headTarget, usage)) { |
288 | 0 | ctp.Add(gt->GetProperty(tp), usage); |
289 | 0 | } |
290 | 0 | } |
291 | 0 | }; |
292 | 0 | addTransitiveProperties("TRANSITIVE_LINK_PROPERTIES", UseTo::Link); |
293 | 0 | addTransitiveProperties("TRANSITIVE_COMPILE_PROPERTIES", UseTo::Compile); |
294 | 0 | i = ctpm.emplace(config, std::move(ctp)).first; |
295 | 0 | } |
296 | 0 | return i->second; |
297 | 0 | } |