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(",