aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main
diff options
context:
space:
mode:
authorGravatar janakr <janakr@google.com>2017-08-17 17:49:50 +0200
committerGravatar Irina Iancu <elenairina@google.com>2017-08-18 09:01:03 +0200
commitb86f8b06f5f4832b51e3be0de9d32170a79371ee (patch)
tree154fe134c52fafccd35b7e086c7076666491901d /src/main
parent7321812d556f659b65952d5205250230e19d76cb (diff)
Open-source Skyframe serialization, and make AppleConfiguration serializable as a pilot. Currently not hooked up to anything, but will be shortly.
PiperOrigin-RevId: 165583517
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/google/devtools/build/lib/BUILD2
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java70
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java98
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/apple/ApplePlatform.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/apple/BUILD5
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/apple/DottedVersion.java50
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/TargetPatternPhaseValue.java1
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/TestSuiteExpansionValue.java1
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/TestsInSuiteValue.java1
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/BUILD20
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/BaseCodec.java31
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/EnumCodec.java59
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/FastStringCodec.java140
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableListCodec.java62
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/LabelCodec.java49
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/NotSerializableRuntimeException.java (renamed from src/main/java/com/google/devtools/build/lib/skyframe/NotSerializableRuntimeException.java)3
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodec.java47
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/PackageIdentifierCodec.java49
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/PathFragmentCodec.java55
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/RepositoryNameCodec.java56
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationCommonUtils.java41
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationException.java50
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/StringCodec.java38
23 files changed, 919 insertions, 12 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index eb3b78d53f..18b40a4636 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -37,6 +37,7 @@ filegroup(
"//src/main/java/com/google/devtools/build/lib/rules/genquery:srcs",
"//src/main/java/com/google/devtools/build/lib/rules/genrule:srcs",
"//src/main/java/com/google/devtools/build/lib/rules/objc:srcs",
+ "//src/main/java/com/google/devtools/build/lib/skyframe/serialization:srcs",
"//src/main/java/com/google/devtools/build/lib/analysis/featurecontrol:srcs",
"//src/main/java/com/google/devtools/build/lib/analysis/platform:srcs",
"//src/main/java/com/google/devtools/build/lib/analysis/whitelisting:srcs",
@@ -641,6 +642,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto",
"//src/main/java/com/google/devtools/build/lib/causes",
"//src/main/java/com/google/devtools/build/lib/cmdline",
+ "//src/main/java/com/google/devtools/build/lib/skyframe/serialization",
"//src/main/java/com/google/devtools/build/skyframe",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
"//src/main/java/com/google/devtools/common/options",
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java
index 36c9449462..268c47d4e7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java
@@ -14,6 +14,8 @@
package com.google.devtools.build.lib.rules.apple;
+import static com.google.devtools.build.lib.skyframe.serialization.SerializationCommonUtils.STRING_LIST_CODEC;
+
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
@@ -24,6 +26,10 @@ import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.rules.apple.AppleConfiguration.ConfigurationDistinguisher;
import com.google.devtools.build.lib.rules.apple.ApplePlatform.PlatformType;
+import com.google.devtools.build.lib.skyframe.serialization.EnumCodec;
+import com.google.devtools.build.lib.skyframe.serialization.FastStringCodec;
+import com.google.devtools.build.lib.skyframe.serialization.LabelCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
@@ -34,6 +40,9 @@ import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionDocumentationCategory;
import com.google.devtools.common.options.OptionEffectTag;
import com.google.devtools.common.options.OptionMetadataTag;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
import java.util.List;
/**
@@ -470,6 +479,8 @@ public class AppleCommandLineOptions extends FragmentOptions {
super(AppleBitcodeMode.class, "apple bitcode mode");
}
}
+
+ static final EnumCodec<AppleBitcodeMode> CODEC = new EnumCodec<>(AppleBitcodeMode.class);
}
@Override
@@ -492,6 +503,65 @@ public class AppleCommandLineOptions extends FragmentOptions {
return host;
}
+ void serialize(CodedOutputStream out) throws IOException, SerializationException {
+ out.writeBoolNoTag(mandatoryMinimumVersion);
+ xcodeVersion.serialize(out);
+ iosSdkVersion.serialize(out);
+ watchOsSdkVersion.serialize(out);
+ tvOsSdkVersion.serialize(out);
+ macOsSdkVersion.serialize(out);
+ iosMinimumOs.serialize(out);
+ watchosMinimumOs.serialize(out);
+ tvosMinimumOs.serialize(out);
+ macosMinimumOs.serialize(out);
+ FastStringCodec.INSTANCE.serialize(iosCpu, out);
+ LabelCodec.INSTANCE.serialize(appleCrosstoolTop, out);
+ PlatformType.CODEC.serialize(applePlatformType, out);
+ FastStringCodec.INSTANCE.serialize(appleSplitCpu, out);
+ ConfigurationDistinguisher.CODEC.serialize(configurationDistinguisher, out);
+ STRING_LIST_CODEC.serialize((ImmutableList<String>) iosMultiCpus, out);
+ STRING_LIST_CODEC.serialize((ImmutableList<String>) watchosCpus, out);
+ STRING_LIST_CODEC.serialize((ImmutableList<String>) tvosCpus, out);
+ STRING_LIST_CODEC.serialize((ImmutableList<String>) macosCpus, out);
+ LabelCodec.INSTANCE.serialize(defaultProvisioningProfile, out);
+ LabelCodec.INSTANCE.serialize(xcodeVersionConfig, out);
+ FastStringCodec.INSTANCE.serialize(xcodeToolchain, out);
+ AppleBitcodeMode.CODEC.serialize(appleBitcodeMode, out);
+ out.writeBoolNoTag(enableAppleCrosstoolTransition);
+ out.writeBoolNoTag(targetUsesAppleCrosstool);
+ }
+
+ static AppleCommandLineOptions deserialize(CodedInputStream in)
+ throws IOException, SerializationException {
+ AppleCommandLineOptions result = new AppleCommandLineOptions();
+ result.mandatoryMinimumVersion = in.readBool();
+ result.xcodeVersion = DottedVersion.deserialize(in);
+ result.iosSdkVersion = DottedVersion.deserialize(in);
+ result.watchOsSdkVersion = DottedVersion.deserialize(in);
+ result.tvOsSdkVersion = DottedVersion.deserialize(in);
+ result.macOsSdkVersion = DottedVersion.deserialize(in);
+ result.iosMinimumOs = DottedVersion.deserialize(in);
+ result.watchosMinimumOs = DottedVersion.deserialize(in);
+ result.tvosMinimumOs = DottedVersion.deserialize(in);
+ result.macosMinimumOs = DottedVersion.deserialize(in);
+ result.iosCpu = FastStringCodec.INSTANCE.deserialize(in);
+ result.appleCrosstoolTop = LabelCodec.INSTANCE.deserialize(in);
+ result.applePlatformType = PlatformType.CODEC.deserialize(in);
+ result.appleSplitCpu = FastStringCodec.INSTANCE.deserialize(in);
+ result.configurationDistinguisher = ConfigurationDistinguisher.CODEC.deserialize(in);
+ result.iosMultiCpus = STRING_LIST_CODEC.deserialize(in);
+ result.watchosCpus = STRING_LIST_CODEC.deserialize(in);
+ result.tvosCpus = STRING_LIST_CODEC.deserialize(in);
+ result.macosCpus = STRING_LIST_CODEC.deserialize(in);
+ result.defaultProvisioningProfile = LabelCodec.INSTANCE.deserialize(in);
+ result.xcodeVersionConfig = LabelCodec.INSTANCE.deserialize(in);
+ result.xcodeToolchain = FastStringCodec.INSTANCE.deserialize(in);
+ result.appleBitcodeMode = AppleBitcodeMode.CODEC.deserialize(in);
+ result.enableAppleCrosstoolTransition = in.readBool();
+ result.targetUsesAppleCrosstool = in.readBool();
+ return result;
+ }
+
/** Converter for the Apple configuration distinguisher. */
public static final class ConfigurationDistinguisherConverter
extends EnumConverter<ConfigurationDistinguisher> {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java
index 3ab182ad59..b2a1a98604 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java
@@ -31,13 +31,20 @@ import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions.AppleBitcodeMode;
import com.google.devtools.build.lib.rules.apple.ApplePlatform.PlatformType;
+import com.google.devtools.build.lib.skyframe.serialization.EnumCodec;
+import com.google.devtools.build.lib.skyframe.serialization.FastStringCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.util.Preconditions;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import javax.annotation.Nullable;
/** A configuration containing flags required for Apple platforms and tools. */
@@ -99,10 +106,11 @@ public class AppleConfiguration extends BuildConfiguration.Fragment {
@Nullable private final Label defaultProvisioningProfileLabel;
private final boolean mandatoryMinimumVersion;
+ @VisibleForTesting
AppleConfiguration(
AppleCommandLineOptions options,
- String cpu,
- XcodeVersionProperties xcodeVersionProperties,
+ String iosCpu,
+ @Nullable DottedVersion xcodeVersion,
DottedVersion iosSdkVersion,
DottedVersion iosMinimumOs,
DottedVersion watchosSdkVersion,
@@ -126,8 +134,8 @@ public class AppleConfiguration extends BuildConfiguration.Fragment {
Preconditions.checkNotNull(macosSdkVersion, "macOsSdkVersion");
this.macosMinimumOs = Preconditions.checkNotNull(macosMinimumOs, "macOsMinimumOs");
- this.xcodeVersion = xcodeVersionProperties.getXcodeVersion().orNull();
- this.iosCpu = iosCpuFromCpu(cpu);
+ this.xcodeVersion = xcodeVersion;
+ this.iosCpu = iosCpu;
this.appleSplitCpu = Preconditions.checkNotNull(options.appleSplitCpu, "appleSplitCpu");
this.applePlatformType =
Preconditions.checkNotNull(options.applePlatformType, "applePlatformType");
@@ -670,6 +678,81 @@ public class AppleConfiguration extends BuildConfiguration.Fragment {
.build();
}
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof AppleConfiguration)) {
+ return false;
+ }
+ AppleConfiguration that = (AppleConfiguration) obj;
+ return this.options.equals(that.options)
+ && Objects.equals(this.xcodeVersion, that.xcodeVersion)
+ && this.iosSdkVersion.equals(that.iosSdkVersion)
+ && this.iosMinimumOs.equals(that.iosMinimumOs)
+ && this.watchosSdkVersion.equals(that.watchosSdkVersion)
+ && this.watchosMinimumOs.equals(that.watchosMinimumOs)
+ && this.tvosSdkVersion.equals(that.tvosSdkVersion)
+ && this.tvosMinimumOs.equals(that.tvosMinimumOs)
+ && this.macosSdkVersion.equals(that.macosSdkVersion)
+ && this.macosMinimumOs.equals(that.macosMinimumOs);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ options,
+ xcodeVersion,
+ iosSdkVersion,
+ iosMinimumOs,
+ watchosSdkVersion,
+ watchosMinimumOs,
+ tvosSdkVersion,
+ tvosMinimumOs,
+ macosSdkVersion,
+ macosMinimumOs);
+ }
+
+ void serialize(CodedOutputStream out) throws IOException, SerializationException {
+ options.serialize(out);
+ out.writeStringNoTag(iosCpu);
+ if (xcodeVersion == null) {
+ out.writeBoolNoTag(false);
+ } else {
+ out.writeBoolNoTag(true);
+ xcodeVersion.serialize(out);
+ }
+ iosSdkVersion.serialize(out);
+ iosMinimumOs.serialize(out);
+ watchosSdkVersion.serialize(out);
+ watchosMinimumOs.serialize(out);
+ tvosSdkVersion.serialize(out);
+ tvosMinimumOs.serialize(out);
+ macosSdkVersion.serialize(out);
+ macosMinimumOs.serialize(out);
+ }
+
+ static AppleConfiguration deserialize(CodedInputStream in)
+ throws IOException, SerializationException {
+ AppleCommandLineOptions options = AppleCommandLineOptions.deserialize(in);
+ String iosCpu = FastStringCodec.INSTANCE.deserialize(in);
+ boolean hasXcodeVersion = in.readBool();
+ DottedVersion xcodeVersion = hasXcodeVersion ? DottedVersion.deserialize(in) : null;
+ return new AppleConfiguration(
+ options,
+ iosCpu,
+ xcodeVersion,
+ DottedVersion.deserialize(in),
+ DottedVersion.deserialize(in),
+ DottedVersion.deserialize(in),
+ DottedVersion.deserialize(in),
+ DottedVersion.deserialize(in),
+ DottedVersion.deserialize(in),
+ DottedVersion.deserialize(in),
+ DottedVersion.deserialize(in));
+ }
+
/**
* Loads {@link AppleConfiguration} from build options.
*/
@@ -701,8 +784,8 @@ public class AppleConfiguration extends BuildConfiguration.Fragment {
AppleConfiguration configuration =
new AppleConfiguration(
appleOptions,
- cpu,
- xcodeVersionProperties,
+ iosCpuFromCpu(cpu),
+ xcodeVersionProperties.getXcodeVersion().orNull(),
iosSdkVersion,
iosMinimumOsVersion,
watchosSdkVersion,
@@ -781,5 +864,8 @@ public class AppleConfiguration extends BuildConfiguration.Fragment {
public String getFileSystemName() {
return fileSystemName;
}
+
+ static final EnumCodec<ConfigurationDistinguisher> CODEC =
+ new EnumCodec<>(ConfigurationDistinguisher.class);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/ApplePlatform.java b/src/main/java/com/google/devtools/build/lib/rules/apple/ApplePlatform.java
index 6be2024464..bccee16485 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/ApplePlatform.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/ApplePlatform.java
@@ -19,6 +19,7 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.packages.Info;
import com.google.devtools.build.lib.packages.NativeProvider;
import com.google.devtools.build.lib.packages.Provider;
+import com.google.devtools.build.lib.skyframe.serialization.EnumCodec;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
@@ -299,5 +300,7 @@ public enum ApplePlatform implements SkylarkValue {
public void repr(SkylarkPrinter printer) {
printer.append(toString());
}
+
+ static final EnumCodec<PlatformType> CODEC = new EnumCodec<>(PlatformType.class);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/BUILD b/src/main/java/com/google/devtools/build/lib/rules/apple/BUILD
index 15ae78dbf2..b6fb30189c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/BUILD
@@ -14,15 +14,14 @@ java_library(
"//src/main/java/com/google/devtools/build/lib:events",
"//src/main/java/com/google/devtools/build/lib:packages-internal",
"//src/main/java/com/google/devtools/build/lib:preconditions",
- "//src/main/java/com/google/devtools/build/lib:shell",
"//src/main/java/com/google/devtools/build/lib:skylarkinterface",
"//src/main/java/com/google/devtools/build/lib:syntax",
- "//src/main/java/com/google/devtools/build/lib:vfs",
- "//src/main/java/com/google/devtools/build/lib/actions",
"//src/main/java/com/google/devtools/build/lib/cmdline",
+ "//src/main/java/com/google/devtools/build/lib/skyframe/serialization",
"//src/main/java/com/google/devtools/common/options",
"//third_party:guava",
"//third_party:jsr305",
+ "//third_party/protobuf:protobuf_java",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/DottedVersion.java b/src/main/java/com/google/devtools/build/lib/rules/apple/DottedVersion.java
index bdbc910905..5fbbef561c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/DottedVersion.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/DottedVersion.java
@@ -26,10 +26,14 @@ import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.annotation.Nullable;
/**
* Represents a value with multiple components, separated by periods, for example {@code 4.5.6} or
@@ -249,13 +253,35 @@ public final class DottedVersion implements Comparable<DottedVersion>, SkylarkVa
printer.append(stringRepresentation);
}
+ void serialize(CodedOutputStream out) throws IOException {
+ out.writeInt32NoTag(components.size());
+ for (Component component : components) {
+ component.serialize(out);
+ }
+ out.writeStringNoTag(stringRepresentation);
+ out.writeInt32NoTag(numOriginalComponents);
+ }
+
+ static DottedVersion deserialize(CodedInputStream in) throws IOException {
+ int numComponents = in.readInt32();
+ // TODO(janakr: Presize this if/when https://github.com/google/guava/issues/196 is resolved.
+ ImmutableList.Builder<Component> components = ImmutableList.builder();
+ for (int i = 0; i < numComponents; i++) {
+ components.add(Component.deserialize(in));
+ }
+ return new DottedVersion(components.build(), in.readString(), in.readInt32());
+ }
+
private static final class Component implements Comparable<Component> {
private final int firstNumber;
- private final String alphaSequence;
+ @Nullable private final String alphaSequence;
private final int secondNumber;
private final String stringRepresentation;
- public Component(int firstNumber, String alphaSequence, int secondNumber,
+ public Component(
+ int firstNumber,
+ @Nullable String alphaSequence,
+ int secondNumber,
String stringRepresentation) {
this.firstNumber = firstNumber;
this.alphaSequence = alphaSequence;
@@ -293,5 +319,25 @@ public final class DottedVersion implements Comparable<DottedVersion>, SkylarkVa
public String toString() {
return stringRepresentation;
}
+
+ void serialize(CodedOutputStream out) throws IOException {
+ if (alphaSequence == null) {
+ out.writeBoolNoTag(false);
+ } else {
+ out.writeBoolNoTag(true);
+ out.writeStringNoTag(alphaSequence);
+ }
+ out.writeInt32NoTag(firstNumber);
+ out.writeInt32NoTag(secondNumber);
+ out.writeStringNoTag(stringRepresentation);
+ }
+
+ static Component deserialize(CodedInputStream in) throws IOException {
+ String alphaSequence = null;
+ if (in.readBool()) {
+ alphaSequence = in.readString();
+ }
+ return new Component(in.readInt32(), alphaSequence, in.readInt32(), in.readString());
+ }
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TargetPatternPhaseValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TargetPatternPhaseValue.java
index 5d237076a1..49cd581f52 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TargetPatternPhaseValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TargetPatternPhaseValue.java
@@ -21,6 +21,7 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.pkgcache.LoadingResult;
import com.google.devtools.build.lib.pkgcache.TestFilter;
+import com.google.devtools.build.lib.skyframe.serialization.NotSerializableRuntimeException;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TestSuiteExpansionValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TestSuiteExpansionValue.java
index 1da1feeffb..8bc2f2bfb4 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TestSuiteExpansionValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TestSuiteExpansionValue.java
@@ -21,6 +21,7 @@ import com.google.devtools.build.lib.cmdline.ResolvedTargets;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.skyframe.serialization.NotSerializableRuntimeException;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TestsInSuiteValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TestsInSuiteValue.java
index bed1646217..63a55fbd86 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TestsInSuiteValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TestsInSuiteValue.java
@@ -19,6 +19,7 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.TargetUtils;
+import com.google.devtools.build.lib.skyframe.serialization.NotSerializableRuntimeException;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BUILD
new file mode 100644
index 0000000000..6de7a8e5a2
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BUILD
@@ -0,0 +1,20 @@
+# TODO(janakr): find out how to avoid this default visibility and still have
+# automatic BUILD-file generation.
+package(default_visibility = ["//src:__subpackages__"])
+
+java_library(
+ name = "serialization",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//src/main/java/com/google/devtools/build/lib:vfs",
+ "//src/main/java/com/google/devtools/build/lib/cmdline",
+ "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
+ "//third_party:guava",
+ "//third_party/protobuf:protobuf_java",
+ ],
+)
+
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+)
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BaseCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BaseCodec.java
new file mode 100644
index 0000000000..dd286fc7eb
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BaseCodec.java
@@ -0,0 +1,31 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+
+/** An opaque interface for codecs that just reveals the {@link Class} of its objects. */
+interface BaseCodec<T> {
+ /**
+ * Returns the class of the objects serialized/deserialized by this codec.
+ *
+ * <p>This is useful for automatically dispatching to the correct codec, e.g. in {@link
+ * ObjectCodecs} and {@link BaseCodecMap}. It may also be useful for automatically registering
+ * codecs for {@link SkyKey}s and {@link SkyValue}s instead of using the current manual mapping
+ * (b/26186886).
+ */
+ Class<T> getEncodedClass();
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/EnumCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/EnumCodec.java
new file mode 100644
index 0000000000..523620e1cb
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/EnumCodec.java
@@ -0,0 +1,59 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+
+/** Codec for an enum. */
+public class EnumCodec<T extends Enum<T>> implements ObjectCodec<T> {
+
+ private final Class<T> enumClass;
+
+ /**
+ * A cached copy of T.values(), to avoid allocating an array upon every deserialization operation.
+ */
+ private final ImmutableList<T> values;
+
+ public EnumCodec(Class<T> enumClass) {
+ this.enumClass = enumClass;
+ this.values = ImmutableList.copyOf(enumClass.getEnumConstants());
+ }
+
+ @Override
+ public Class<T> getEncodedClass() {
+ return enumClass;
+ }
+
+ @Override
+ public void serialize(T value, CodedOutputStream codedOut) throws IOException {
+ Preconditions.checkNotNull(value, "Enum value for %s is null", enumClass);
+ codedOut.writeEnumNoTag(value.ordinal());
+ }
+
+ @Override
+ public T deserialize(CodedInputStream codedIn) throws SerializationException, IOException {
+ int ordinal = codedIn.readEnum();
+ try {
+ return values.get(ordinal);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new SerializationException(
+ "Invalid ordinal for " + enumClass.getName() + " enum: " + ordinal, e);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/FastStringCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/FastStringCodec.java
new file mode 100644
index 0000000000..23bf38baae
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/FastStringCodec.java
@@ -0,0 +1,140 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import sun.misc.Unsafe;
+
+/**
+ * Similar to {@link StringCodec}, except with deserialization optimized for ascii data. It can
+ * still handle UTF-8, though less efficiently than {@link StringCodec}. Should be used when the
+ * majority of the data passing through will be ascii.
+ */
+public class FastStringCodec implements ObjectCodec<String> {
+ public static final FastStringCodec INSTANCE = new FastStringCodec();
+
+ private static final Unsafe theUnsafe;
+ private static final long STRING_VALUE_OFFSET;
+
+ private static final String EMPTY_STRING = "";
+
+ static {
+ theUnsafe = getUnsafe();
+ try {
+ // String's 'value' field stores its char[]. If this field changes name or type then the
+ // reflective check below will fail. We can reasonably expect our approach to be stable for
+ // now, but things are likely to change in java 9, hopefully in a way which obsoletes this
+ // optimization.
+ Field valueField = String.class.getDeclaredField("value");
+ Class<?> valueFieldType = valueField.getType();
+ if (!valueFieldType.equals(char[].class)) {
+ throw new AssertionError(
+ "Expected String's value field to be char[], but was " + valueFieldType);
+ }
+ STRING_VALUE_OFFSET = theUnsafe.objectFieldOffset(valueField);
+ } catch (NoSuchFieldException | SecurityException e) {
+ throw new AssertionError("Failed to find String's 'value' offset", e);
+ }
+ }
+
+ @Override
+ public Class<String> getEncodedClass() {
+ return String.class;
+ }
+
+ @Override
+ public void serialize(String string, CodedOutputStream codedOut) throws IOException {
+ codedOut.writeStringNoTag(string);
+ }
+
+ @Override
+ public String deserialize(CodedInputStream codedIn) throws IOException {
+ int length = codedIn.readInt32();
+ if (length == 0) {
+ return EMPTY_STRING;
+ }
+
+ char[] maybeDecoded = new char[length];
+ for (int i = 0; i < length; i++) {
+ // Read one byte at a time to avoid creating a new ByteString/copy of the underlying array.
+ byte b = codedIn.readRawByte();
+ // Check highest order bit, if it's set we've crossed into extended ascii/utf8.
+ if ((b & 0x80) == 0) {
+ maybeDecoded[i] = (char) b;
+ } else {
+ // Fail, we encountered a non-ascii byte. Copy what we have so far plus and then the rest
+ // of the data into a buffer and let String's constructor do the UTF-8 decoding work.
+ byte[] decodeFrom = new byte[length];
+ for (int j = 0; j < i; j++) {
+ decodeFrom[j] = (byte) maybeDecoded[j];
+ }
+ decodeFrom[i] = b;
+ for (int j = i + 1; j < length; j++) {
+ decodeFrom[j] = codedIn.readRawByte();
+ }
+ return new String(decodeFrom, StandardCharsets.UTF_8);
+ }
+ }
+
+ try {
+ String result = (String) theUnsafe.allocateInstance(String.class);
+ theUnsafe.putObject(result, STRING_VALUE_OFFSET, maybeDecoded);
+ return result;
+ } catch (Exception e) {
+ // This should only catch InstantiationException, but that makes IntelliJ unhappy for
+ // some reason; it insists that that exception cannot be thrown from here, even though it
+ // is set to JDK 8
+ throw new IllegalStateException("Could not create string", e);
+ }
+ }
+
+ /**
+ * Get a reference to {@link sun.misc.Unsafe} or throw an {@link AssertionError} if failing to do
+ * so. Failure is highly unlikely, but possible if the underlying VM stores unsafe in an
+ * unexpected location.
+ */
+ private static Unsafe getUnsafe() {
+ try {
+ // sun.misc.Unsafe is intentionally difficult to get a hold of - it gives us the power to
+ // do things like access raw memory and segfault the JVM.
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Unsafe>() {
+ @Override
+ public Unsafe run() throws Exception {
+ Class<Unsafe> unsafeClass = Unsafe.class;
+ // Unsafe usually exists in the field 'theUnsafe', however check all fields
+ // in case it's somewhere else in this VM's version of Unsafe.
+ for (Field f : unsafeClass.getDeclaredFields()) {
+ f.setAccessible(true);
+ Object fieldValue = f.get(null);
+ if (unsafeClass.isInstance(fieldValue)) {
+ return unsafeClass.cast(fieldValue);
+ }
+ }
+ throw new AssertionError("Failed to find sun.misc.Unsafe instance");
+ }
+ });
+ } catch (PrivilegedActionException pae) {
+ throw new AssertionError("Unable to get sun.misc.Unsafe", pae);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableListCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableListCodec.java
new file mode 100644
index 0000000000..1b54d1fb17
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableListCodec.java
@@ -0,0 +1,62 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+
+/** Encodes a list of elements using a specified {@link ObjectCodec}. */
+public class ImmutableListCodec<T> implements ObjectCodec<ImmutableList<T>> {
+
+ private final ObjectCodec<T> codec;
+
+ public ImmutableListCodec(ObjectCodec<T> codec) {
+ this.codec = codec;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Class<ImmutableList<T>> getEncodedClass() {
+ // Compiler doesn't like cast from Class<ImmutableList> -> Class<ImmutableList<T>>, but it
+ // does allow what we see below. Type is lost at runtime anyway, so while gross this works.
+ return (Class<ImmutableList<T>>) ((Class<?>) ImmutableList.class);
+ }
+
+ @Override
+ public void serialize(ImmutableList<T> list, CodedOutputStream codedOut)
+ throws SerializationException, IOException {
+ codedOut.writeInt32NoTag(list.size());
+ for (T item : list) {
+ codec.serialize(item, codedOut);
+ }
+ }
+
+ @Override
+ public ImmutableList<T> deserialize(CodedInputStream codedIn)
+ throws SerializationException, IOException {
+ int length = codedIn.readInt32();
+ if (length < 0) {
+ throw new SerializationException("Expected non-negative length: " + length);
+ }
+
+ ImmutableList.Builder<T> builder = ImmutableList.builder();
+ for (int i = 0; i < length; i++) {
+ builder.add(codec.deserialize(codedIn));
+ }
+ return builder.build();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/LabelCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/LabelCodec.java
new file mode 100644
index 0000000000..6f8881dfef
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/LabelCodec.java
@@ -0,0 +1,49 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+
+/** Custom serialization logic for {@link Label}s. */
+public class LabelCodec implements ObjectCodec<Label> {
+ public static final LabelCodec INSTANCE = new LabelCodec();
+
+ // TODO(michajlo): Share single instance of package id codec among all the codecs.
+ private final PackageIdentifierCodec packageIdCodec = new PackageIdentifierCodec();
+ private final ObjectCodec<String> stringCodec = new FastStringCodec();
+
+ @Override
+ public Class<Label> getEncodedClass() {
+ return Label.class;
+ }
+
+ @Override
+ public void serialize(Label label, CodedOutputStream codedOut)
+ throws IOException, SerializationException {
+ packageIdCodec.serialize(label.getPackageIdentifier(), codedOut);
+ stringCodec.serialize(label.getName(), codedOut);
+ }
+
+ @Override
+ public Label deserialize(CodedInputStream codedIn) throws SerializationException, IOException {
+ PackageIdentifier packageId = packageIdCodec.deserialize(codedIn);
+ String name = stringCodec.deserialize(codedIn);
+ return Label.createUnvalidated(packageId, name);
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/NotSerializableRuntimeException.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/NotSerializableRuntimeException.java
index c54bd45dd7..78dcc2576d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/NotSerializableRuntimeException.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/NotSerializableRuntimeException.java
@@ -11,7 +11,8 @@
// 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.skyframe;
+
+package com.google.devtools.build.lib.skyframe.serialization;
import java.io.Serializable;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodec.java
new file mode 100644
index 0000000000..b7009236c2
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodec.java
@@ -0,0 +1,47 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+
+/**
+ * Generic object serialization/deserialization. Implementations should serialize values
+ * deterministically.
+ */
+public interface ObjectCodec<T> extends BaseCodec<T> {
+
+ /**
+ * Serializes {@code obj}, inverse of {@link #deserialize(CodedInputStream)}.
+ *
+ * @param obj the object to serialize
+ * @param codedOut the {@link CodedOutputStream} to write this object into. Implementations need
+ * not call {@link CodedOutputStream#flush()}, this should be handled by the caller.
+ * @throws SerializationException on failure to serialize
+ * @throws IOException on {@link IOException} during serialization
+ */
+ void serialize(T obj, CodedOutputStream codedOut) throws SerializationException, IOException;
+
+ /**
+ * Deserializes from {@code codedIn}, inverse of {@link #serialize(Object, CodedOutputStream)}.
+ *
+ * @param codedIn the {@link CodedInputStream} to read the serialized object from
+ * @return the object deserialized from {@code codedIn}
+ * @throws SerializationException on failure to deserialize
+ * @throws IOException on {@link IOException} during deserialization
+ */
+ T deserialize(CodedInputStream codedIn) throws SerializationException, IOException;
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PackageIdentifierCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PackageIdentifierCodec.java
new file mode 100644
index 0000000000..73aede67b1
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PackageIdentifierCodec.java
@@ -0,0 +1,49 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.cmdline.RepositoryName;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+
+/** Custom serialization logic for {@link PackageIdentifier}s. */
+class PackageIdentifierCodec implements ObjectCodec<PackageIdentifier> {
+
+ private final RepositoryNameCodec repoNameCodec = new RepositoryNameCodec();
+ private final PathFragmentCodec pathFragmentCodec = new PathFragmentCodec();
+
+ @Override
+ public Class<PackageIdentifier> getEncodedClass() {
+ return PackageIdentifier.class;
+ }
+
+ @Override
+ public void serialize(PackageIdentifier pkgId, CodedOutputStream codedOut)
+ throws IOException, SerializationException {
+ repoNameCodec.serialize(pkgId.getRepository(), codedOut);
+ pathFragmentCodec.serialize(pkgId.getPackageFragment(), codedOut);
+ }
+
+ @Override
+ public PackageIdentifier deserialize(CodedInputStream codedIn)
+ throws IOException, SerializationException {
+ RepositoryName repoName = repoNameCodec.deserialize(codedIn);
+ PathFragment pathFragment = pathFragmentCodec.deserialize(codedIn);
+ return PackageIdentifier.create(repoName, pathFragment);
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PathFragmentCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PathFragmentCodec.java
new file mode 100644
index 0000000000..7438f6de0b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PathFragmentCodec.java
@@ -0,0 +1,55 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+
+/** Custom serialization for {@link PathFragment}s. */
+class PathFragmentCodec implements ObjectCodec<PathFragment> {
+
+ private final ObjectCodec<String> stringCodec = new FastStringCodec();
+
+ @Override
+ public Class<PathFragment> getEncodedClass() {
+ return PathFragment.class;
+ }
+
+ @Override
+ public void serialize(PathFragment pathFragment, CodedOutputStream codedOut)
+ throws IOException, SerializationException {
+ codedOut.writeInt32NoTag(pathFragment.getDriveLetter());
+ codedOut.writeBoolNoTag(pathFragment.isAbsolute());
+ codedOut.writeInt32NoTag(pathFragment.segmentCount());
+ for (int i = 0; i < pathFragment.segmentCount(); i++) {
+ stringCodec.serialize(pathFragment.getSegment(i), codedOut);
+ }
+ }
+
+ @Override
+ public PathFragment deserialize(CodedInputStream codedIn)
+ throws IOException, SerializationException {
+ char driveLetter = (char) codedIn.readInt32();
+ boolean isAbsolute = codedIn.readBool();
+ int segmentCount = codedIn.readInt32();
+ String[] segments = new String[segmentCount];
+ for (int i = 0; i < segmentCount; i++) {
+ segments[i] = stringCodec.deserialize(codedIn);
+ }
+ return PathFragment.create(driveLetter, isAbsolute, segments);
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/RepositoryNameCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/RepositoryNameCodec.java
new file mode 100644
index 0000000000..c9d0976acd
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/RepositoryNameCodec.java
@@ -0,0 +1,56 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.cmdline.RepositoryName;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+
+/** Custom serialization for {@link RepositoryName}. */
+class RepositoryNameCodec implements ObjectCodec<RepositoryName> {
+
+ @Override
+ public Class<RepositoryName> getEncodedClass() {
+ return RepositoryName.class;
+ }
+
+ @Override
+ public void serialize(RepositoryName repoName, CodedOutputStream codedOut) throws IOException {
+ boolean isMain = repoName.isMain();
+ // Main is by far the most common. Use boolean to short-circuit string encoding on
+ // serialization and byte[]/ByteString creation on deserialization.
+ codedOut.writeBoolNoTag(isMain);
+ if (!isMain) {
+ codedOut.writeStringNoTag(repoName.getName());
+ }
+ }
+
+ @Override
+ public RepositoryName deserialize(CodedInputStream codedIn)
+ throws SerializationException, IOException {
+ boolean isMain = codedIn.readBool();
+ if (isMain) {
+ return RepositoryName.MAIN;
+ }
+ try {
+ // We can read the string we wrote back as bytes to avoid string decoding/copying.
+ return SerializationCommonUtils.deserializeRepoName(codedIn.readBytes());
+ } catch (LabelSyntaxException e) {
+ throw new SerializationException("Failed to deserialize RepositoryName", e);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationCommonUtils.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationCommonUtils.java
new file mode 100644
index 0000000000..50d81b43e7
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationCommonUtils.java
@@ -0,0 +1,41 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.cmdline.RepositoryName;
+import com.google.protobuf.ByteString;
+
+/** Common utilities for serialization. */
+public class SerializationCommonUtils {
+ public static final ImmutableListCodec<String> STRING_LIST_CODEC =
+ new ImmutableListCodec<>(FastStringCodec.INSTANCE);
+ private static final ByteString DEFAULT_REPOSITORY =
+ ByteString.copyFromUtf8(RepositoryName.DEFAULT.getName());
+ private static final ByteString MAIN_REPOSITORY =
+ ByteString.copyFromUtf8(RepositoryName.MAIN.getName());
+
+ public static RepositoryName deserializeRepoName(ByteString repoNameBytes)
+ throws LabelSyntaxException {
+ // We expect MAIN_REPOSITORY the vast majority of the time, so check for it first.
+ if (repoNameBytes.equals(MAIN_REPOSITORY)) {
+ return RepositoryName.MAIN;
+ } else if (repoNameBytes.equals(DEFAULT_REPOSITORY)) {
+ return RepositoryName.DEFAULT;
+ } else {
+ return RepositoryName.create(repoNameBytes.toStringUtf8());
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationException.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationException.java
new file mode 100644
index 0000000000..1477e3a2d1
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationException.java
@@ -0,0 +1,50 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import java.io.NotSerializableException;
+
+/** Exception signaling a failure to Serialize or Deserialize an Object. */
+public class SerializationException extends Exception {
+
+ public SerializationException(String msg) {
+ super(msg);
+ }
+
+ public SerializationException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ // No SerializationException(Throwable) overload because serialization errors should always
+ // provide as much context as possible.
+
+ /**
+ * {@link SerializationException} indicating that Blaze has no serialization schema for an object
+ * or type of object.
+ */
+ public static class NoCodecException extends SerializationException {
+ NoCodecException(String message) {
+ super(message);
+ }
+
+ NoCodecException(String message, NotSerializableException e) {
+ super(message, e);
+ }
+
+ NoCodecException(String message, NotSerializableRuntimeException e) {
+ super(message, e);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/StringCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/StringCodec.java
new file mode 100644
index 0000000000..4b1bc70b7b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/StringCodec.java
@@ -0,0 +1,38 @@
+// Copyright 2017 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.skyframe.serialization;
+
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+
+/** Dead-simple serialization for {@link String}s. */
+public class StringCodec implements ObjectCodec<String> {
+
+ @Override
+ public Class<String> getEncodedClass() {
+ return String.class;
+ }
+
+ @Override
+ public void serialize(String str, CodedOutputStream codedOut) throws IOException {
+ codedOut.writeStringNoTag(str);
+ }
+
+ @Override
+ public String deserialize(CodedInputStream codedIn) throws IOException {
+ return codedIn.readString();
+ }
+}