blob: 7e187d2cf2be9ea4ed5c03637d230edfd9324a4f [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.cmdline.Label;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.Type;
import java.util.Arrays;
import java.util.List;
/** A Skylark value that is a result of an 'aspect(..)' function call. */
@SkylarkModule(
name = "Aspect",
category = SkylarkModuleCategory.NONE,
doc =
"For more information about Aspects, please consult the <a href=\"globals.html#aspect\">"
+ "documentation of the aspect function</a> or the "
+ "<a href=\"../aspects.md\">introduction to Aspects</a>."
)
public class SkylarkAspect implements SkylarkExportable {
private final BaseFunction implementation;
private final ImmutableList<String> attributeAspects;
private final ImmutableList<Attribute> attributes;
private final ImmutableList<ImmutableSet<SkylarkProviderIdentifier>> requiredAspectProviders;
private final ImmutableSet<SkylarkProviderIdentifier> provides;
private final ImmutableSet<String> paramAttributes;
private final ImmutableSet<String> fragments;
private final ImmutableSet<String> hostFragments;
private final ImmutableList<Label> requiredToolchains;
private final Environment funcallEnv;
private SkylarkAspectClass aspectClass;
public SkylarkAspect(
BaseFunction implementation,
ImmutableList<String> attributeAspects,
ImmutableList<Attribute> attributes,
ImmutableList<ImmutableSet<SkylarkProviderIdentifier>> requiredAspectProviders,
ImmutableSet<SkylarkProviderIdentifier> provides,
ImmutableSet<String> paramAttributes,
ImmutableSet<String> fragments,
ImmutableSet<String> hostFragments,
ImmutableList<Label> requiredToolchains,
Environment funcallEnv) {
this.implementation = implementation;
this.attributeAspects = attributeAspects;
this.attributes = attributes;
this.requiredAspectProviders = requiredAspectProviders;
this.provides = provides;
this.paramAttributes = paramAttributes;
this.fragments = fragments;
this.hostFragments = hostFragments;
this.requiredToolchains = requiredToolchains;
this.funcallEnv = funcallEnv;
}
public BaseFunction getImplementation() {
return implementation;
}
public ImmutableList<String> getAttributeAspects() {
return attributeAspects;
}
public Environment getFuncallEnv() {
return funcallEnv;
}
public ImmutableList<Attribute> getAttributes() {
return attributes;
}
@Override
public boolean isImmutable() {
return implementation.isImmutable();
}
@Override
public void repr(SkylarkPrinter printer) {
printer.append("<aspect>");
}
public String getName() {
return getAspectClass().getName();
}
public SkylarkAspectClass getAspectClass() {
Preconditions.checkState(isExported());
return aspectClass;
}
public ImmutableSet<String> getParamAttributes() {
return paramAttributes;
}
@Override
public void export(Label extensionLabel, String name) {
Preconditions.checkArgument(!isExported());
this.aspectClass = new SkylarkAspectClass(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,
String.format("Aspect %s parameter %s has %d 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<SkylarkProviderIdentifier> advertisedSkylarkProviders =
ImmutableList.builder();
for (SkylarkProviderIdentifier provider : provides) {
advertisedSkylarkProviders.add(provider);
}
builder.advertiseProvider(advertisedSkylarkProviders.build());
builder.requiresConfigurationFragmentsBySkylarkModuleName(fragments);
builder.requiresHostConfigurationFragmentsBySkylarkModuleName(hostFragments);
builder.addRequiredToolchains(requiredToolchains);
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) {
if (!Attribute.isImplicit(aspectAttr.getName())) {
String param = aspectAttr.getName();
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,
String.format(
"Cannot apply aspect %s to %s that does not define attribute '%s'.",
getName(), rule.getTargetKind(), param));
Preconditions.checkArgument(
ruleAttr.getType() == Type.STRING,
String.format(
"Cannot apply aspect %s to %s with non-string attribute '%s'.",
getName(), rule.getTargetKind(), param));
}
if (ruleAttr != null && ruleAttr.getType() == aspectAttr.getType()) {
builder.addAttribute(param, (String) ruleAttrs.get(param, ruleAttr.getType()));
}
}
}
return builder.build();
};
}
public ImmutableList<Label> getRequiredToolchains() {
return requiredToolchains;
}
}