| // 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.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 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 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, | 
 |       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.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 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(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); | 
 |     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(); | 
 |       } | 
 |     }; | 
 |   } | 
 | } |