aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2017-12-20 04:34:12 +0100
committerGravatar Laszlo Csomor <laszlocsomor@google.com>2017-12-20 13:42:22 +0100
commit87dd180cbb10be3cafbdad27fadc80a8502503e8 (patch)
tree30018ac824a582238b7ee1f54c3d4a20db8b3d2b /src/main/java
parent5c16ab2378befcd8000bf4f95da596ada04ae8c6 (diff)
@AutoCodec features.
* Adds PUBLIC_FIELDS strategy, which can be used to generate codecs for FragmentOptions. * Support for boolean, enum, Void, String and List types. PiperOrigin-RevId: 179636138
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java33
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java322
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/BUILD7
3 files changed, 244 insertions, 118 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java
index 17e3224fbf..6652972035 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java
@@ -18,20 +18,18 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
- * Specifies that AutoCodec should generate a codec implementation for the annotated abstract class.
+ * Specifies that AutoCodec should generate a codec implementation for the annotated class.
*
* <p>Example:
*
* <pre>{@code
* @AutoCodec
- * abstract class Codec implements ObjectCodec<Target> {
- * static Codec create() {
- * return new AutoCodec_Target();
- * }
+ * class Target {
+ * public static final ObjectCodec<Target> CODEC = new Target_AutoCodec();
* }
* }</pre>
*
- * The {@code AutoCodec_} prefix is added to the {@Target} to obtain the generated class name.
+ * The {@code _AutoCodec} suffix is added to the {@code Target} to obtain the generated class name.
*/
@Target(ElementType.TYPE)
public @interface AutoCodec {
@@ -42,13 +40,28 @@ public @interface AutoCodec {
*/
public static enum Strategy {
/**
- * Uses the constructor to infer serialization code.
+ * Uses the first constructor of the class to synthesize a codec.
*
- * <p>Each constructor parameter is expected to have a corresponding getter. These pairs are
- * used for serialization and deserialization.
+ * <p>This strategy depends on
+ *
+ * <ul>
+ * <li>the first class constructor taking all serialized fields as parameters
+ * <li>and each serialized field having a corresponding getter.
+ * </ul>
+ *
+ * For example, a constructor having parameter, {@code target}, should having a matching getter,
+ * {@code getTarget()}.
+ *
+ * <p>The first constructor is the first ocurring in the source code.
*/
CONSTRUCTOR,
- // TODO(shahan): Add a strategy that serializes from public members.
+ /**
+ * Uses the public fields to infer serialization code.
+ *
+ * <p>Serializes each public field. Calls the no-arg constructor of the class to instantiate an
+ * instance for deserialization.
+ */
+ PUBLIC_FIELDS,
}
Strategy strategy() default Strategy.CONSTRUCTOR;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java
index ed54d58508..7dddb37a4d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java
@@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
+import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import com.squareup.javapoet.ClassName;
@@ -28,9 +29,11 @@ import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
+import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
@@ -38,11 +41,13 @@ import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;
@@ -87,69 +92,82 @@ public class AutoCodecProcessor extends AbstractProcessor {
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(AutoCodecUtil.ANNOTATION)) {
AutoCodec annotation = element.getAnnotation(AutoCodecUtil.ANNOTATION);
+ TypeElement encodedType = (TypeElement) element;
+ TypeSpec.Builder codecClassBuilder = initializeCodecClassBuilder(encodedType);
+ codecClassBuilder.addMethod(buildGetEncodedClassMethod(encodedType));
switch (annotation.strategy()) {
case CONSTRUCTOR:
- buildCodecUsingConstructor((TypeElement) element);
+ buildClassWithConstructorStrategy(codecClassBuilder, encodedType);
+ break;
+ case PUBLIC_FIELDS:
+ buildClassWithPublicFieldsStrategy(codecClassBuilder, encodedType);
break;
default:
throw new IllegalArgumentException("Unknown strategy: " + annotation.strategy());
}
+ String packageName =
+ env.getElementUtils().getPackageOf(encodedType).getQualifiedName().toString();
+ try {
+ JavaFile file = JavaFile.builder(packageName, codecClassBuilder.build()).build();
+ file.writeTo(env.getFiler());
+ if (env.getOptions().containsKey(PRINT_GENERATED_OPTION)) {
+ note("AutoCodec generated codec for " + encodedType + ":\n" + file);
+ }
+ } catch (IOException e) {
+ env.getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR, "Failed to generate output file: " + e.getMessage());
+ }
}
return true;
}
- /**
- * Uses the first constructor of the class to synthesize a codec.
- *
- * <p>This strategy depends on
- *
- * <ul>
- * <li>the class constructor taking all serialized fields as parameters
- * <li>and each serialized field having a corresponding getter.
- * </ul>
- *
- * For example, a constructor having parameter, {@code target}, should having a matching getter,
- * {@code getTarget()}.
- *
- * <p>The first constructor is the first ocurring in the source code.
- */
- private void buildCodecUsingConstructor(TypeElement classElement) {
- TypeSpec.Builder codecClassBuilder =
- TypeSpec.classBuilder(AutoCodecUtil.getCodecName(classElement))
- .superclass(TypeName.get(classElement.asType()));
-
- TypeElement encodedType = getEncodedType(classElement);
-
- // Generates the getEncodedClass method.
- codecClassBuilder.addMethod(
- MethodSpec.methodBuilder("getEncodedClass")
- .addModifiers(Modifier.PUBLIC)
- .addAnnotation(Override.class)
- .returns(
- ParameterizedTypeName.get(
- ClassName.get(Class.class), TypeName.get(encodedType.asType())))
- .addStatement("return $T.class", TypeName.get(encodedType.asType()))
- .build());
-
+ void buildClassWithConstructorStrategy(
+ TypeSpec.Builder codecClassBuilder, TypeElement encodedType) {
// In Java, every class has a constructor, so this always succeeds.
ExecutableElement constructor =
ElementFilter.constructorsIn(encodedType.getEnclosedElements()).get(0);
List<? extends VariableElement> constructorParameters = constructor.getParameters();
- addSerializeMethodUsingConstructor(codecClassBuilder, encodedType, constructorParameters);
- addDeserializeMethodUsingConstructor(codecClassBuilder, encodedType, constructorParameters);
+ codecClassBuilder.addMethod(
+ buildSerializeMethod(
+ encodedType, constructorParameters, AutoCodecProcessor::paramNameAsGetter));
+ MethodSpec.Builder deserializeBuilder =
+ initializeDeserializeMethodBuilder(encodedType, constructorParameters);
+ addReturnNew(deserializeBuilder, encodedType, constructorParameters);
+ codecClassBuilder.addMethod(deserializeBuilder.build());
+ }
- String packageName =
- env.getElementUtils().getPackageOf(classElement).getQualifiedName().toString();
- try {
- JavaFile file = JavaFile.builder(packageName, codecClassBuilder.build()).build();
- file.writeTo(env.getFiler());
- if (env.getOptions().containsKey("autocodec_print_generated")) {
- note("AutoCodec generated codec for " + classElement + ":\n" + file);
- }
- } catch (IOException e) {
- env.getMessager()
- .printMessage(Diagnostic.Kind.ERROR, "Failed to generate output file: " + e.getMessage());
- }
+ void buildClassWithPublicFieldsStrategy(
+ TypeSpec.Builder codecClassBuilder, TypeElement encodedType) {
+ List<? extends VariableElement> publicFields =
+ ElementFilter.fieldsIn(env.getElementUtils().getAllMembers(encodedType))
+ .stream()
+ .filter(this::isPublicField)
+ .collect(Collectors.toList());
+ codecClassBuilder.addMethod(
+ buildSerializeMethod(encodedType, publicFields, UnaryOperator.identity()));
+ MethodSpec.Builder deserializeBuilder =
+ initializeDeserializeMethodBuilder(encodedType, publicFields);
+ addInstantiatePopulateFieldsAndReturn(deserializeBuilder, encodedType, publicFields);
+ codecClassBuilder.addMethod(deserializeBuilder.build());
+ }
+
+ private TypeSpec.Builder initializeCodecClassBuilder(TypeElement encodedType) {
+ return TypeSpec.classBuilder(AutoCodecUtil.getCodecName(encodedType))
+ .addSuperinterface(
+ ParameterizedTypeName.get(
+ ClassName.get(ObjectCodec.class), TypeName.get(encodedType.asType())));
+ }
+
+ private static MethodSpec buildGetEncodedClassMethod(TypeElement encodedType) {
+ return MethodSpec.methodBuilder("getEncodedClass")
+ .addModifiers(Modifier.PUBLIC)
+ .addAnnotation(Override.class)
+ .returns(
+ ParameterizedTypeName.get(
+ ClassName.get(Class.class), TypeName.get(encodedType.asType())))
+ .addStatement("return $T.class", TypeName.get(encodedType.asType()))
+ .build();
}
/**
@@ -157,14 +175,22 @@ public class AutoCodecProcessor extends AbstractProcessor {
*
* <p>For example, a parameter called {@code target} results in {@code getTarget()}.
*/
- private static String paramNameAsAccessor(String name) {
+ private static String paramNameAsGetter(String name) {
return "get" + name.substring(0, 1).toUpperCase() + name.substring(1) + "()";
}
- private void addSerializeMethodUsingConstructor(
- TypeSpec.Builder codecClassBuilder,
+ private boolean isPublicField(VariableElement element) {
+ if (matchesType(element.asType(), Void.class)) {
+ return false; // Void types can't be instantiated, so the processor ignores them completely.
+ }
+ Set<Modifier> modifiers = element.getModifiers();
+ return modifiers.contains(Modifier.PUBLIC) && !modifiers.contains(Modifier.STATIC);
+ }
+
+ private MethodSpec buildSerializeMethod(
TypeElement encodedType,
- List<? extends VariableElement> constructorParameters) {
+ List<? extends VariableElement> parameters,
+ UnaryOperator<String> nameToAccessor) {
MethodSpec.Builder serializeBuilder =
MethodSpec.methodBuilder("serialize")
.addModifiers(Modifier.PUBLIC)
@@ -174,20 +200,34 @@ public class AutoCodecProcessor extends AbstractProcessor {
.addAnnotation(Override.class)
.addException(SerializationException.class)
.addException(IOException.class);
- for (VariableElement parameter : constructorParameters) {
- buildSerializeBody(
- serializeBuilder,
- (DeclaredType) parameter.asType(),
- "input." + paramNameAsAccessor(parameter.getSimpleName().toString()));
+ for (VariableElement parameter : parameters) {
+ String paramAccessor = "input." + nameToAccessor.apply(parameter.getSimpleName().toString());
+ TypeKind typeKind = parameter.asType().getKind();
+ switch (typeKind) {
+ case BOOLEAN:
+ serializeBuilder.addStatement("codedOut.writeBoolNoTag($L)", paramAccessor);
+ break;
+ case DECLARED:
+ buildSerializeBody(serializeBuilder, (DeclaredType) parameter.asType(), paramAccessor);
+ break;
+ default:
+ throw new IllegalArgumentException("Unimplemented or invalid kind: " + typeKind);
+ }
}
- codecClassBuilder.addMethod(serializeBuilder.build());
+ return serializeBuilder.build();
}
- private void addDeserializeMethodUsingConstructor(
- TypeSpec.Builder codecClassBuilder,
- TypeElement encodedType,
- List<? extends VariableElement> constructorParameters) {
- MethodSpec.Builder deserializeBuilder =
+ /**
+ * Creates a method builder defining the deserialize method and a body that extracts serialized
+ * parameters.
+ *
+ * <p>Parameter values are extracted into local variables with the same name as the parameter
+ * suffixed with a trailing underscore. For example, {@code target} becomes {@code target_}. This
+ * is to avoid name collisions.
+ */
+ private MethodSpec.Builder initializeDeserializeMethodBuilder(
+ TypeElement encodedType, List<? extends VariableElement> parameters) {
+ MethodSpec.Builder builder =
MethodSpec.methodBuilder("deserialize")
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.get(encodedType.asType()))
@@ -195,21 +235,21 @@ public class AutoCodecProcessor extends AbstractProcessor {
.addAnnotation(Override.class)
.addException(SerializationException.class)
.addException(IOException.class);
- for (VariableElement parameter : constructorParameters) {
- buildDeserializeBody(
- deserializeBuilder,
- (DeclaredType) parameter.asType(),
- parameter.getSimpleName().toString());
+ for (VariableElement parameter : parameters) {
+ String paramName = parameter.getSimpleName() + "_";
+ TypeKind typeKind = parameter.asType().getKind();
+ switch (typeKind) {
+ case BOOLEAN:
+ builder.addStatement("boolean $L = codedIn.readBool()", paramName);
+ break;
+ case DECLARED:
+ buildDeserializeBody(builder, (DeclaredType) parameter.asType(), paramName);
+ break;
+ default:
+ throw new IllegalArgumentException("Unimplemented or invalid kind: " + typeKind);
+ }
}
- // Invokes the constructor and returns the value.
- deserializeBuilder.addStatement(
- "return new $T($L)",
- TypeName.get(encodedType.asType()),
- constructorParameters
- .stream()
- .map(p -> p.getSimpleName().toString())
- .collect(Collectors.joining(", ")));
- codecClassBuilder.addMethod(deserializeBuilder.build());
+ return builder;
}
/**
@@ -217,21 +257,29 @@ public class AutoCodecProcessor extends AbstractProcessor {
* accessor}.
*
* @param type the type of {@code accessor}
+ * @param depth recursion depth of buildSerializeBody
*/
- private void buildSerializeBody(MethodSpec.Builder builder, DeclaredType type, String accessor) {
+ private void buildSerializeBody(
+ MethodSpec.Builder builder, DeclaredType type, String accessor, int depth) {
builder.beginControlFlow("if ($L != null)", accessor); // Begin if not null block.
builder.addStatement("codedOut.writeBoolNoTag(true)");
// TODO(shahan): Add support for more types.
- if (matchesErased(type, ImmutableSortedSet.class)) {
+ if (isEnum(type)) {
+ builder.addStatement("codedOut.writeInt32NoTag($L.ordinal())", accessor);
+ } else if (matchesType(type, String.class)) {
+ builder.addStatement(
+ "$T.asciiOptimized().serialize($L, codedOut)", StringCodecs.class, accessor);
+ } else if (matchesErased(type, List.class) || matchesErased(type, ImmutableSortedSet.class)) {
// Writes the target count to the stream so deserialization knows when to stop.
builder.addStatement("codedOut.writeInt32NoTag($L.size())", accessor);
DeclaredType repeatedType = (DeclaredType) type.getTypeArguments().get(0);
- // TODO(shahan): consider introducing a depth parameter to avoid shadowing here.
- builder.beginControlFlow("for ($T repeated : $L)", TypeName.get(repeatedType), accessor);
- buildSerializeBody(builder, repeatedType, "repeated");
+ String repeatedName = "repeated" + depth;
+ builder.beginControlFlow(
+ "for ($T $L : $L)", TypeName.get(repeatedType), repeatedName, accessor);
+ buildSerializeBody(builder, repeatedType, repeatedName, depth + 1);
builder.endControlFlow();
} else {
- // Otherwise use the type's CODEC.
+ // Otherwise use the type's codec.
builder.addStatement("$T.CODEC.serialize($L, codedOut)", TypeName.get(type), accessor);
}
builder.nextControlFlow("else");
@@ -239,17 +287,73 @@ public class AutoCodecProcessor extends AbstractProcessor {
builder.endControlFlow(); // End if not null.
}
+ /** Convenience overload for depth = 0. */
+ private void buildSerializeBody(MethodSpec.Builder builder, DeclaredType type, String accessor) {
+ buildSerializeBody(builder, type, accessor, /*depth=*/ 0);
+ }
+
/**
- * Appends code statements to {@code builder} declaring a variable called {@code name} and
- * initializing it by deserialization.
+ * Invokes the constructor and returns the value.
*
- * @param type the type of {@code name}
+ * <p>Used by the {@link AutoCodec.Strategy.CONSTRUCTOR} strategy.
*/
- private void buildDeserializeBody(MethodSpec.Builder builder, DeclaredType type, String name) {
+ private void addReturnNew(
+ MethodSpec.Builder builder, TypeElement type, List<? extends VariableElement> parameters) {
+ builder.addStatement(
+ "return new $T($L)",
+ TypeName.get(type.asType()),
+ parameters.stream().map(p -> p.getSimpleName() + "_").collect(Collectors.joining(", ")));
+ }
+
+ /**
+ * Invokes the constructor, populates public fields and returns the value.
+ *
+ * <p>Used by the {@link AutoCodec.Strategy.PUBLIC_FIELDS} strategy.
+ */
+ private static void addInstantiatePopulateFieldsAndReturn(
+ MethodSpec.Builder builder, TypeElement type, List<? extends VariableElement> fields) {
+ builder.addStatement(
+ "$T deserializationResult = new $T()",
+ TypeName.get(type.asType()),
+ TypeName.get(type.asType()));
+ for (VariableElement field : fields) {
+ String fieldName = field.getSimpleName().toString();
+ builder.addStatement("deserializationResult.$L = $L", fieldName, fieldName + "_");
+ }
+ builder.addStatement("return deserializationResult");
+ }
+
+ /**
+ * Appends code statements to {@code builder}, declaring a variable called {@code name} and
+ * initializing it with deserialization.
+ *
+ * @param type the type of {@code name}.
+ * @param depth recursion depth of buildDeserializeBody.
+ */
+ private void buildDeserializeBody(
+ MethodSpec.Builder builder, DeclaredType type, String name, int depth) {
builder.addStatement("$T $L = null", TypeName.get(type), name);
builder.beginControlFlow("if (codedIn.readBool())"); // Begin null-handling block.
// TODO(shahan): Add support for more types.
- if (matchesErased(type, ImmutableSortedSet.class)) {
+ if (isEnum(type)) {
+ // TODO(shahan): memoize this expensive call to values().
+ builder.addStatement("$L = $T.values()[codedIn.readInt32()]", name, TypeName.get(type));
+ } else if (matchesType(type, String.class)) {
+ builder.addStatement(
+ "$L = $T.asciiOptimized().deserialize(codedIn)", name, StringCodecs.class);
+ } else if (matchesErased(type, List.class)) {
+ builder.addStatement("$L = new $T<>()", name, ArrayList.class);
+ String lengthName = "length" + depth;
+ builder.addStatement("int $L = codedIn.readInt32()", lengthName);
+ String indexName = "i" + depth;
+ builder.beginControlFlow(
+ "for (int $L = 0; $L < $L; ++$L)", indexName, indexName, lengthName, indexName);
+ DeclaredType repeatedType = (DeclaredType) type.getTypeArguments().get(0);
+ String repeatedName = "repeated" + depth;
+ buildDeserializeBody(builder, repeatedType, repeatedName, depth + 1);
+ builder.addStatement("$L.add($L)", name, repeatedName);
+ builder.endControlFlow();
+ } else if (matchesErased(type, ImmutableSortedSet.class)) {
DeclaredType repeatedType = (DeclaredType) type.getTypeArguments().get(0);
builder.addStatement(
"$T<$T> builder = new $T<>($T.naturalOrder())",
@@ -257,33 +361,33 @@ public class AutoCodecProcessor extends AbstractProcessor {
TypeName.get(repeatedType),
ImmutableSortedSet.Builder.class,
Comparator.class);
- builder.addStatement("int length = codedIn.readInt32()");
- builder.beginControlFlow("for (int i = 0; i < length; ++i)");
- buildDeserializeBody(builder, repeatedType, "repeated");
- builder.addStatement("builder.add(repeated)");
+ String lengthName = "length" + depth;
+ builder.addStatement("int $L = codedIn.readInt32()", lengthName);
+ String indexName = "i" + depth;
+ builder.beginControlFlow(
+ "for (int $L = 0; $L < $L; ++$L)", indexName, indexName, lengthName, indexName);
+ String repeatedName = "repeated" + depth;
+ buildDeserializeBody(builder, repeatedType, repeatedName, depth + 1);
+ builder.addStatement("builder.add($L)", repeatedName);
builder.endControlFlow();
builder.addStatement("$L = builder.build()", name);
} else {
- // Otherwise, use the type's CODEC value.
+ // Otherwise, use the type's codec.
builder.addStatement("$L = $T.CODEC.deserialize(codedIn)", name, TypeName.get(type));
}
builder.endControlFlow(); // End null-handling block.
}
- /**
- * Gets the type parameter of ObjectCodec, i.e., the type being encoded.
- *
- * <p>{@code element} must implement ObjectCodec.
- */
- private TypeElement getEncodedType(TypeElement element) {
- for (TypeMirror implementedInterface : element.getInterfaces()) {
- if (matchesErased(implementedInterface, ObjectCodec.class)) {
- return (TypeElement)
- env.getTypeUtils()
- .asElement(((DeclaredType) implementedInterface).getTypeArguments().get(0));
- }
- }
- throw new IllegalArgumentException(element + " does not implement ObjectCodec!");
+ /** Overload of above, for common case of 0 depth. */
+ private void buildDeserializeBody(MethodSpec.Builder builder, DeclaredType type, String name) {
+ buildDeserializeBody(builder, type, name, /*depth=*/ 0);
+ }
+
+ /** True when {@code type} has the same type as {@code clazz}. */
+ private boolean matchesType(TypeMirror type, Class<?> clazz) {
+ return env.getTypeUtils()
+ .isSameType(
+ type, env.getElementUtils().getTypeElement((clazz.getCanonicalName())).asType());
}
/** True when erasure of {@code type} matches erasure of {@code clazz}. */
@@ -296,6 +400,10 @@ public class AutoCodecProcessor extends AbstractProcessor {
env.getElementUtils().getTypeElement((clazz.getCanonicalName())).asType()));
}
+ private boolean isEnum(TypeMirror type) {
+ return env.getTypeUtils().asElement(type).getKind() == ElementKind.ENUM;
+ }
+
/** Emits a note to BUILD log during annotation processing for debugging. */
private void note(String note) {
env.getMessager().printMessage(Diagnostic.Kind.NOTE, note);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/BUILD
index 193570ebb4..9fff28b2dc 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/BUILD
@@ -11,7 +11,12 @@ filegroup(
java_library(
name = "autocodec",
exported_plugins = [":autocodec-plugin"],
- exports = [":autocodec-annotation"],
+ exports = [
+ ":autocodec-annotation",
+ # Generated classes have the following dependencies.
+ "//third_party/protobuf:protobuf_java",
+ "//src/main/java/com/google/devtools/build/lib/skyframe/serialization",
+ ],
)
# @AutoCodec annotation only. Used by clients and the processor.