blob: 14bade6f02146ada1f999e511a639083ee5815ba [file] [log] [blame]
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.packages;
16
17import com.google.common.base.Preconditions;
18import com.google.common.collect.ImmutableMap;
19import com.google.common.collect.ImmutableMultimap;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.LinkedHashMultimap;
22import com.google.common.collect.Multimap;
23import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
Marian Loburfdd788e2015-03-25 09:36:28 +000024import com.google.devtools.build.lib.syntax.Label;
25import com.google.devtools.build.lib.util.BinaryPredicate;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026
27import java.util.LinkedHashMap;
28import java.util.LinkedHashSet;
Marian Loburfdd788e2015-03-25 09:36:28 +000029import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import java.util.Map;
31import java.util.Set;
32
33/**
34 * The definition of an aspect (see {@link com.google.devtools.build.lib.analysis.Aspect} for more
35 * information.)
36 *
37 * <p>Contains enough information to build up the configured target graph except for the actual way
38 * to build the Skyframe node (that is the territory of
39 * {@link com.google.devtools.build.lib.view AspectFactory}). In particular:
40 * <ul>
41 * <li>The condition that must be fulfilled for an aspect to be able to operate on a configured
42 * target
43 * <li>The (implicit or late-bound) attributes of the aspect that denote dependencies the aspect
44 * itself needs (e.g. runtime libraries for a new language for protocol buffers)
45 * <li>The aspects this aspect requires from its direct dependencies
46 * </ul>
47 *
48 * <p>The way to build the Skyframe node is not here because this data needs to be accessible from
49 * the {@code .packages} package and that one requires references to the {@code .view} package.
50 */
51@Immutable
52public final class AspectDefinition {
53
54 private final String name;
55 private final ImmutableSet<Class<?>> requiredProviders;
56 private final ImmutableMap<String, Attribute> attributes;
57 private final ImmutableMultimap<String, Class<? extends AspectFactory<?, ?, ?>>> attributeAspects;
58
59 private AspectDefinition(
60 String name,
61 ImmutableSet<Class<?>> requiredProviders,
62 ImmutableMap<String, Attribute> attributes,
63 ImmutableMultimap<String, Class<? extends AspectFactory<?, ?, ?>>> attributeAspects) {
64 this.name = name;
65 this.requiredProviders = requiredProviders;
66 this.attributes = attributes;
67 this.attributeAspects = attributeAspects;
68 }
69
70 public String getName() {
71 return name;
72 }
73
74 /**
75 * Returns the attributes of the aspect in the form of a String -&gt; {@link Attribute} map.
76 *
77 * <p>All attributes are either implicit or late-bound.
78 */
79 public ImmutableMap<String, Attribute> getAttributes() {
80 return attributes;
81 }
82
83 /**
84 * Returns the set of {@link com.google.devtools.build.lib.analysis.TransitiveInfoProvider} instances
85 * that must be present on a configured target so that this aspect can be applied to it.
86 *
87 * <p>We cannot refer to that class here due to our dependency structure, so this returns a set
88 * of unconstrained class objects.
89 *
90 * <p>If a configured target does not have a required provider, the aspect is silently not created
91 * for it.
92 */
93 public ImmutableSet<Class<?>> getRequiredProviders() {
94 return requiredProviders;
95 }
96
97 /**
98 * Returns the attribute -&gt; set of required aspects map.
99 *
100 * <p>Note that the map actually contains {@link AspectFactory}
101 * instances, except that we cannot reference that class here.
102 */
103 public ImmutableMultimap<String, Class<? extends AspectFactory<?, ?, ?>>> getAttributeAspects() {
104 return attributeAspects;
105 }
106
107 /**
Marian Loburfdd788e2015-03-25 09:36:28 +0000108 * Returns the attribute -&gt; set of labels that are provided by aspects of attribute.
109 */
110 public static ImmutableMultimap<Attribute, Label> visitAspectsIfRequired(
111 Target from, Attribute attribute, Target to) {
Marian Loburfdd788e2015-03-25 09:36:28 +0000112 // Aspect can be declared only for Rules.
113 if (!(from instanceof Rule) || !(to instanceof Rule)) {
Lukacs Berkibcb81ab2015-06-10 08:24:43 +0000114 return ImmutableMultimap.of();
Marian Loburfdd788e2015-03-25 09:36:28 +0000115 }
Lukacs Berkibcb81ab2015-06-10 08:24:43 +0000116 LinkedHashMultimap<Attribute, Label> result = LinkedHashMultimap.create();
Marian Loburfdd788e2015-03-25 09:36:28 +0000117 RuleClass ruleClass = ((Rule) to).getRuleClassObject();
118 for (Class<? extends AspectFactory<?, ?, ?>> candidateClass : attribute.getAspects()) {
119 AspectFactory<?, ?, ?> candidate = AspectFactory.Util.create(candidateClass);
120 // Check if target satisfies condition for this aspect (has to provide all required
121 // TransitiveInfoProviders)
122 if (!ruleClass.getAdvertisedProviders().containsAll(
123 candidate.getDefinition().getRequiredProviders())) {
124 continue;
125 }
Lukacs Berkibcb81ab2015-06-10 08:24:43 +0000126 addAllAttributesOfAspect((Rule) from, result, candidate.getDefinition(), Rule.ALL_DEPS);
Marian Loburfdd788e2015-03-25 09:36:28 +0000127 }
Lukacs Berkibcb81ab2015-06-10 08:24:43 +0000128 return ImmutableMultimap.copyOf(result);
Marian Loburfdd788e2015-03-25 09:36:28 +0000129 }
130
131 /**
132 * Collects all attribute labels from the specified aspectDefinition.
133 */
134 public static void addAllAttributesOfAspect(Rule from,
Lukacs Berkibcb81ab2015-06-10 08:24:43 +0000135 Multimap<Attribute, Label> labelBuilder, AspectDefinition aspectDefinition,
Marian Loburfdd788e2015-03-25 09:36:28 +0000136 BinaryPredicate<Rule, Attribute> predicate) {
137 ImmutableMap<String, Attribute> attributes = aspectDefinition.getAttributes();
138 for (Attribute aspectAttribute : attributes.values()) {
139 if (!predicate.apply(from, aspectAttribute)) {
140 continue;
141 }
142 if (aspectAttribute.getType() == Type.LABEL) {
143 Label label = Type.LABEL.cast(aspectAttribute.getDefaultValue(from));
144 if (label != null) {
145 labelBuilder.put(aspectAttribute, label);
146 }
147 } else if (aspectAttribute.getType() == Type.LABEL_LIST) {
148 List<Label> labelList = Type.LABEL_LIST.cast(aspectAttribute.getDefaultValue(from));
149 labelBuilder.putAll(aspectAttribute, labelList);
150 }
151 }
152 }
153
154 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100155 * Builder class for {@link AspectDefinition}.
156 */
157 public static final class Builder {
158 private final String name;
159 private final Map<String, Attribute> attributes = new LinkedHashMap<>();
160 private final Set<Class<?>> requiredProviders = new LinkedHashSet<>();
161 private final Multimap<String, Class<? extends AspectFactory<?, ?, ?>>> attributeAspects =
162 LinkedHashMultimap.create();
163
164 public Builder(String name) {
165 this.name = name;
166 }
167
168 /**
169 * Asserts that this aspect can only be evaluated for rules that supply the specified provider.
170 */
171 public Builder requireProvider(Class<?> requiredProvider) {
172 this.requiredProviders.add(requiredProvider);
173 return this;
174 }
175
176 /**
Rumou Duan5b0b3102015-06-03 20:27:57 +0000177 * Declares that this aspect depends on the given aspects in {@code aspectFactories} provided
178 * by direct dependencies through attribute {@code attribute} on the target associated with this
179 * aspect.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100180 *
181 * <p>Note that {@code AspectFactory} instances are expected in the second argument, but we
182 * cannot reference that interface here.
183 */
Ulf Adams4b5a3f72015-08-05 09:47:57 +0000184 @SafeVarargs
185 public final Builder attributeAspect(
Rumou Duan5b0b3102015-06-03 20:27:57 +0000186 String attribute, Class<? extends AspectFactory<?, ?, ?>>... aspectFactories) {
187 Preconditions.checkNotNull(attribute);
188 for (Class<? extends AspectFactory<?, ?, ?>> aspectFactory : aspectFactories) {
189 this.attributeAspects.put(attribute, Preconditions.checkNotNull(aspectFactory));
190 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100191 return this;
192 }
193
194 /**
195 * Adds an attribute to the aspect.
196 *
197 * <p>Since aspects do not appear in BUILD files, the attribute must be either implicit
198 * (not available in the BUILD file, starting with '$') or late-bound (determined after the
199 * configuration is available, starting with ':')
200 */
201 public <TYPE> Builder add(Attribute.Builder<TYPE> attr) {
202 Attribute attribute = attr.build();
203 Preconditions.checkState(attribute.isImplicit() || attribute.isLateBound());
204 Preconditions.checkState(!attributes.containsKey(attribute.getName()),
205 "An attribute with the name '%s' already exists.", attribute.getName());
206 attributes.put(attribute.getName(), attribute);
207 return this;
208 }
209
210 /**
211 * Builds the aspect definition.
212 *
213 * <p>The builder object is reusable afterwards.
214 */
215 public AspectDefinition build() {
216 return new AspectDefinition(name, ImmutableSet.copyOf(requiredProviders),
217 ImmutableMap.copyOf(attributes), ImmutableMultimap.copyOf(attributeAspects));
218 }
219 }
220}