blob: ffb979452b50b763761c20fb5aa0ad5e33a64d94 [file] [log] [blame]
// Copyright 2016 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.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Printer;
import com.google.devtools.build.lib.syntax.Starlark;
import com.google.devtools.build.lib.syntax.StarlarkCallable;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/** A Starlark value that is a result of an 'aspect(..)' function call. */
@AutoCodec
public class StarlarkDefinedAspect implements StarlarkExportable, StarlarkAspect {
private final StarlarkCallable implementation;
private final ImmutableList<String> attributeAspects;
private final ImmutableList<Attribute> attributes;
private final ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> requiredAspectProviders;
private final ImmutableSet<StarlarkProviderIdentifier> provides;
private final ImmutableSet<String> paramAttributes;
private final ImmutableSet<String> fragments;
private final ConfigurationTransition hostTransition;
private final ImmutableSet<String> hostFragments;
private final ImmutableList<Label> requiredToolchains;
private final boolean useToolchainTransition;
private final boolean applyToGeneratingRules;
private StarlarkAspectClass aspectClass;
public StarlarkDefinedAspect(
StarlarkCallable implementation,
ImmutableList<String> attributeAspects,
ImmutableList<Attribute> attributes,
ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> requiredAspectProviders,
ImmutableSet<StarlarkProviderIdentifier> provides,
ImmutableSet<String> paramAttributes,
ImmutableSet<String> fragments,
// The host transition is in lib.analysis, so we can't reference it directly here.
ConfigurationTransition hostTransition,
ImmutableSet<String> hostFragments,
ImmutableList<Label> requiredToolchains,
boolean useToolchainTransition,
boolean applyToGeneratingRules) {
this.implementation = implementation;
this.attributeAspects = attributeAspects;
this.attributes = attributes;
this.requiredAspectProviders = requiredAspectProviders;
this.provides = provides;
this.paramAttributes = paramAttributes;
this.fragments = fragments;
this.hostTransition = hostTransition;
this.hostFragments = hostFragments;
this.requiredToolchains = requiredToolchains;
this.useToolchainTransition = useToolchainTransition;
this.applyToGeneratingRules = applyToGeneratingRules;
}
/** Constructor for post export reconstruction for serialization. */
@VisibleForSerialization
@AutoCodec.Instantiator
StarlarkDefinedAspect(
StarlarkCallable implementation,
ImmutableList<String> attributeAspects,
ImmutableList<Attribute> attributes,
ImmutableList<ImmutableSet<StarlarkProviderIdentifier>> requiredAspectProviders,
ImmutableSet<StarlarkProviderIdentifier> provides,
ImmutableSet<String> paramAttributes,
ImmutableSet<String> fragments,
// The host transition is in lib.analysis, so we can't reference it directly here.
ConfigurationTransition hostTransition,
ImmutableSet<String> hostFragments,
ImmutableList<Label> requiredToolchains,
boolean useToolchainTransition,
boolean applyToGeneratingRules,
StarlarkAspectClass aspectClass) {
this(
implementation,
attributeAspects,
attributes,
requiredAspectProviders,
provides,
paramAttributes,
fragments,
hostTransition,
hostFragments,
requiredToolchains,
useToolchainTransition,
applyToGeneratingRules);
this.aspectClass = aspectClass;
}
public StarlarkCallable getImplementation() {
return implementation;
}
public ImmutableList<String> getAttributeAspects() {
return attributeAspects;
}
public ImmutableList<Attribute> getAttributes() {
return attributes;
}
@Override
public boolean isImmutable() {
return implementation.isImmutable();
}
@Override
public void repr(Printer printer) {
printer.append("<aspect>");
}
@Override
public String getName() {
return getAspectClass().getName();
}
@Override
public StarlarkAspectClass getAspectClass() {
Preconditions.checkState(isExported());
return aspectClass;
}
@Override
public ImmutableSet<String> getParamAttributes() {
return paramAttributes;
}
@Override
public void export(Label extensionLabel, String name) {
Preconditions.checkArgument(!isExported());
this.aspectClass = new StarlarkAspectClass(extensionLabel, name);
}
private static final List<String> allAttrAspects = Arrays.asList("*");
public AspectDefinition getDefinition(AspectParameters aspectParams) {
AspectDefinition.Builder builder = new AspectDefinition.Builder(aspectClass);
if (allAttrAspects.equals(attributeAspects)) {
builder.propagateAlongAllAttributes();
} else {
for (String attributeAspect : attributeAspects) {
builder.propagateAlongAttribute(attributeAspect);
}
}
for (Attribute attribute : attributes) {
Attribute attr = attribute; // Might be reassigned.
if (!aspectParams.getAttribute(attr.getName()).isEmpty()) {
String value = aspectParams.getOnlyValueOfAttribute(attr.getName());
Preconditions.checkState(!Attribute.isImplicit(attr.getName()));
Preconditions.checkState(attr.getType() == Type.STRING);
Preconditions.checkArgument(
aspectParams.getAttribute(attr.getName()).size() == 1,
"Aspect %s parameter %s has %s values (must have exactly 1).",
getName(),
attr.getName(),
aspectParams.getAttribute(attr.getName()).size());
attr = attr.cloneBuilder(Type.STRING).value(value).build(attr.getName());
}
builder.add(attr);
}
builder.requireAspectsWithProviders(requiredAspectProviders);
ImmutableList.Builder<StarlarkProviderIdentifier> advertisedStarlarkProviders =
ImmutableList.builder();
for (StarlarkProviderIdentifier provider : provides) {
advertisedStarlarkProviders.add(provider);
}
builder.advertiseProvider(advertisedStarlarkProviders.build());
builder.requiresConfigurationFragmentsByStarlarkBuiltinName(fragments);
builder.requiresConfigurationFragmentsByStarlarkBuiltinName(hostTransition, hostFragments);
builder.addRequiredToolchains(requiredToolchains);
builder.useToolchainTransition(useToolchainTransition);
builder.applyToGeneratingRules(applyToGeneratingRules);
return builder.build();
}
@Override
public boolean isExported() {
return aspectClass != null;
}
public Function<Rule, AspectParameters> getDefaultParametersExtractor() {
return rule -> {
AttributeMap ruleAttrs = RawAttributeMapper.of(rule);
AspectParameters.Builder builder = new AspectParameters.Builder();
for (Attribute aspectAttr : attributes) {
String param = aspectAttr.getName();
if (Attribute.isImplicit(param) || Attribute.isLateBound(param)) {
// These attributes are the private matters of the aspect
continue;
}
Attribute ruleAttr = ruleAttrs.getAttributeDefinition(param);
if (paramAttributes.contains(aspectAttr.getName())) {
// These are preconditions because if they are false, RuleFunction.call() should
// already have generated an error.
Preconditions.checkArgument(
ruleAttr != null,
"Cannot apply aspect %s to %s that does not define attribute '%s'.",
getName(),
rule.getTargetKind(),
param);
Preconditions.checkArgument(
ruleAttr.getType() == Type.STRING,
"Cannot apply aspect %s to %s with non-string attribute '%s'.",
getName(),
rule.getTargetKind(),
param);
}
if (ruleAttr != null && ruleAttr.getType() == aspectAttr.getType()) {
// If the attribute has a select() (which aspect attributes don't yet support), the
// error gets reported in RuleClass.checkAspectAllowedValues.
if (!ruleAttrs.isConfigurable(param)) {
builder.addAttribute(param, (String) ruleAttrs.get(param, ruleAttr.getType()));
}
}
}
return builder.build();
};
}
public ImmutableList<Label> getRequiredToolchains() {
return requiredToolchains;
}
public boolean useToolchainTransition() {
return useToolchainTransition;
}
@Override
public void attachToAttribute(Attribute.Builder<?> attrBuilder) throws EvalException {
if (!isExported()) {
throw Starlark.errorf(
"Aspects should be top-level values in extension files that define them.");
}
attrBuilder.aspect(this);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
StarlarkDefinedAspect that = (StarlarkDefinedAspect) o;
return Objects.equals(implementation, that.implementation)
&& Objects.equals(attributeAspects, that.attributeAspects)
&& Objects.equals(attributes, that.attributes)
&& Objects.equals(requiredAspectProviders, that.requiredAspectProviders)
&& Objects.equals(provides, that.provides)
&& Objects.equals(paramAttributes, that.paramAttributes)
&& Objects.equals(fragments, that.fragments)
&& Objects.equals(hostTransition, that.hostTransition)
&& Objects.equals(hostFragments, that.hostFragments)
&& Objects.equals(requiredToolchains, that.requiredToolchains)
&& Objects.equals(useToolchainTransition, that.useToolchainTransition)
&& Objects.equals(aspectClass, that.aspectClass);
}
@Override
public int hashCode() {
return Objects.hash(
implementation,
attributeAspects,
attributes,
requiredAspectProviders,
provides,
paramAttributes,
fragments,
hostTransition,
hostFragments,
requiredToolchains,
useToolchainTransition,
aspectClass);
}
}