| // 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.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.syntax.BaseFunction; | |
| import com.google.devtools.build.lib.syntax.Environment; | |
| import com.google.devtools.build.lib.syntax.Printer; | |
| import com.google.devtools.build.lib.syntax.Type; | |
| import com.google.devtools.build.lib.util.Pair; | |
| import com.google.devtools.build.lib.util.Preconditions; | |
| import java.util.Arrays; | |
| import java.util.List; | |
| import javax.annotation.Nullable; | |
| /** 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 ImmutableSet<String> paramAttributes; | |
| private final ImmutableSet<String> fragments; | |
| private final ImmutableSet<String> hostFragments; | |
| private final Environment funcallEnv; | |
| private SkylarkAspectClass aspectClass; | |
| public SkylarkAspect( | |
| BaseFunction implementation, | |
| ImmutableList<String> attributeAspects, | |
| ImmutableList<Attribute> attributes, | |
| ImmutableSet<String> paramAttributes, | |
| ImmutableSet<String> fragments, | |
| ImmutableSet<String> hostFragments, | |
| Environment funcallEnv) { | |
| this.implementation = implementation; | |
| this.attributeAspects = attributeAspects; | |
| this.attributes = attributes; | |
| this.paramAttributes = paramAttributes; | |
| this.fragments = fragments; | |
| this.hostFragments = hostFragments; | |
| this.funcallEnv = funcallEnv; | |
| ImmutableList.Builder<Pair<String, Attribute>> builder = ImmutableList.builder(); | |
| } | |
| 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 write(Appendable buffer, char quotationMark) { | |
| Printer.append(buffer, "Aspect:"); | |
| implementation.write(buffer, quotationMark); | |
| } | |
| 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(getName()); | |
| if (allAttrAspects.equals(attributeAspects)) { | |
| builder.allAttributesAspect(aspectClass); | |
| } else { | |
| for (String attributeAspect : attributeAspects) { | |
| builder.attributeAspect(attributeAspect, aspectClass); | |
| } | |
| } | |
| 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.requiresConfigurationFragmentsBySkylarkModuleName(fragments); | |
| builder.requiresHostConfigurationFragmentsBySkylarkModuleName(hostFragments); | |
| return builder.build(); | |
| } | |
| @Override | |
| public boolean isExported() { | |
| return aspectClass != null; | |
| } | |
| public Function<Rule, AspectParameters> getDefaultParametersExtractor() { | |
| return new Function<Rule, AspectParameters>() { | |
| @Nullable | |
| @Override | |
| public AspectParameters apply(Rule 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(); | |
| } | |
| }; | |
| } | |
| } |