Support skylark aspects in serialization/deserialization.

--
MOS_MIGRATED_REVID=108964575
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 de9c5c3..70ca95a 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
@@ -112,9 +112,6 @@
 
   /**
    * Returns the attribute -> set of required aspects map.
-   *
-   * <p>Note that the map actually contains {@link AspectFactory}
-   * instances, except that we cannot reference that class here.
    */
   public ImmutableMultimap<String, AspectClass> getAttributeAspects() {
     return attributeAspects;
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageDeserializer.java b/src/main/java/com/google/devtools/build/lib/packages/PackageDeserializer.java
index bb6ec72..f393a78 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageDeserializer.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageDeserializer.java
@@ -15,6 +15,7 @@
 package com.google.devtools.build.lib.packages;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
@@ -43,6 +44,7 @@
 import com.google.devtools.build.lib.syntax.GlobCriteria;
 import com.google.devtools.build.lib.syntax.GlobList;
 import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.syntax.Type.ConversionException;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.protobuf.CodedInputStream;
@@ -76,7 +78,8 @@
     Path getPath(String buildFilePath);
 
     /** Returns a {@link RuleClass} object for the serialized rule. */
-    RuleClass getRuleClass(Build.Rule rulePb, Location ruleLocation);
+    RuleClass getRuleClass(Build.Rule rulePb, Location ruleLocation)
+        throws PackageDeserializationException;
 
     /** Description of what rule attributes of each rule should be deserialized. */
     AttributesToDeserialize attributesToDeserialize();
@@ -764,6 +767,61 @@
     return rule;
   }
 
+  /**
+   * Implemetation of a SkylarkAspectClass deserialized with a package.
+   */
+  public static class DeserializedSkylarkAspectClass extends SkylarkAspectClass {
+
+    private final Label extenesionFileLabel;
+    private final String exportedName;
+    private final AspectDefinition definition;
+
+    private DeserializedSkylarkAspectClass(Build.SkylarkAspect aspect)
+        throws PackageDeserializationException {
+      this.extenesionFileLabel = deserializeLabel(aspect.getExtensionFileLabel());
+      this.exportedName = aspect.getExportedName();
+      AspectDefinition.Builder builder = new AspectDefinition.Builder(getName());
+
+      for (Build.Attribute attributePb : aspect.getAttributeList()) {
+        Type<?> type =
+            ProtoUtils.getTypeFromDiscriminator(
+                attributePb.getType(), Optional.<Boolean>absent(), "", attributePb.getName());
+        ParsedAttributeValue parsedAttributeValue = deserializeAttribute(type, attributePb);
+        Attribute.Builder<?> attribute = Attribute.attr(attributePb.getName(), type);
+        try {
+          attribute.defaultValue(parsedAttributeValue.value);
+        } catch (ConversionException e) {
+          throw new PackageDeserializationException(
+              "Wrong type of a value for attribute " + attributePb.getName(), e);
+        }
+        builder.add(attribute);
+      }
+
+      this.definition = builder.build();
+    }
+
+    @Override
+    public Label getExtensionLabel() {
+      return extenesionFileLabel;
+    }
+
+    @Override
+    public String getExportedName() {
+      return exportedName;
+    }
+
+    @Override
+    public AspectDefinition getDefinition(AspectParameters aspectParameters) {
+      return definition;
+    }
+  }
+
+  public SkylarkAspectClass createDeserializedSkylarkAspectClass(Build.SkylarkAspect aspect)
+      throws PackageDeserializationException {
+    return new DeserializedSkylarkAspectClass(aspect);
+  }
+
+
   private static class ParsedAttributeValue {
     private final boolean explicitlySpecified;
     private final Object value;
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java b/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java
index f07e54c..5fd5188 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.packages;
 
+import com.google.common.base.Verify;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import com.google.devtools.build.lib.cmdline.Label;
@@ -21,6 +22,8 @@
 import com.google.devtools.build.lib.packages.License.DistributionType;
 import com.google.devtools.build.lib.packages.MakeEnvironment.Binding;
 import com.google.devtools.build.lib.query2.proto.proto2api.Build;
+import com.google.devtools.build.lib.query2.proto.proto2api.Build.AttributeAspect;
+import com.google.devtools.build.lib.query2.proto.proto2api.Build.SkylarkAspect;
 import com.google.protobuf.CodedOutputStream;
 
 import java.io.IOException;
@@ -28,6 +31,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 /** Functionality to serialize loaded packages. */
@@ -171,6 +175,26 @@
         .build();
   }
 
