// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.packages; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy.MissingFragmentPolicy; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.Preconditions; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * The definition of an aspect (see {@link Aspect} for moreinformation.) * *

Contains enough information to build up the configured target graph except for the actual way * to build the Skyframe node (that is the territory of * {@link com.google.devtools.build.lib.view AspectFactory}). In particular: *

* *

The way to build the Skyframe node is not here because this data needs to be accessible from * the {@code .packages} package and that one requires references to the {@code .view} package. */ @Immutable public final class AspectDefinition { private final String name; private final ImmutableSet> requiredProviders; private final ImmutableSet requiredProviderNames; private final ImmutableMap attributes; private final ImmutableMultimap attributeAspects; @Nullable private final ConfigurationFragmentPolicy configurationFragmentPolicy; private AspectDefinition( String name, ImmutableSet> requiredProviders, ImmutableMap attributes, ImmutableMultimap attributeAspects, @Nullable ConfigurationFragmentPolicy configurationFragmentPolicy) { this.name = name; this.requiredProviders = requiredProviders; this.requiredProviderNames = toStringSet(requiredProviders); this.attributes = attributes; this.attributeAspects = attributeAspects; this.configurationFragmentPolicy = configurationFragmentPolicy; } public String getName() { return name; } /** * Returns the attributes of the aspect in the form of a String -> {@link Attribute} map. * *

All attributes are either implicit or late-bound. */ public ImmutableMap getAttributes() { return attributes; } /** * Returns the set of {@link com.google.devtools.build.lib.analysis.TransitiveInfoProvider} * instances that must be present on a configured target so that this aspect can be applied to it. * *

We cannot refer to that class here due to our dependency structure, so this returns a set * of unconstrained class objects. * *

If a configured target does not have a required provider, the aspect is silently not created * for it. */ public ImmutableSet> getRequiredProviders() { return requiredProviders; } /** * Returns the set of class names of * {@link com.google.devtools.build.lib.analysis.TransitiveInfoProvider} instances that must be * present on a configured target so that this aspect can be applied to it. * *

This set is a mirror of the set returned by {@link #getRequiredProviders}, but contains the * names of the classes rather than the class objects themselves. * *

If a configured target does not have a required provider, the aspect is silently not created * for it. */ public ImmutableSet getRequiredProviderNames() { return requiredProviderNames; } /** * Returns the attribute -> set of required aspects map. */ public ImmutableMultimap getAttributeAspects() { return attributeAspects; } /** * Returns the set of configuration fragments required by this Aspect. */ public ConfigurationFragmentPolicy getConfigurationFragmentPolicy() { return configurationFragmentPolicy; } /** * Returns the attribute -> set of labels that are provided by aspects of attribute. */ public static ImmutableMultimap visitAspectsIfRequired( Target from, Attribute attribute, Target to, DependencyFilter dependencyFilter) { // Aspect can be declared only for Rules. if (!(from instanceof Rule) || !(to instanceof Rule)) { return ImmutableMultimap.of(); } RuleClass ruleClass = ((Rule) to).getRuleClassObject(); ImmutableSet> providers = ruleClass.getAdvertisedProviders(); return visitAspectsIfRequired((Rule) from, attribute, ruleClass.canHaveAnyProvider(), toStringSet(providers), dependencyFilter); } /** * Returns the attribute -> set of labels that are provided by aspects of attribute. */ public static ImmutableMultimap visitAspectsIfRequired( Rule from, Attribute attribute, boolean canHaveAnyProvider, Set advertisedProviders, DependencyFilter dependencyFilter) { if (advertisedProviders.isEmpty() && !canHaveAnyProvider) { return ImmutableMultimap.of(); } LinkedHashMultimap result = LinkedHashMultimap.create(); for (Aspect candidateClass : attribute.getAspects(from)) { // Check if target satisfies condition for this aspect (has to provide all required // TransitiveInfoProviders) if (!canHaveAnyProvider && !advertisedProviders.containsAll( candidateClass.getDefinition().getRequiredProviderNames())) { continue; } addAllAttributesOfAspect(from, result, candidateClass, dependencyFilter); } return ImmutableMultimap.copyOf(result); } private static ImmutableSet toStringSet(ImmutableSet> classes) { ImmutableSet.Builder classStrings = new ImmutableSet.Builder<>(); for (Class clazz : classes) { classStrings.add(clazz.getName()); } return classStrings.build(); } @Nullable private static Label maybeGetRepositoryRelativeLabel(Rule from, @Nullable Label label) { return label == null ? null : from.getLabel().resolveRepositoryRelative(label); } /** * Collects all attribute labels from the specified aspectDefinition. */ public static void addAllAttributesOfAspect( Rule from, Multimap labelBuilder, Aspect aspect, DependencyFilter dependencyFilter) { ImmutableMap attributes = aspect.getDefinition().getAttributes(); for (Attribute aspectAttribute : attributes.values()) { if (!dependencyFilter.apply(aspect, aspectAttribute)) { continue; } if (aspectAttribute.getType() == BuildType.LABEL) { Label label = maybeGetRepositoryRelativeLabel( from, BuildType.LABEL.cast(aspectAttribute.getDefaultValue(from))); if (label != null) { labelBuilder.put(aspectAttribute, label); } } else if (aspectAttribute.getType() == BuildType.LABEL_LIST) { List

Note that {@code ConfiguredAspectFactory} instances are expected in the second argument, * but we cannot reference that interface here. */ @SafeVarargs public final Builder attributeAspect(String attribute, NativeAspectClass... aspectClasses) { Preconditions.checkNotNull(attribute); for (NativeAspectClass aspectClass : aspectClasses) { this.attributeAspect(attribute, Preconditions.checkNotNull(aspectClass)); } return this; } /** * Declares that this aspect depends on the given {@link AspectClass} provided * by direct dependencies through attribute {@code attribute} on the target associated with this * aspect. */ public final Builder attributeAspect(String attribute, AspectClass aspectClass) { Preconditions.checkNotNull(attribute); this.attributeAspects.put(attribute, Preconditions.checkNotNull(aspectClass)); return this; } /** * Adds an attribute to the aspect. * *

Since aspects do not appear in BUILD files, the attribute must be either implicit * (not available in the BUILD file, starting with '$') or late-bound (determined after the * configuration is available, starting with ':') */ public Builder add(Attribute.Builder attr) { Attribute attribute = attr.build(); return add(attribute); } /** * Adds an attribute to the aspect. * *

Since aspects do not appear in BUILD files, the attribute must be either implicit * (not available in the BUILD file, starting with '$') or late-bound (determined after the * configuration is available, starting with ':') */ public Builder add(Attribute attribute) { Preconditions.checkArgument(attribute.isImplicit() || attribute.isLateBound() || (attribute.getType() == Type.STRING && attribute.checkAllowedValues()), "Invalid attribute '%s' (%s)", attribute.getName(), attribute.getType()); Preconditions.checkArgument(!attributes.containsKey(attribute.getName()), "An attribute with the name '%s' already exists.", attribute.getName()); attributes.put(attribute.getName(), attribute); return this; } /** * Declares that the implementation of the associated aspect definition requires the given * fragments to be present in this rule's host and target configurations. * *

The value is inherited by subclasses. */ public Builder requiresConfigurationFragments(Class... configurationFragments) { configurationFragmentPolicy .requiresConfigurationFragments(ImmutableSet.copyOf(configurationFragments)); return this; } /** * Declares that the implementation of the associated aspect definition requires the given * fragments to be present in the host configuration. * *

The value is inherited by subclasses. */ public Builder requiresHostConfigurationFragments(Class... configurationFragments) { configurationFragmentPolicy .requiresHostConfigurationFragments(ImmutableSet.copyOf(configurationFragments)); return this; } /** * Declares the configuration fragments that are required by this rule for the target * configuration. * *

In contrast to {@link #requiresConfigurationFragments(Class...)}, this method takes the * Skylark module names of fragments instead of their classes. */ public Builder requiresConfigurationFragmentsBySkylarkModuleName( Collection configurationFragmentNames) { configurationFragmentPolicy .requiresConfigurationFragmentsBySkylarkModuleName(configurationFragmentNames); return this; } /** * Declares the configuration fragments that are required by this rule for the host * configuration. * *

In contrast to {@link #requiresHostConfigurationFragments(Class...)}, this method takes * the Skylark module names of fragments instead of their classes. */ public Builder requiresHostConfigurationFragmentsBySkylarkModuleName( Collection configurationFragmentNames) { configurationFragmentPolicy .requiresHostConfigurationFragmentsBySkylarkModuleName(configurationFragmentNames); return this; } /** * Sets the policy for the case where the configuration is missing required fragments (see * {@link #requiresConfigurationFragments}). */ public Builder setMissingFragmentPolicy(MissingFragmentPolicy missingFragmentPolicy) { configurationFragmentPolicy.setMissingFragmentPolicy(missingFragmentPolicy); return this; } /** * Builds the aspect definition. * *

The builder object is reusable afterwards. */ public AspectDefinition build() { return new AspectDefinition(name, ImmutableSet.copyOf(requiredProviders), ImmutableMap.copyOf(attributes), ImmutableSetMultimap.copyOf(attributeAspects), configurationFragmentPolicy.build()); } } }