Add toolchains attribute to aspect.

Part of #2219.

Change-Id: I39ced1f3e2605154771df9424d6ed2f971820baf
PiperOrigin-RevId: 157002268
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
index b1ec35d..3e48b7a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
@@ -244,6 +244,9 @@
             .setConfigConditions(configConditions)
             .setUniversalFragment(ruleClassProvider.getUniversalFragment())
             .setSkylarkProvidersRegistry(ruleClassProvider.getRegisteredSkylarkProviders())
+            // TODO(katre): Populate the actual selected toolchains.
+            .setToolchainContext(
+                new ToolchainContext(rule.getRuleClassObject().getRequiredToolchains(), null))
             .build();
     if (ruleContext.hasErrors()) {
       return null;
@@ -350,6 +353,9 @@
             .setAspectAttributes(aspect.getDefinition().getAttributes())
             .setConfigConditions(configConditions)
             .setUniversalFragment(ruleClassProvider.getUniversalFragment())
+            // TODO(katre): Populate the actual selected toolchains.
+            .setToolchainContext(
+                new ToolchainContext(aspect.getDefinition().getRequiredToolchains(), null))
             .build();
     if (ruleContext.hasErrors()) {
       return null;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 5989b62..170d323 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -167,7 +167,7 @@
   private final ErrorReporter reporter;
   private final ImmutableBiMap<String, Class<? extends TransitiveInfoProvider>>
       skylarkProviderRegistry;
-  private final ToolchainContext toolchainContext;
+  @Nullable private final ToolchainContext toolchainContext;
 
   private ActionOwner actionOwner;
 
@@ -182,7 +182,8 @@
       ImmutableMap<Label, ConfigMatchingProvider> configConditions,
       Class<? extends BuildConfiguration.Fragment> universalFragment,
       String ruleClassNameForLogging,
-      ImmutableMap<String, Attribute> aspectAttributes) {
+      ImmutableMap<String, Attribute> aspectAttributes,
+      @Nullable ToolchainContext toolchainContext) {
     super(builder.env, builder.rule, builder.configuration, builder.prerequisiteMap.get(null),
         builder.visibility);
     this.rule = builder.rule;
@@ -198,8 +199,7 @@
     this.skylarkProviderRegistry = builder.skylarkProviderRegistry;
     this.hostConfiguration = builder.hostConfiguration;
     reporter = builder.reporter;
-    // TODO(katre): Populate the actual selected toolchains.
-    this.toolchainContext = new ToolchainContext(null);
+    this.toolchainContext = toolchainContext;
   }
 
   private ImmutableSet<String> getEnabledFeatures() {
@@ -1124,6 +1124,7 @@
     }
   }
 
+  @Nullable
   public ToolchainContext getToolchainContext() {
     return toolchainContext;
   }
@@ -1497,6 +1498,7 @@
     private ImmutableMap<String, Attribute> aspectAttributes;
     private ImmutableBiMap<String, Class<? extends TransitiveInfoProvider>> skylarkProviderRegistry;
     private ImmutableList<AspectDescriptor> aspectDescriptors;
+    private ToolchainContext toolchainContext;
 
     Builder(
         AnalysisEnvironment env,
@@ -1533,7 +1535,8 @@
           configConditions,
           universalFragment,
           getRuleClassNameForLogging(),
-          aspectAttributes != null ? aspectAttributes : ImmutableMap.<String, Attribute>of());
+          aspectAttributes != null ? aspectAttributes : ImmutableMap.<String, Attribute>of(),
+          toolchainContext);
     }
 
     private void validateAttributes(AttributeMap attributes) {
@@ -1593,6 +1596,12 @@
       return this;
     }
 