+  public Build.SkylarkAspect serializeSkylarkAspect(Rule rule, Aspect aspect) {
+    SkylarkAspectClass aspectClass = (SkylarkAspectClass) aspect.getAspectClass();
+
+    SkylarkAspect.Builder builder = SkylarkAspect.newBuilder()
+        .setExtensionFileLabel(aspectClass.getExtensionLabel().toString())
+        .setExportedName(aspectClass.getExportedName());
+
+    AspectDefinition definition = aspect.getDefinition();
+    for (Entry<String, Attribute> entry : definition.getAttributes().entrySet()) {
+      Attribute attribute = entry.getValue();
+      Object defaultValue = attribute.getDefaultValue(rule);
+      Verify.verify(defaultValue != null, "Aspect attributes must have default values.");
+      Build.Attribute attributePb =
+          AttributeSerializer.getAttributeProto(
+              attribute, ImmutableList.of(defaultValue), true, false);
+      builder.addAttribute(attributePb);
+    }
+    return builder.build();
+  }
+
   private static List<Build.MakeVar> serializeMakeEnvironment(MakeEnvironment makeEnv) {
     List<Build.MakeVar> result = new ArrayList<>();
 
@@ -256,7 +280,7 @@
         .build());
   }
 
-  private static void maybeSerializeAdditionalDataForRule(Rule rule, Build.Rule.Builder builder) {
+  private void maybeSerializeAdditionalDataForRule(Rule rule, Build.Rule.Builder builder) {
     if (rule.getRuleClassObject().isSkylark()) {
       builder.setIsSkylark(true);
       // We explicitly serialize the implicit output files for this rule so that we can recreate
@@ -281,6 +305,24 @@
           builder.addRuleOutput(label.getName());
         }
       }
+
+      // Serialize aspects.
+      List<Attribute> attributes = rule.getRuleClassObject().getAttributes();
+      for (Attribute attribute : attributes) {
+        ImmutableList<Aspect> aspects = attribute.getAspects(rule);
+        for (Aspect aspect : aspects) {
+          if (aspect.getAspectClass() instanceof NativeAspectClass<?>) {
+            continue;
+          }
+
+          SkylarkAspect skylarkAspect = serializeSkylarkAspect(rule, aspect);
+          builder.addSkylarkAttributeAspects(
+              AttributeAspect.newBuilder()
+                  .setAttributeName(attribute.getName())
+                  .setAspect(skylarkAspect)
+                  .build());
+        }
+      }
     }
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspectClass.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspectClass.java
new file mode 100644
index 0000000..fa4fc5a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspectClass.java
@@ -0,0 +1,55 @@
+// Copyright 2015 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.devtools.build.lib.cmdline.Label;
+
+import java.util.Objects;
+
+/**
+ * {@link AspectClass} for aspects defined in Skylark.
+ */
+public abstract class SkylarkAspectClass implements AspectClass {
+
+  public abstract Label getExtensionLabel();
+
+  public abstract String getExportedName();
+
+  @Override
+  public final String getName() {
+    return getExtensionLabel() + "%" + getExportedName();
+  }
+
+  @Override
+  public final boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+
+    if (!(o instanceof SkylarkAspectClass)) {
+      return false;
+    }
+
+    SkylarkAspectClass that = (SkylarkAspectClass) o;
+
+    return getExtensionLabel().equals(that.getExtensionLabel())
+        && getExportedName().equals(that.getExportedName());
+  }
+
+  @Override
+  public final int hashCode() {
+    return Objects.hash(getExtensionLabel(), getExportedName());
+  }
+}
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 7d32b77..2eb5c30 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
@@ -43,7 +43,6 @@
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.events.Location;
-import com.google.devtools.build.lib.packages.AspectClass;
 import com.google.devtools.build.lib.packages.AspectDefinition;
 import com.google.devtools.build.lib.packages.AspectParameters;
 import com.google.devtools.build.lib.packages.Attribute;
@@ -61,6 +60,7 @@
 import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
 import com.google.devtools.build.lib.packages.RuleFactory;
 import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException;
+import com.google.devtools.build.lib.packages.SkylarkAspectClass;
 import com.google.devtools.build.lib.packages.TargetUtils;
 import com.google.devtools.build.lib.packages.TestSize;
 import com.google.devtools.build.lib.syntax.BaseFunction;
@@ -88,7 +88,6 @@
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 
@@ -456,7 +455,7 @@
             throw new EvalException(definitionLocation,
                 "All aspects applied to rule dependencies must be top-level values");
           }
