Implement Skylark aspects originating from Skylark rules.

--
MOS_MIGRATED_REVID=108777120
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
index f445eb66..58b9386 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
@@ -14,6 +14,8 @@
 
 package com.google.devtools.build.lib.rules;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.packages.Attribute;
@@ -21,6 +23,7 @@
 import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
 import com.google.devtools.build.lib.packages.Attribute.SkylarkLateBound;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.SkylarkAspect;
 import com.google.devtools.build.lib.syntax.BuiltinFunction;
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.EvalException;
@@ -68,6 +71,10 @@
       "which rule targets (name of the classes) are allowed. This is deprecated (kept only for "
           + "compatiblity), use providers instead.";
 
+  private static final String ASPECTS_ARG = "aspects";
+  private static final String ASPECT_ARG_DOC =
+      "aspects that should be applied to dependencies specified by this attribute";
+
   private static final String CONFIGURATION_ARG = "cfg";
   private static final String CONFIGURATION_DOC =
       "configuration of the attribute. " + "For example, use DATA_CFG or HOST_CFG.";
@@ -184,7 +191,7 @@
     return builder;
   }
 
-  private static Descriptor createAttribute(
+  private static Descriptor createAttrDescriptor(
       Map<String, Object> kwargs, Type<?> type, FuncallExpression ast, Environment env)
       throws EvalException {
     try {
@@ -230,7 +237,7 @@
             throws EvalException {
           // TODO(bazel-team): Replace literal strings with constants.
           env.checkLoadingPhase("attr.int", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(
                   DEFAULT_ARG, defaultInt, MANDATORY_ARG, mandatory, VALUES_ARG, values),
               Type.INTEGER,
@@ -274,7 +281,7 @@
             Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.string", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(
                   DEFAULT_ARG, defaultString, MANDATORY_ARG, mandatory, VALUES_ARG, values),
               Type.STRING,
@@ -362,7 +369,7 @@
             Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.label", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(
                   DEFAULT_ARG,
                   defaultO,
@@ -417,7 +424,7 @@
             Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.string_list", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(
                   DEFAULT_ARG, defaultList, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty),
               Type.STRING_LIST,
@@ -457,7 +464,7 @@
             Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.int_list", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(
                   DEFAULT_ARG, defaultList, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty),
               Type.INTEGER_LIST,
@@ -522,6 +529,13 @@
         noneable = true,
         defaultValue = "None",
         doc = CONFIGURATION_DOC
+      ),
+      @Param(
+        name = ASPECTS_ARG,
+        type = SkylarkList.class,
+        generic1 = SkylarkAspect.class,
+        defaultValue = "[]",
+        doc = ASPECT_ARG_DOC
       )
     },
     useAst = true,
@@ -538,31 +552,48 @@
             Boolean mandatory,
             Boolean nonEmpty,
             Object cfg,
+            SkylarkList aspects,
             FuncallExpression ast,
             Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.label_list", ast.getLocation());
-          return createAttribute(
-              EvalUtils.optionMap(
-                  DEFAULT_ARG,
-                  defaultList,
-                  ALLOW_FILES_ARG,
-                  allowFiles,
-                  ALLOW_RULES_ARG,
-                  allowRules,
-                  PROVIDERS_ARG,
-                  providers,
-                  FLAGS_ARG,
-                  flags,
-                  MANDATORY_ARG,
-                  mandatory,
-                  NON_EMPTY_ARG,
-                  nonEmpty,
-                  CONFIGURATION_ARG,
-                  cfg),
-              BuildType.LABEL_LIST,
-              ast,
-              env);
+          ImmutableMap<String, Object> kwargs = EvalUtils.optionMap(
+              DEFAULT_ARG, defaultList,
+              ALLOW_FILES_ARG,
+              allowFiles,
+              ALLOW_RULES_ARG,
+              allowRules,
+              PROVIDERS_ARG,
+              providers,
+              FLAGS_ARG,
+              flags,
+              MANDATORY_ARG,
+              mandatory,
+              NON_EMPTY_ARG,
+              nonEmpty,
+              CONFIGURATION_ARG,
+              cfg);
+          try {
+            Attribute.Builder<?> attribute = createAttribute(
+                BuildType.LABEL_LIST, kwargs, ast, env);
+
+            ImmutableList<SkylarkAspect> skylarkAspects = getSkylarkAspects(aspects);
+            return new Descriptor(attribute, skylarkAspects);
+          } catch (ConversionException e) {
+            throw new EvalException(ast.getLocation(), e.getMessage());
+          }
+        }
+
+        protected ImmutableList<SkylarkAspect> getSkylarkAspects(SkylarkList aspects)
+            throws ConversionException {
+          ImmutableList.Builder<SkylarkAspect> aspectBuilder = ImmutableList.builder();
+          for (Object aspect : aspects) {
+            if (!(aspect instanceof SkylarkAspect)) {
+              throw new ConversionException("Expected a list of aspects for 'aspects'");
+            }
+            aspectBuilder.add((SkylarkAspect) aspect);
+          }
+          return aspectBuilder.build();
         }
       };
 
@@ -585,7 +616,7 @@
             Boolean defaultO, Boolean mandatory, FuncallExpression ast, Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.bool", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory),
               Type.BOOLEAN,
               ast,
@@ -621,7 +652,7 @@
             Object defaultO, Boolean mandatory, FuncallExpression ast, Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.output", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory),
               BuildType.OUTPUT,
               ast,
@@ -662,7 +693,7 @@
             Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.output_list", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(
                   DEFAULT_ARG, defaultList, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty),
               BuildType.OUTPUT_LIST,
@@ -698,7 +729,7 @@
             Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.string_dict", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(
                   DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty),
               Type.STRING_DICT,
@@ -734,7 +765,7 @@
             Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.string_list_dict", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(
                   DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty),
               Type.STRING_LIST_DICT,
@@ -764,7 +795,7 @@
             Object defaultO, Boolean mandatory, FuncallExpression ast, Environment env)
             throws EvalException {
           env.checkLoadingPhase("attr.license", ast.getLocation());
-          return createAttribute(
+          return createAttrDescriptor(
               EvalUtils.optionMap(DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory),
               BuildType.LICENSE,
               ast,
@@ -777,14 +808,24 @@
    */
   public static final class Descriptor {
     private final Attribute.Builder<?> attributeBuilder;
+    private final ImmutableList<SkylarkAspect> aspects;
 
     public Descriptor(Attribute.Builder<?> attributeBuilder) {
+      this(attributeBuilder, ImmutableList.<SkylarkAspect>of());
+    }
+
+    public Descriptor(Attribute.Builder<?> attributeBuilder, ImmutableList<SkylarkAspect> aspects) {
       this.attributeBuilder = attributeBuilder;
+      this.aspects = aspects;
     }
 
     public Attribute.Builder<?> getAttributeBuilder() {
       return attributeBuilder;
     }
+
+    public ImmutableList<SkylarkAspect> getAspects() {
+      return aspects;
+    }
   }
 
   static {