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