+    /** Sets the {@link ToolchainContext} used to access toolchains used by this rule. */
+    Builder setToolchainContext(ToolchainContext toolchainContext) {
+      this.toolchainContext = toolchainContext;
+      return this;
+    }
+
     private boolean validateFilesetEntry(FilesetEntry filesetEntry, ConfiguredTarget src) {
       if (src.getProvider(FilesetProvider.class) != null) {
         return true;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java
index c4c9a9f..fc903dc 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.analysis;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.analysis.platform.ToolchainInfo;
 import com.google.devtools.build.lib.packages.ClassObjectConstructor;
@@ -23,15 +24,23 @@
 
 /** Contains toolchain-related information needed for a {@link RuleContext}. */
 public class ToolchainContext {
+  private final ImmutableList<ClassObjectConstructor.Key> requiredToolchains;
   private final ImmutableMap<ClassObjectConstructor.Key, ToolchainInfo> toolchains;
 
-  public ToolchainContext(@Nullable Map<ClassObjectConstructor.Key, ToolchainInfo> toolchains) {
+  public ToolchainContext(
+      ImmutableList<ClassObjectConstructor.Key> requiredToolchains,
+      @Nullable Map<ClassObjectConstructor.Key, ToolchainInfo> toolchains) {
+    this.requiredToolchains = requiredToolchains;
     this.toolchains =
         toolchains == null
             ? ImmutableMap.<ClassObjectConstructor.Key, ToolchainInfo>of()
             : ImmutableMap.copyOf(toolchains);
   }
 
+  public ImmutableList<ClassObjectConstructor.Key> getRequiredToolchains() {
+    return requiredToolchains;
+  }
+
   public SkylarkDict<ClassObjectConstructor.Key, ToolchainInfo> collectToolchains() {
     return SkylarkDict.<ClassObjectConstructor.Key, ToolchainInfo>copyOf(null, toolchains);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
index 1dbb131..860e3dff 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
@@ -28,9 +28,11 @@
 import com.google.devtools.build.lib.syntax.Type.LabelClass;
 import com.google.devtools.build.lib.syntax.Type.LabelVisitor;
 import com.google.devtools.build.lib.util.Preconditions;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
 
@@ -58,6 +60,7 @@
   private final RequiredProviders requiredProviders;
   private final RequiredProviders requiredProvidersForAspects;
   private final ImmutableMap<String, Attribute> attributes;
+  private final ImmutableList<ClassObjectConstructor.Key> requiredToolchains;
 
   /**
    * Which attributes aspect should propagate along:
@@ -74,13 +77,13 @@
     return advertisedProviders;
   }
 
-
   private AspectDefinition(
       AspectClass aspectClass,
       AdvertisedProviderSet advertisedProviders,
       RequiredProviders requiredProviders,
       RequiredProviders requiredAspectProviders,
       ImmutableMap<String, Attribute> attributes,
+      ImmutableList<ClassObjectConstructor.Key> requiredToolchains,
       @Nullable ImmutableSet<String> restrictToAttributes,
       @Nullable ConfigurationFragmentPolicy configurationFragmentPolicy,
       boolean applyToFiles) {
@@ -90,6 +93,7 @@
     this.requiredProvidersForAspects = requiredAspectProviders;
 
     this.attributes = attributes;
+    this.requiredToolchains = requiredToolchains;
     this.restrictToAttributes = restrictToAttributes;
     this.configurationFragmentPolicy = configurationFragmentPolicy;
     this.applyToFiles = applyToFiles;
@@ -112,6 +116,11 @@
     return attributes;
   }
 
+  /** Returns the required toolchains declared by this aspect. */
+  public ImmutableList<ClassObjectConstructor.Key> getRequiredToolchains() {
+    return requiredToolchains;
+  }
+
   /**
    * Returns {@link RequiredProviders} that a configured target must have so that
    * this aspect can be applied to it.
@@ -256,6 +265,7 @@
     private final ConfigurationFragmentPolicy.Builder configurationFragmentPolicy =
         new ConfigurationFragmentPolicy.Builder();
     private boolean applyToFiles = false;
+    private final List<ClassObjectConstructor.Key> requiredToolchains = new ArrayList<>();
 
     public Builder(AspectClass aspectClass) {
       this.aspectClass = aspectClass;
@@ -456,21 +466,25 @@
       return this;
     }
 
-
+    /** Adds the given toolchains as requirements for this aspect. */
+    public Builder addRequiredToolchains(List<ClassObjectConstructor.Key> requiredToolchains) {
+      this.requiredToolchains.addAll(requiredToolchains);
+      return this;
+    }
     /**
      * Builds the aspect definition.
      *
      * <p>The builder object is reusable afterwards.
      */
     public AspectDefinition build() {
-      return new AspectDefinition(aspectClass,
+      return new AspectDefinition(
+          aspectClass,
           advertisedProviders.build(),
           requiredProviders.build(),
           requiredAspectProviders.build(),
           ImmutableMap.copyOf(attributes),
-          propagateAlongAttributes == null
-              ? null
-              : ImmutableSet.copyOf(propagateAlongAttributes),
+          ImmutableList.copyOf(requiredToolchains),
+          propagateAlongAttributes == null ? null : ImmutableSet.copyOf(propagateAlongAttributes),
           configurationFragmentPolicy.build(),
           applyToFiles);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspect.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspect.java
index 58a5801..895afba 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspect.java
@@ -47,6 +47,8 @@
   private final ImmutableSet<String> paramAttributes;
   private final ImmutableSet<String> fragments;
   private final ImmutableSet<String> hostFragments;
+  private final ImmutableList<ClassObjectConstructor.Key> requiredToolchains;
+
   private final Environment funcallEnv;
   private SkylarkAspectClass aspectClass;
 
@@ -59,6 +61,7 @@
       ImmutableSet<String> paramAttributes,
       ImmutableSet<String> fragments,
       ImmutableSet<String> hostFragments,
+      ImmutableList<ClassObjectConstructor.Key> requiredToolchains,
       Environment funcallEnv) {
     this.implementation = implementation;
     this.attributeAspects = attributeAspects;
@@ -68,6 +71,7 @@
     this.paramAttributes = paramAttributes;
     this.fragments = fragments;
     this.hostFragments = hostFragments;
+    this.requiredToolchains = requiredToolchains;
     this.funcallEnv = funcallEnv;
   }
 
@@ -153,6 +157,7 @@
     builder.advertiseProvider(advertisedSkylarkProviders.build());
     builder.requiresConfigurationFragmentsBySkylarkModuleName(fragments);
     builder.requiresHostConfigurationFragmentsBySkylarkModuleName(hostFragments);
+    builder.addRequiredToolchains(requiredToolchains);
     return builder.build();
   }
 
@@ -195,4 +200,8 @@
       }
     };
   }
+
+  public ImmutableList<ClassObjectConstructor.Key> getRequiredToolchains() {
+    return requiredToolchains;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
index 083cb92..f11b74a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
@@ -501,71 +501,97 @@
     }
   }
 
-
-  @SkylarkSignature(name = "aspect", doc =
-      "Creates a new aspect. The result of this function must be stored in a global value. "
-          + "Please see the <a href=\"../aspects.md\">introduction to Aspects</a> for more "
-          + "details.",
-      returnType = SkylarkAspect.class,
-      parameters = {
-          @Param(name = "implementation", type = BaseFunction.class,
-              doc = "the function implementing this aspect. Must have two parameters: "
-                  + "<a href=\"Target.html\">Target</a> (the target to which the aspect is "
-                  + "applied) and <a href=\"ctx.html\">ctx</a>. Attributes of the target are "
-                  + "available via ctx.rule field. The function is called during the analysis "
-                  + "phase for each application of an aspect to a target."
-          ),
-          @Param(name = "attr_aspects", type = SkylarkList.class, generic1 = String.class,
-              defaultValue = "[]",
-              doc = "List of attribute names.  The aspect propagates along dependencies specified "
-                  + "by attributes of a target with this name. The list can also contain a single "
-                  + "string '*': in that case aspect propagates along all dependencies of a target."
-          ),
-          @Param(name = "attrs", type = SkylarkDict.class, noneable = true, defaultValue = "None",
-              doc = "dictionary to declare all the attributes of the aspect.  "
-                  + "It maps from an attribute name to an attribute object "
-                  + "(see <a href=\"attr.html\">attr</a> module). "
-                  + "Aspect attributes are available to implementation function as fields of ctx "
-                  + "parameter. Implicit attributes starting with <code>_</code> must have default "
-                  + "values, and have type <code>label</code> or <code>label_list</code>. "
-                  + "Explicit attributes must have type <code>string</code>, and must use the "
-                  + "<code>values</code> restriction. If explicit attributes are present, the "
-                  + "aspect can only be used with rules that have attributes of the same name and "
-                  + "type, with valid values."
-          ),
-          @Param(name = "required_aspect_providers",
-              type = SkylarkList.class,
-              defaultValue = "[]",
-              // todo(dslomov): Document once it works.
-              doc = "<not available>"
-          ),
-          @Param(name = "provides",
-              type = SkylarkList.class,
-              defaultValue = "[]",
-              // todo(dslomov): Document once it works.
-              doc = "<not available>"
-          ),
-          @Param(
-              name = "fragments",
-              type = SkylarkList.class,
-              generic1 = String.class,
-              defaultValue = "[]",
-              doc =
-                  "List of names of configuration fragments that the aspect requires "
-                      + "in target configuration."
-          ),
-          @Param(
-              name = "host_fragments",
-              type = SkylarkList.class,
-              generic1 = String.class,
-              defaultValue = "[]",
-              doc =
-                  "List of names of configuration fragments that the aspect requires "
-                      + "in host configuration."
-          )
-      },
-      useEnvironment = true,
-      useAst = true
+  @SkylarkSignature(
+    name = "aspect",
+    doc =
+        "Creates a new aspect. The result of this function must be stored in a global value. "
+            + "Please see the <a href=\"../aspects.md\">introduction to Aspects</a> for more "
+            + "details.",
+    returnType = SkylarkAspect.class,
+    parameters = {
+      @Param(
+        name = "implementation",
+        type = BaseFunction.class,
+        doc =
+            "the function implementing this aspect. Must have two parameters: "
+                + "<a href=\"Target.html\">Target</a> (the target to which the aspect is "
+                + "applied) and <a href=\"ctx.html\">ctx</a>. Attributes of the target are "
+                + "available via ctx.rule field. The function is called during the analysis "
+                + "phase for each application of an aspect to a target."
+      ),
+      @Param(
+        name = "attr_aspects",
+        type = SkylarkList.class,
+        generic1 = String.class,
+        defaultValue = "[]",
+        doc =
+            "List of attribute names.  The aspect propagates along dependencies specified "
+                + "by attributes of a target with this name. The list can also contain a single "
+                + "string '*': in that case aspect propagates along all dependencies of a target."
+      ),
+      @Param(
+        name = "attrs",
+        type = SkylarkDict.class,
+        noneable = true,
+        defaultValue = "None",
+        doc =
+            "dictionary to declare all the attributes of the aspect.  "
+                + "It maps from an attribute name to an attribute object "
+                + "(see <a href=\"attr.html\">attr</a> module). "
+                + "Aspect attributes are available to implementation function as fields of ctx "
+                + "parameter. Implicit attributes starting with <code>_</code> must have default "
+                + "values, and have type <code>label</code> or <code>label_list</code>. "
+                + "Explicit attributes must have type <code>string</code>, and must use the "
+                + "<code>values</code> restriction. If explicit attributes are present, the "
+                + "aspect can only be used with rules that have attributes of the same name and "
+                + "type, with valid values."
+      ),
+      @Param(
+        name = "required_aspect_providers",
+        type = SkylarkList.class,
+        defaultValue = "[]",
+        // todo(dslomov): Document once it works.
+        doc = "<not available>"
+      ),
+      @Param(
+        name = "provides",
+        type = SkylarkList.class,
+        defaultValue = "[]",
+        // todo(dslomov): Document once it works.
+        doc = "<not available>"
+      ),
+      @Param(
+        name = "fragments",
+        type = SkylarkList.class,
+        generic1 = String.class,
+        defaultValue = "[]",
+        doc =
+            "List of names of configuration fragments that the aspect requires "
+                + "in target configuration."
+      ),
+      @Param(
+        name = "host_fragments",
+        type = SkylarkList.class,
+        generic1 = String.class,
+        defaultValue = "[]",
+        doc =
+            "List of names of configuration fragments that the aspect requires "
+                + "in host configuration."
+      ),
+      @Param(
+        name = "toolchains",
+        type = SkylarkList.class,
+        generic1 = ToolchainConstructor.class,
+        defaultValue = "[]",
+        doc =
+            "<i>(Experimental)</i><br/><br/>"
+                + "If set, the set of toolchains this rule requires. Toolchains will be "
+                + "found by checking the current platform, and provided to the rule "
+                + "implementation via <code>ctx.toolchain</code>."
+      )
+    },
+    useEnvironment = true,
+    useAst = true
   )
   private static final BuiltinFunction aspect =
       new BuiltinFunction("aspect") {
@@ -577,6 +603,7 @@
             SkylarkList providesArg,
             SkylarkList fragments,
             SkylarkList hostFragments,
+            SkylarkList<ToolchainConstructor> toolchains,
             FuncallExpression ast,
             Environment funcallEnv)
             throws EvalException {
@@ -586,14 +613,12 @@
 
             if (attrName.equals("*") && attributeAspects.size() != 1) {
               throw new EvalException(
-                  ast.getLocation(),
-                  "'*' must be the only string in 'attr_aspects' list"
-              );
+                  ast.getLocation(), "'*' must be the only string in 'attr_aspects' list");
             }
 
             if (!attrName.startsWith("_")) {
               attrAspects.add(attrName);
-            } else  {
+            } else {
               // Implicit attribute names mean either implicit or late-bound attributes
               // (``$attr`` or ``:attr``). Depend on both.
               attrAspects.add(
@@ -613,7 +638,7 @@
             Attribute attribute = nameDescriptorPair.second.build(nameDescriptorPair.first);
             if (attribute.getType() == Type.STRING
                 && ((String) attribute.getDefaultValue(null)).isEmpty()) {
-              hasDefault = false;  // isValueSet() is always true for attr.string.
+              hasDefault = false; // isValueSet() is always true for attr.string.
             }
             if (!Attribute.isImplicit(nativeName)) {
               if (!attribute.checkAllowedValues() || attribute.getType() != Type.STRING) {
@@ -634,11 +659,10 @@
                       ast.getLocation(),
                       String.format(
                           "Aspect parameter attribute '%s' has a bad default value: %s",
-                          nativeName,
-                          allowed.getErrorReason(defaultVal)));
+                          nativeName, allowed.getErrorReason(defaultVal)));
                 }
               }
-            } else if (!hasDefault) {  // Implicit attribute
+            } else if (!hasDefault) { // Implicit attribute
               String skylarkName = "_" + nativeName.substring(1);
               throw new EvalException(
                   ast.getLocation(),
@@ -649,29 +673,38 @@
 
           for (Object o : providesArg) {
             if (!SkylarkAttr.isProvider(o)) {
-              throw new EvalException(ast.getLocation(),
-                  String.format("Illegal argument: element in 'provides' is of unexpected type. "
-                      + "Should be list of providers, but got %s. ",
+              throw new EvalException(
+                  ast.getLocation(),
+                  String.format(
+                      "Illegal argument: element in 'provides' is of unexpected type. "
+                          + "Should be list of providers, but got %s. ",
                       EvalUtils.getDataTypeName(o, true)));
             }
           }
 
+          // Collect the required toolchain keys.
+          ImmutableList.Builder<ClassObjectConstructor.Key> requiredToolchains =
+              new ImmutableList.Builder<>();
+          for (ToolchainConstructor toolchain :
+              toolchains.getContents(ToolchainConstructor.class, "toolchains")) {
+            requiredToolchains.add(toolchain.getKey());
+          }
+
           return new SkylarkAspect(
               implementation,
               attrAspects.build(),
               attributes.build(),
-              SkylarkAttr.buildProviderPredicate(requiredAspectProvidersArg,
-                  "required_aspect_providers", ast.getLocation()
-              ),
+              SkylarkAttr.buildProviderPredicate(
+                  requiredAspectProvidersArg, "required_aspect_providers", ast.getLocation()),
               SkylarkAttr.getSkylarkProviderIdentifiers(providesArg, ast.getLocation()),
               requiredParams.build(),
               ImmutableSet.copyOf(fragments.getContents(String.class, "fragments")),
               ImmutableSet.copyOf(hostFragments.getContents(String.class, "host_fragments")),
+              requiredToolchains.build(),
               funcallEnv);
         }
       };
 
-
   /** The implementation for the magic function "rule" that creates Skylark rule classes */
   public static final class RuleFunction extends BaseFunction implements SkylarkExportable {
     private RuleClass.Builder builder;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
index 90c121b..acd7e31 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
@@ -262,7 +262,6 @@
               this, attributes, ruleContext, attributeValueExtractorForRule(ruleContext));
       this.splitAttributes = buildSplitAttributeInfo(attributes, ruleContext);
       this.ruleAttributesCollection = null;
-      toolchains = ruleContext.getToolchainContext().collectToolchains();
     } else { // ASPECT
       this.artifactsLabelMap = ImmutableMap.of();
       this.outputsObject = null;
@@ -279,11 +278,13 @@
               ruleContext.getRule().getAttributes(),
               ruleContext,
               attributeValueExtractorForRule(ruleContext));
-      // TODO(katre): Collect toolchains for aspects.
-      toolchains = null;
     }
 
     makeVariables = ruleContext.getConfigurationMakeVariableContext().collectMakeVariables();
+    toolchains =
+        ruleContext.getToolchainContext() == null
+            ? SkylarkDict.<ClassObjectConstructor.Key, ToolchainInfo>of(null)
+            : ruleContext.getToolchainContext().collectToolchains();
   }
 
   /**