-          attributeBuilder.aspect(new SkylarkAspectClass(skylarkAspect));
+          attributeBuilder.aspect(skylarkAspect.getAspectClass());
         }
 
         addAttribute(definitionLocation, builder,
@@ -632,7 +631,7 @@
   /**
    * A Skylark value that is a result of 'aspect(..)' function call.
    */
-  public static class SkylarkAspect implements SkylarkValue {
+  public static final class SkylarkAspect implements SkylarkValue {
     private final BaseFunction implementation;
     private final ImmutableList<String> attributeAspects;
     private final ImmutableList<Label> extraDeps;
@@ -677,7 +676,12 @@
     }
 
     public String getName() {
-      return exported != null ? exported.toString() : "<skylark aspect>";
+      return getAspectClass().getName();
+    }
+
+    public SkylarkAspectClass getAspectClass() {
+      Preconditions.checkState(isExported());
+      return new SkylarkAspectClassImpl(this);
     }
 
     void export(Label extensionLabel, String name) {
@@ -719,27 +723,23 @@
    * Implementation of an aspect class defined in Skylark.
    */
   @Immutable
-  public static final class SkylarkAspectClass implements AspectClass {
+  private static final class SkylarkAspectClassImpl extends SkylarkAspectClass {
     private final AspectDefinition aspectDefinition;
     private final Label extensionLabel;
     private final String exportedName;
 
-    public SkylarkAspectClass(SkylarkAspect skylarkAspect) {
+    public SkylarkAspectClassImpl(SkylarkAspect skylarkAspect) {
       Preconditions.checkArgument(skylarkAspect.isExported(), "Skylark aspects must be exported");
-      AspectDefinition.Builder builder = new AspectDefinition.Builder(skylarkAspect.getName());
+      this.extensionLabel = skylarkAspect.getExtensionLabel();
+      this.exportedName = skylarkAspect.getExportedName();
+
+      AspectDefinition.Builder builder = new AspectDefinition.Builder(getName());
       for (String attributeAspect : skylarkAspect.getAttributeAspects()) {
         builder.attributeAspect(attributeAspect, this);
       }
       builder.add(attr("$extra_deps", LABEL_LIST).value(skylarkAspect.getExtraDeps()));
       this.aspectDefinition = builder.build();
 
-      this.extensionLabel = skylarkAspect.getExtensionLabel();
-      this.exportedName = skylarkAspect.getExportedName();
-    }
-
-    @Override
-    public String getName() {
-      return aspectDefinition.getName();
     }
 
     @Override
@@ -755,24 +755,5 @@
       return exportedName;
     }
 
-    @Override
-    public int hashCode() {
-      return Objects.hash(extensionLabel, exportedName);
-    }
-
-    @Override
-    public boolean equals(Object other) {
-      if (this == other) {
-        return true;
-      }
-      if (!(other instanceof SkylarkAspectClass)) {
-        return false;
-      }
-
-      SkylarkAspectClass that = (SkylarkAspectClass) other;
-      return Objects.equals(this.extensionLabel, that.extensionLabel)
-          && Objects.equals(this.exportedName, that.exportedName);
-    }
-
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
index 97f6fdd..8b433883 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
@@ -35,9 +35,9 @@
 import com.google.devtools.build.lib.packages.Package;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.RuleClassProvider;
+import com.google.devtools.build.lib.packages.SkylarkAspectClass;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.SkylarkAspect;
-import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.SkylarkAspectClass;
 import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.DependencyEvaluationException;
 import com.google.devtools.build.lib.skyframe.SkyframeExecutor.BuildViewProvider;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelSkylarkAspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelSkylarkAspectFunction.java
index cecc689..5cf7d97 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelSkylarkAspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelSkylarkAspectFunction.java
@@ -19,7 +19,6 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.packages.AspectParameters;
 import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.SkylarkAspect;
-import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.SkylarkAspectClass;
 import com.google.devtools.build.lib.skyframe.AspectValue.SkylarkAspectLoadingKey;
 import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction.SkylarkImportFailedException;
 import com.google.devtools.build.lib.syntax.Type.ConversionException;
@@ -74,7 +73,7 @@
         AspectValue.key(
             aspectLoadingKey.getTargetLabel(),
             aspectLoadingKey.getTargetConfiguration(),
-            new SkylarkAspectClass(skylarkAspect),
+            skylarkAspect.getAspectClass(),
             AspectParameters.EMPTY);
 
     return env.getValue(aspectKey);
diff --git a/src/main/protobuf/build.proto b/src/main/protobuf/build.proto
index a3f4c19..7c2cbd3 100644
--- a/src/main/protobuf/build.proto
+++ b/src/main/protobuf/build.proto
@@ -242,6 +242,22 @@
 
   // If this rule is of a skylark-defined RuleClass.
   optional bool is_skylark = 10;
+
+  // List of Skylark aspects that this rule applies.
+  repeated AttributeAspect skylark_attribute_aspects = 11;
+}
+
+// A pairing of attribute name and a Skylark aspect that is applied to that attribute.
+message AttributeAspect {
+  required string attribute_name = 1;
+  required SkylarkAspect aspect = 2;
+}
+
+// Aspect defined in Skylark.
+message SkylarkAspect {
+  required string extension_file_label = 1;
+  required string exported_name = 2;
+  repeated Attribute attribute = 3;
 }
 
 // Summary of all transitive dependencies of 'rule,' where each dependent
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java
index b3b6bc9..9c4b29b 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java
@@ -145,11 +145,12 @@
   public void testAspectsFromSkylarkRules() throws Exception {
     scratch.file(
         "test/aspect.bzl",
-        "def _impl(target, ctx):",
+        "def _aspect_impl(target, ctx):",
         "   s = set([target.label])",
         "   for i in ctx.attr.deps:",
         "       s += i.target_labels",
         "   return struct(target_labels = s)",
+        "",
         "def _rule_impl(ctx):",
         "   s = set([])",
         "   for i in ctx.attr.attr:",
@@ -157,7 +158,7 @@
         "   return struct(rule_deps = s)",
         "",
         "MyAspect = aspect(",
-        "   implementation=_impl,",
+        "   implementation=_aspect_impl,",
         "   attr_aspects=['deps'],",
         ")",
         "my_rule = rule(",