Refactor the FileSystem API to allow for different hash functions.
Refactor the FileSystem class to include the hash function as an
instance field. This allows us to have a different hash function
per FileSystem and removes technical debt, as currently that's
somewhat accomplished by a horrible hack that has a static method
to set the hash function for all FileSystem instances.
The FileSystem's default hash function remains MD5.
RELNOTES: None
PiperOrigin-RevId: 177479772
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java
index 4c52695..96a52be 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java
@@ -37,6 +37,7 @@
*/
public static final ImmutableList<Class<? extends BlazeModule>> BAZEL_MODULES =
ImmutableList.of(
+ com.google.devtools.build.lib.runtime.BazelFileSystemModule.class,
com.google.devtools.build.lib.runtime.mobileinstall.MobileInstallModule.class,
com.google.devtools.build.lib.bazel.BazelWorkspaceStatusModule.class,
com.google.devtools.build.lib.bazel.BazelDiffAwarenessModule.class,
diff --git a/src/main/java/com/google/devtools/build/lib/remote/Chunker.java b/src/main/java/com/google/devtools/build/lib/remote/Chunker.java
index abf894e..9f14fa3 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/Chunker.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/Chunker.java
@@ -43,9 +43,6 @@
*/
public final class Chunker {
- private static final Chunk EMPTY_CHUNK =
- new Chunk(Digests.computeDigest(new byte[0]), ByteString.EMPTY, 0);
-
private static int defaultChunkSize = 1024 * 16;
/** This method must only be called in tests! */
@@ -106,6 +103,7 @@
private final Supplier<InputStream> dataSupplier;
private final Digest digest;
private final int chunkSize;
+ private final Chunk emptyChunk;
private InputStream data;
private long offset;
@@ -115,12 +113,12 @@
// lazily on the first call to next(), as opposed to opening it in the constructor or on reset().
private boolean initialized;
- public Chunker(byte[] data) throws IOException {
- this(data, getDefaultChunkSize());
+ public Chunker(byte[] data, DigestUtil digestUtil) throws IOException {
+ this(data, getDefaultChunkSize(), digestUtil);
}
- public Chunker(byte[] data, int chunkSize) throws IOException {
- this(() -> new ByteArrayInputStream(data), Digests.computeDigest(data), chunkSize);
+ public Chunker(byte[] data, int chunkSize, DigestUtil digestUtil) throws IOException {
+ this(() -> new ByteArrayInputStream(data), digestUtil.compute(data), chunkSize, digestUtil);
}
public Chunker(Path file) throws IOException {
@@ -128,38 +126,52 @@
}
public Chunker(Path file, int chunkSize) throws IOException {
- this(() -> {
- try {
- return file.getInputStream();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }, Digests.computeDigest(file), chunkSize);
+ this(
+ () -> {
+ try {
+ return file.getInputStream();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ new DigestUtil(file.getFileSystem().getDigestFunction()).compute(file),
+ chunkSize,
+ new DigestUtil(file.getFileSystem().getDigestFunction()));
}
- public Chunker(ActionInput actionInput, MetadataProvider inputCache, Path execRoot) throws
- IOException{
- this(actionInput, inputCache, execRoot, getDefaultChunkSize());
- }
-
- public Chunker(ActionInput actionInput, MetadataProvider inputCache, Path execRoot,
- int chunkSize)
+ public Chunker(
+ ActionInput actionInput, MetadataProvider inputCache, Path execRoot, DigestUtil digestUtil)
throws IOException {
- this(() -> {
- try {
- return execRoot.getRelative(actionInput.getExecPathString()).getInputStream();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }, Digests.getDigestFromInputCache(actionInput, inputCache), chunkSize);
+ this(actionInput, inputCache, execRoot, getDefaultChunkSize(), digestUtil);
+ }
+
+ public Chunker(
+ ActionInput actionInput,
+ MetadataProvider inputCache,
+ Path execRoot,
+ int chunkSize,
+ DigestUtil digestUtil)
+ throws IOException {
+ this(
+ () -> {
+ try {
+ return execRoot.getRelative(actionInput.getExecPathString()).getInputStream();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ DigestUtil.getFromInputCache(actionInput, inputCache),
+ chunkSize,
+ digestUtil);
}
@VisibleForTesting
- Chunker(Supplier<InputStream> dataSupplier, Digest digest, int chunkSize)
+ Chunker(Supplier<InputStream> dataSupplier, Digest digest, int chunkSize, DigestUtil digestUtil)
throws IOException {
this.dataSupplier = checkNotNull(dataSupplier);
this.digest = checkNotNull(digest);
this.chunkSize = chunkSize;
+ this.emptyChunk = new Chunk(digestUtil.compute(new byte[0]), ByteString.EMPTY, 0);
}
public Digest digest() {
@@ -206,7 +218,7 @@
if (digest.getSizeBytes() == 0) {
data = null;
- return EMPTY_CHUNK;
+ return emptyChunk;
}
// The cast to int is safe, because the return value is capped at chunkSize.
diff --git a/src/main/java/com/google/devtools/build/lib/remote/Digests.java b/src/main/java/com/google/devtools/build/lib/remote/DigestUtil.java
similarity index 67%
rename from src/main/java/com/google/devtools/build/lib/remote/Digests.java
rename to src/main/java/com/google/devtools/build/lib/remote/DigestUtil.java
index c3b6e0d..2ef23b5 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/Digests.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/DigestUtil.java
@@ -1,4 +1,4 @@
-// Copyright 2016 The Bazel Authors. All rights reserved.
+// 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.
@@ -11,7 +11,6 @@
// 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.remote;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -23,8 +22,7 @@
import com.google.devtools.build.lib.actions.cache.DigestUtils;
import com.google.devtools.build.lib.actions.cache.Metadata;
import com.google.devtools.build.lib.actions.cache.VirtualActionInput;
-import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
-import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.remoteexecution.v1test.Action;
import com.google.devtools.remoteexecution.v1test.Digest;
@@ -32,44 +30,12 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-/** Helper methods relating to computing Digest messages for remote execution. */
-@ThreadSafe
-public final class Digests {
- private Digests() {}
-
- public static Digest computeDigest(byte[] blob) {
- return buildDigest(
- FileSystem.getDigestFunction().getHash().hashBytes(blob).toString(), blob.length);
- }
-
- public static Digest computeDigest(Path file) throws IOException {
- long fileSize = file.getFileSize();
- byte[] digest = DigestUtils.getDigestOrFail(file, fileSize);
- return buildDigest(digest, fileSize);
- }
-
- public static Digest computeDigest(VirtualActionInput input) throws IOException {
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- input.writeTo(buffer);
- return computeDigest(buffer.toByteArray());
- }
+/** Utility methods to work with {@link Digest}. */
+public class DigestUtil {
/**
- * Computes a digest of the given proto message. Currently, we simply rely on message output as
- * bytes, but this implementation relies on the stability of the proto encoding, in particular
- * between different platforms and languages. TODO(olaola): upgrade to a better implementation!
- */
- public static Digest computeDigest(Message message) {
- return computeDigest(message.toByteArray());
- }
-
- public static Digest computeDigestUtf8(String str) {
- return computeDigest(str.getBytes(UTF_8));
- }
-
- /**
- * A special type of Digest that is used only as a remote action cache key. This is a
- * separate type in order to prevent accidentally using other Digests as action keys.
+ * A special type of Digest that is used only as a remote action cache key. This is a separate
+ * type in order to prevent accidentally using other Digests as action keys.
*/
public static final class ActionKey {
private final Digest digest;
@@ -83,16 +49,51 @@
}
}
- public static ActionKey computeActionKey(Action action) {
- return new ActionKey(computeDigest(action));
+ private final HashFunction hashFn;
+
+ public DigestUtil(HashFunction hashFn) {
+ this.hashFn = hashFn;
+ }
+
+ public Digest compute(byte[] blob) {
+ return buildDigest(hashFn.getHash().hashBytes(blob).toString(), blob.length);
+ }
+
+ public Digest compute(Path file) throws IOException {
+ long fileSize = file.getFileSize();
+ byte[] digest = DigestUtils.getDigestOrFail(file, fileSize);
+ return buildDigest(digest, fileSize);
+ }
+
+ public Digest compute(VirtualActionInput input) throws IOException {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ input.writeTo(buffer);
+ return compute(buffer.toByteArray());
}
/**
- * Assumes that the given Digest is a valid digest of an Action, and creates an ActionKey
- * wrapper. This should not be called on the client side!
+ * Computes a digest of the given proto message. Currently, we simply rely on message output as
+ * bytes, but this implementation relies on the stability of the proto encoding, in particular
+ * between different platforms and languages. TODO(olaola): upgrade to a better implementation!
*/
- public static ActionKey unsafeActionKeyFromDigest(Digest digest) {
- return new ActionKey(digest);
+ public Digest compute(Message message) {
+ return compute(message.toByteArray());
+ }
+
+ public Digest computeAsUtf8(String str) {
+ return compute(str.getBytes(UTF_8));
+ }
+
+ public DigestUtil.ActionKey computeActionKey(Action action) {
+ return new DigestUtil.ActionKey(compute(action));
+ }
+
+ /**
+ * Assumes that the given Digest is a valid digest of an Action, and creates an ActionKey wrapper.
+ * This should not be called on the client side!
+ */
+ public DigestUtil.ActionKey asActionKey(Digest digest) {
+ return new DigestUtil.ActionKey(digest);
}
public static Digest buildDigest(byte[] hash, long size) {
@@ -103,7 +104,7 @@
return Digest.newBuilder().setHash(hexHash).setSizeBytes(size).build();
}
- public static Digest getDigestFromInputCache(ActionInput input, MetadataProvider cache)
+ public static Digest getFromInputCache(ActionInput input, MetadataProvider cache)
throws IOException {
Metadata metadata = cache.getMetadata(input);
Preconditions.checkNotNull(metadata, "Input cache %s returned no value for %s", cache, input);
diff --git a/src/main/java/com/google/devtools/build/lib/remote/GrpcRemoteCache.java b/src/main/java/com/google/devtools/build/lib/remote/GrpcRemoteCache.java
index 28c04bf..5c3722e 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/GrpcRemoteCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/GrpcRemoteCache.java
@@ -29,7 +29,7 @@
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.MetadataProvider;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
@@ -71,19 +71,23 @@
private final CallCredentials credentials;
private final Channel channel;
private final Retrier retrier;
-
private final ByteStreamUploader uploader;
-
+ private final DigestUtil digestUtil;
private final ListeningScheduledExecutorService retryScheduler =
MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(1));
@VisibleForTesting
- public GrpcRemoteCache(Channel channel, CallCredentials credentials, RemoteOptions options,
- Retrier retrier) {
+ public GrpcRemoteCache(
+ Channel channel,
+ CallCredentials credentials,
+ RemoteOptions options,
+ Retrier retrier,
+ DigestUtil digestUtil) {
this.options = options;
this.credentials = credentials;
this.channel = channel;
this.retrier = retrier;
+ this.digestUtil = digestUtil;
uploader = new ByteStreamUploader(options.remoteInstanceName, channel, credentials,
options.remoteTimeout, retrier, retryScheduler);
@@ -143,7 +147,7 @@
TreeNodeRepository repository, Path execRoot, TreeNode root, Command command)
throws IOException, InterruptedException {
repository.computeMerkleDigests(root);
- Digest commandDigest = Digests.computeDigest(command);
+ Digest commandDigest = digestUtil.compute(command);
// TODO(olaola): avoid querying all the digests, only ask for novel subtrees.
ImmutableSet<Digest> missingDigests =
getMissingDigests(
@@ -158,17 +162,17 @@
repository.getDataFromDigests(missingTreeDigests, missingActionInputs, missingTreeNodes);
if (missingDigests.contains(commandDigest)) {
- toUpload.add(new Chunker(command.toByteArray()));
+ toUpload.add(new Chunker(command.toByteArray(), digestUtil));
}
if (!missingTreeNodes.isEmpty()) {
for (Directory d : missingTreeNodes) {
- toUpload.add(new Chunker(d.toByteArray()));
+ toUpload.add(new Chunker(d.toByteArray(), digestUtil));
}
}
if (!missingActionInputs.isEmpty()) {
MetadataProvider inputFileCache = repository.getInputFileCache();
for (ActionInput actionInput : missingActionInputs) {
- toUpload.add(new Chunker(actionInput, inputFileCache, execRoot));
+ toUpload.add(new Chunker(actionInput, inputFileCache, execRoot, digestUtil));
}
}
uploader.uploadBlobs(toUpload);
@@ -202,7 +206,7 @@
}
return null;
});
- Digest receivedDigest = Digests.computeDigest(path);
+ Digest receivedDigest = digestUtil.compute(path);
if (!receivedDigest.equals(digest)) {
throw new IOException(
"Digest does not match " + receivedDigest + " != " + digest);
@@ -336,7 +340,7 @@
throw new UnsupportedOperationException("Storing a directory is not yet supported.");
}
- Digest digest = Digests.computeDigest(file);
+ Digest digest = digestUtil.compute(file);
// TODO(olaola): inline small results here.
result
.addOutputFilesBuilder()
@@ -378,7 +382,7 @@
* @return The key for fetching the file contents blob from cache.
*/
private Digest uploadFileContents(Path file) throws IOException, InterruptedException {
- Digest digest = Digests.computeDigest(file);
+ Digest digest = digestUtil.compute(file);
ImmutableSet<Digest> missing = getMissingDigests(ImmutableList.of(digest));
if (!missing.isEmpty()) {
uploader.uploadBlob(new Chunker(file));
@@ -394,19 +398,19 @@
*/
Digest uploadFileContents(ActionInput input, Path execRoot, MetadataProvider inputCache)
throws IOException, InterruptedException {
- Digest digest = Digests.getDigestFromInputCache(input, inputCache);
+ Digest digest = DigestUtil.getFromInputCache(input, inputCache);
ImmutableSet<Digest> missing = getMissingDigests(ImmutableList.of(digest));
if (!missing.isEmpty()) {
- uploader.uploadBlob(new Chunker(input, inputCache, execRoot));
+ uploader.uploadBlob(new Chunker(input, inputCache, execRoot, digestUtil));
}
return digest;
}
Digest uploadBlob(byte[] blob) throws IOException, InterruptedException {
- Digest digest = Digests.computeDigest(blob);
+ Digest digest = digestUtil.compute(blob);
ImmutableSet<Digest> missing = getMissingDigests(ImmutableList.of(digest));
if (!missing.isEmpty()) {
- uploader.uploadBlob(new Chunker(blob));
+ uploader.uploadBlob(new Chunker(blob, digestUtil));
}
return digest;
}
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteActionCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteActionCache.java
index d480a93..6bc53aa 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteActionCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteActionCache.java
@@ -16,7 +16,7 @@
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.Path;
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteActionContextProvider.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteActionContextProvider.java
index 46ef89f..c66015a 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteActionContextProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteActionContextProvider.java
@@ -36,12 +36,17 @@
private final CommandEnvironment env;
private final RemoteActionCache cache;
private final GrpcRemoteExecutor executor;
+ private final DigestUtil digestUtil;
- RemoteActionContextProvider(CommandEnvironment env, @Nullable RemoteActionCache cache,
- @Nullable GrpcRemoteExecutor executor) {
+ RemoteActionContextProvider(
+ CommandEnvironment env,
+ @Nullable RemoteActionCache cache,
+ @Nullable GrpcRemoteExecutor executor,
+ DigestUtil digestUtil) {
this.env = env;
this.executor = executor;
this.cache = cache;
+ this.digestUtil = digestUtil;
}
@Override
@@ -61,7 +66,8 @@
buildRequestId,
commandId,
executionOptions.verboseFailures,
- env.getReporter());
+ env.getReporter(),
+ digestUtil);
return ImmutableList.of(spawnCache);
} else {
RemoteSpawnRunner spawnRunner =
@@ -74,7 +80,8 @@
buildRequestId,
commandId,
cache,
- executor);
+ executor,
+ digestUtil);
return ImmutableList.of(new RemoteSpawnStrategy(spawnRunner));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java
index 702d7a6..c923a4d 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java
@@ -28,6 +28,7 @@
import com.google.devtools.build.lib.runtime.ServerBuilder;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.ExitCode;
+import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsProvider;
@@ -50,16 +51,17 @@
// TODO(ulfjack): Change the Bazel startup process to make the options available when we create
// the PathConverter.
RemoteOptions options;
+ DigestUtil digestUtil;
@Override
public String apply(Path path) {
- if (options == null || !remoteEnabled(options)) {
+ if (options == null || digestUtil == null || !remoteEnabled(options)) {
return null;
}
String server = options.remoteCache;
String remoteInstanceName = options.remoteInstanceName;
try {
- Digest digest = Digests.computeDigest(path);
+ Digest digest = digestUtil.compute(path);
return remoteInstanceName.isEmpty()
? String.format(
"bytestream://%s/blobs/%s/%d",
@@ -97,13 +99,17 @@
logger.info("Command: buildRequestId = " + buildRequestId + ", commandId = " + commandId);
RemoteOptions remoteOptions = env.getOptions().getOptions(RemoteOptions.class);
AuthAndTLSOptions authAndTlsOptions = env.getOptions().getOptions(AuthAndTLSOptions.class);
+ HashFunction hashFn = env.getRuntime().getFileSystem().getDigestFunction();
+ DigestUtil digestUtil = new DigestUtil(hashFn);
converter.options = remoteOptions;
+ converter.digestUtil = digestUtil;
// Quit if no remote options specified.
if (remoteOptions == null) {
return;
}
+
try {
boolean remoteOrLocalCache = SimpleBlobStoreFactory.isRemoteCacheOptions(remoteOptions);
boolean grpcCache = GrpcRemoteCache.isRemoteCacheOptions(remoteOptions);
@@ -116,12 +122,13 @@
if (remoteOrLocalCache) {
cache =
new SimpleBlobStoreActionCache(
- SimpleBlobStoreFactory.create(remoteOptions, env.getWorkingDirectory()));
+ SimpleBlobStoreFactory.create(remoteOptions, env.getWorkingDirectory()),
+ digestUtil);
} else if (grpcCache || remoteOptions.remoteExecutor != null) {
// If a remote executor but no remote cache is specified, assume both at the same target.
String target = grpcCache ? remoteOptions.remoteCache : remoteOptions.remoteExecutor;
Channel ch = GrpcUtils.newChannel(target, authAndTlsOptions);
- cache = new GrpcRemoteCache(ch, creds, remoteOptions, retrier);
+ cache = new GrpcRemoteCache(ch, creds, remoteOptions, retrier, digestUtil);
} else {
cache = null;
}
@@ -137,7 +144,7 @@
executor = null;
}
- actionContextProvider = new RemoteActionContextProvider(env, cache, executor);
+ actionContextProvider = new RemoteActionContextProvider(env, cache, executor, digestUtil);
} catch (IOException e) {
env.getReporter().handle(Event.error(e.getMessage()));
env.getBlazeModuleEnvironment().exit(new AbruptExitException(ExitCode.COMMAND_LINE_ERROR));
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
index 4a6ba9c..57f19fa 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
@@ -24,7 +24,7 @@
import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.exec.SpawnCache;
import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionPolicy;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -64,6 +64,8 @@
// Used to ensure that a warning is reported only once.
private final AtomicBoolean warningReported = new AtomicBoolean();
+ private final DigestUtil digestUtil;
+
RemoteSpawnCache(
Path execRoot,
RemoteOptions options,
@@ -71,7 +73,8 @@
String buildRequestId,
String commandId,
boolean verboseFailures,
- @Nullable Reporter cmdlineReporter) {
+ @Nullable Reporter cmdlineReporter,
+ DigestUtil digestUtil) {
this.execRoot = execRoot;
this.options = options;
this.platform = options.parseRemotePlatformOverride();
@@ -80,6 +83,7 @@
this.cmdlineReporter = cmdlineReporter;
this.buildRequestId = buildRequestId;
this.commandId = commandId;
+ this.digestUtil = digestUtil;
}
@Override
@@ -87,7 +91,7 @@
throws InterruptedException, IOException, ExecException {
// Temporary hack: the TreeNodeRepository should be created and maintained upstream!
TreeNodeRepository repository =
- new TreeNodeRepository(execRoot, policy.getActionInputFileCache());
+ new TreeNodeRepository(execRoot, policy.getActionInputFileCache(), digestUtil);
SortedMap<PathFragment, ActionInput> inputMap = policy.getInputMapping();
TreeNode inputRoot = repository.buildFromActionInputs(inputMap);
repository.computeMerkleDigests(inputRoot);
@@ -95,13 +99,13 @@
Action action =
RemoteSpawnRunner.buildAction(
spawn.getOutputFiles(),
- Digests.computeDigest(command),
+ digestUtil.compute(command),
repository.getMerkleDigest(inputRoot),
platform,
policy.getTimeout());
// Look up action cache, and reuse the action output if it is found.
- final ActionKey actionKey = Digests.computeActionKey(action);
+ final ActionKey actionKey = digestUtil.computeActionKey(action);
Context withMetadata =
TracingMetadataUtils.contextWithMetadata(buildRequestId, commandId, actionKey);
// Metadata will be available in context.current() until we detach.
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
index 7672539..3a1b474 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
@@ -32,7 +32,7 @@
import com.google.devtools.build.lib.exec.SpawnExecException;
import com.google.devtools.build.lib.exec.SpawnInputExpander;
import com.google.devtools.build.lib.exec.SpawnRunner;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.io.FileOutErr;
@@ -79,6 +79,7 @@
@Nullable private final GrpcRemoteExecutor remoteExecutor;
private final String buildRequestId;
private final String commandId;
+ private final DigestUtil digestUtil;
// Used to ensure that a warning is reported only once.
private final AtomicBoolean warningReported = new AtomicBoolean();
@@ -92,7 +93,8 @@
String buildRequestId,
String commandId,
@Nullable RemoteActionCache remoteCache,
- @Nullable GrpcRemoteExecutor remoteExecutor) {
+ @Nullable GrpcRemoteExecutor remoteExecutor,
+ DigestUtil digestUtil) {
this.execRoot = execRoot;
this.options = options;
this.platform = options.parseRemotePlatformOverride();
@@ -103,6 +105,7 @@
this.cmdlineReporter = cmdlineReporter;
this.buildRequestId = buildRequestId;
this.commandId = commandId;
+ this.digestUtil = digestUtil;
}
@Override
@@ -115,7 +118,7 @@
policy.report(ProgressStatus.EXECUTING, "remote");
// Temporary hack: the TreeNodeRepository should be created and maintained upstream!
ActionInputFileCache inputFileCache = policy.getActionInputFileCache();
- TreeNodeRepository repository = new TreeNodeRepository(execRoot, inputFileCache);
+ TreeNodeRepository repository = new TreeNodeRepository(execRoot, inputFileCache, digestUtil);
SortedMap<PathFragment, ActionInput> inputMap = policy.getInputMapping();
TreeNode inputRoot = repository.buildFromActionInputs(inputMap);
repository.computeMerkleDigests(inputRoot);
@@ -123,13 +126,13 @@
Action action =
buildAction(
spawn.getOutputFiles(),
- Digests.computeDigest(command),
+ digestUtil.compute(command),
repository.getMerkleDigest(inputRoot),
platform,
policy.getTimeout());
// Look up action cache, and reuse the action output if it is found.
- ActionKey actionKey = Digests.computeActionKey(action);
+ ActionKey actionKey = digestUtil.computeActionKey(action);
Context withMetadata =
TracingMetadataUtils.contextWithMetadata(buildRequestId, commandId, actionKey);
Context previous = withMetadata.attach();
diff --git a/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreActionCache.java b/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreActionCache.java
index af7920e..e5d32e4 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreActionCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreActionCache.java
@@ -20,7 +20,7 @@
import com.google.devtools.build.lib.actions.MetadataProvider;
import com.google.devtools.build.lib.actions.cache.VirtualActionInput;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
import com.google.devtools.build.lib.remote.blobstore.SimpleBlobStore;
import com.google.devtools.build.lib.util.io.FileOutErr;
@@ -55,9 +55,11 @@
private static final int MAX_BLOB_SIZE_FOR_INLINE = 10 * 1024;
private final SimpleBlobStore blobStore;
+ private final DigestUtil digestUtil;
- public SimpleBlobStoreActionCache(SimpleBlobStore blobStore) {
+ public SimpleBlobStoreActionCache(SimpleBlobStore blobStore, DigestUtil digestUtil) {
this.blobStore = blobStore;
+ this.digestUtil = digestUtil;
}
@Override
@@ -88,7 +90,7 @@
}
private Digest uploadFileContents(Path file) throws IOException, InterruptedException {
- Digest digest = Digests.computeDigest(file);
+ Digest digest = digestUtil.compute(file);
try (InputStream in = file.getInputStream()) {
return uploadStream(digest, in);
}
@@ -99,10 +101,10 @@
throws IOException, InterruptedException {
if (input instanceof VirtualActionInput) {
byte[] blob = ((VirtualActionInput) input).getBytes().toByteArray();
- return uploadBlob(blob, Digests.computeDigest(blob));
+ return uploadBlob(blob, digestUtil.compute(blob));
}
try (InputStream in = execRoot.getRelative(input.getExecPathString()).getInputStream()) {
- return uploadStream(Digests.getDigestFromInputCache(input, inputCache), in);
+ return uploadStream(DigestUtil.getFromInputCache(input, inputCache), in);
}
}
@@ -243,7 +245,7 @@
}
public Digest uploadBlob(byte[] blob) throws IOException, InterruptedException {
- return uploadBlob(blob, Digests.computeDigest(blob));
+ return uploadBlob(blob, digestUtil.compute(blob));
}
private Digest uploadBlob(byte[] blob, Digest digest) throws IOException, InterruptedException {
diff --git a/src/main/java/com/google/devtools/build/lib/remote/TracingMetadataUtils.java b/src/main/java/com/google/devtools/build/lib/remote/TracingMetadataUtils.java
index 18da622..a795861 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/TracingMetadataUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/TracingMetadataUtils.java
@@ -15,7 +15,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.remoteexecution.v1test.RequestMetadata;
import com.google.devtools.remoteexecution.v1test.ToolDetails;
import io.grpc.ClientInterceptor;
diff --git a/src/main/java/com/google/devtools/build/lib/remote/TreeNodeRepository.java b/src/main/java/com/google/devtools/build/lib/remote/TreeNodeRepository.java
index 4eac48e..0554682 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/TreeNodeRepository.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/TreeNodeRepository.java
@@ -189,10 +189,13 @@
private final Map<TreeNode, Directory> directoryCache = new HashMap<>();
private final Map<VirtualActionInput, Digest> virtualInputDigestCache = new HashMap<>();
private final Map<Digest, VirtualActionInput> digestVirtualInputCache = new HashMap<>();
+ private final DigestUtil digestUtil;
- public TreeNodeRepository(Path execRoot, ActionInputFileCache inputFileCache) {
+ public TreeNodeRepository(
+ Path execRoot, ActionInputFileCache inputFileCache, DigestUtil digestUtil) {
this.execRoot = execRoot;
this.inputFileCache = inputFileCache;
+ this.digestUtil = digestUtil;
}
public ActionInputFileCache getInputFileCache() {
@@ -302,7 +305,7 @@
ActionInput input = child.getActionInput();
if (input instanceof VirtualActionInput) {
VirtualActionInput virtualInput = (VirtualActionInput) input;
- Digest digest = Digests.computeDigest(virtualInput);
+ Digest digest = digestUtil.compute(virtualInput);
virtualInputDigestCache.put(virtualInput, digest);
// There may be multiple inputs with the same digest. In that case, we don't care which
// one we get back from the digestVirtualInputCache later.
@@ -314,7 +317,7 @@
} else {
b.addFilesBuilder()
.setName(entry.getSegment())
- .setDigest(Digests.getDigestFromInputCache(input, inputFileCache))
+ .setDigest(DigestUtil.getFromInputCache(input, inputFileCache))
.setIsExecutable(execRoot.getRelative(input.getExecPathString()).isExecutable());
}
} else {
@@ -324,7 +327,7 @@
}
directory = b.build();
directoryCache.put(node, directory);
- Digest digest = Digests.computeDigest(directory);
+ Digest digest = digestUtil.compute(directory);
treeNodeDigestCache.put(node, digest);
digestTreeNodeCache.put(digest, node);
}
@@ -377,7 +380,7 @@
if (input instanceof VirtualActionInput) {
return Preconditions.checkNotNull(virtualInputDigestCache.get(input));
}
- return Digests.getDigestFromInputCache(input, inputFileCache);
+ return DigestUtil.getFromInputCache(input, inputFileCache);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BazelFileSystemModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BazelFileSystemModule.java
new file mode 100644
index 0000000..d7d6b0a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BazelFileSystemModule.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.runtime;
+
+import com.google.devtools.build.lib.unix.UnixFileSystem;
+import com.google.devtools.build.lib.util.AbruptExitException;
+import com.google.devtools.build.lib.util.ExitCode;
+import com.google.devtools.build.lib.util.OS;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
+import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
+import com.google.devtools.build.lib.windows.WindowsFileSystem;
+import com.google.devtools.common.options.OptionsParsingException;
+import com.google.devtools.common.options.OptionsProvider;
+
+/**
+ * Module to provide a {@link FileSystem} instance that uses {@code SHA256} as the default hash
+ * function, or else what's specified by {@code -Dbazel.DigestFunction}.
+ *
+ * <p>For legacy reasons we can't make the {@link FileSystem} class use {@code SHA256} by default.
+ */
+public class BazelFileSystemModule extends BlazeModule {
+
+ @Override
+ public FileSystem getFileSystem(OptionsProvider startupOptions) throws AbruptExitException {
+ final HashFunction hashFunction;
+ String value = null;
+ try {
+ value = System.getProperty("bazel.DigestFunction", "MD5");
+ hashFunction = new HashFunction.Converter().convert(value);
+ } catch (OptionsParsingException e) {
+ throw new AbruptExitException(
+ "The specified hash function '" + value + "' is not supported.",
+ ExitCode.COMMAND_LINE_ERROR,
+ e);
+ }
+ if ("0".equals(System.getProperty("io.bazel.EnableJni"))) {
+ // Ignore UnixFileSystem, to be used for bootstrapping.
+ return OS.getCurrent() == OS.WINDOWS
+ ? new WindowsFileSystem(hashFunction)
+ : new JavaIoFileSystem(hashFunction);
+ }
+ // The JNI-based UnixFileSystem is faster, but on Windows it is not available.
+ return OS.getCurrent() == OS.WINDOWS
+ ? new WindowsFileSystem(hashFunction)
+ : new UnixFileSystem(hashFunction);
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java
index 65dccd9..4eb1908 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java
@@ -80,7 +80,7 @@
*
* @param startupOptions the server's startup options
*/
- public FileSystem getFileSystem(OptionsProvider startupOptions) {
+ public FileSystem getFileSystem(OptionsProvider startupOptions) throws AbruptExitException {
return null;
}
diff --git a/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java b/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java
index 7247680..1a13b46 100644
--- a/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java
@@ -36,9 +36,14 @@
*/
@ThreadSafe
public class UnixFileSystem extends AbstractFileSystemWithCustomStat {
+
public UnixFileSystem() {
}
+ public UnixFileSystem(HashFunction hashFunction) {
+ super(hashFunction);
+ }
+
/**
* Eager implementation of FileStatus for file systems that have an atomic
* stat(2) syscall. A proxy for {@link com.google.devtools.build.lib.unix.FileStatus}.
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java
index a03ec02..ef1946b 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java
@@ -30,6 +30,12 @@
protected static final String ERR_PERMISSION_DENIED = " (Permission denied)";
protected static final Profiler profiler = Profiler.instance();
+ public AbstractFileSystem() {}
+
+ public AbstractFileSystem(HashFunction digestFunction) {
+ super(digestFunction);
+ }
+
@Override
protected InputStream getInputStream(Path path) throws IOException {
// This loop is a workaround for an apparent bug in FileInputStream.open, which delegates
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystemWithCustomStat.java b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystemWithCustomStat.java
index db4fca9..875df98 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystemWithCustomStat.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystemWithCustomStat.java
@@ -21,6 +21,13 @@
* {@link FileSystem} does).
*/
public abstract class AbstractFileSystemWithCustomStat extends AbstractFileSystem {
+
+ public AbstractFileSystemWithCustomStat() {}
+
+ public AbstractFileSystemWithCustomStat(HashFunction hashFunction) {
+ super(hashFunction);
+ }
+
@Override
protected boolean isFile(Path path, boolean followSymlinks) {
FileStatus stat = statNullable(path, followSymlinks);
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
index fe707ea..6386b73 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
@@ -16,7 +16,7 @@
import static java.nio.charset.StandardCharsets.ISO_8859_1;
-import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteSource;
@@ -25,7 +25,6 @@
import com.google.devtools.build.lib.vfs.Dirent.Type;
import com.google.devtools.build.lib.vfs.Path.PathFactory;
import com.google.devtools.common.options.EnumConverter;
-import com.google.devtools.common.options.OptionsParsingException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -71,23 +70,17 @@
}
}
- // This is effectively final, should be changed only in unit-tests!
- private static HashFunction digestFunction;
- static {
- try {
- digestFunction = new HashFunction.Converter().convert(
- System.getProperty("bazel.DigestFunction", "MD5"));
- } catch (OptionsParsingException e) {
- throw new IllegalStateException(e);
- }
+ private final HashFunction digestFunction;
+
+ public FileSystem() {
+ this(HashFunction.MD5);
}
- @VisibleForTesting
- public static void setDigestFunctionForTesting(HashFunction value) {
- digestFunction = value;
+ public FileSystem(HashFunction digestFunction) {
+ this.digestFunction = Preconditions.checkNotNull(digestFunction);
}
- public static HashFunction getDigestFunction() {
+ public HashFunction getDigestFunction() {
return digestFunction;
}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java
index 73074b2..e5ca35d 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java
@@ -56,6 +56,11 @@
this(new JavaClock());
}
+ public JavaIoFileSystem(HashFunction hashFunction) {
+ super(hashFunction);
+ this.clock = new JavaClock();
+ }
+
@VisibleForTesting
JavaIoFileSystem(Clock clock) {
this.clock = clock;
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java
index d7b0960..7e4d660 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java
@@ -70,7 +70,18 @@
* paths are considered to be within scope).
*/
public InMemoryFileSystem(Clock clock) {
- this(clock, null);
+ this(clock, (PathFragment) null);
+ }
+
+ /**
+ * Creates a new InMemoryFileSystem with scope checking disabled (all paths are considered to be
+ * within scope).
+ */
+ public InMemoryFileSystem(Clock clock, HashFunction hashFunction) {
+ super(hashFunction);
+ this.clock = clock;
+ this.rootInode = newRootInode(clock);
+ this.scopeRoot = null;
}
/**
@@ -80,9 +91,14 @@
public InMemoryFileSystem(Clock clock, PathFragment scopeRoot) {
this.scopeRoot = scopeRoot;
this.clock = clock;
- this.rootInode = new InMemoryDirectoryInfo(clock);
+ this.rootInode = newRootInode(clock);
+ }
+
+ private static InMemoryDirectoryInfo newRootInode(Clock clock) {
+ InMemoryDirectoryInfo rootInode = new InMemoryDirectoryInfo(clock);
rootInode.addChild(".", rootInode);
rootInode.addChild("..", rootInode);
+ return rootInode;
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
index dfbb8ca..21de058 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
@@ -308,6 +308,12 @@
public static final LinkOption[] NO_OPTIONS = new LinkOption[0];
public static final LinkOption[] NO_FOLLOW = new LinkOption[] {LinkOption.NOFOLLOW_LINKS};
+ public WindowsFileSystem() {}
+
+ public WindowsFileSystem(HashFunction hashFunction) {
+ super(hashFunction);
+ }
+
@Override
protected PathFactory getPathFactory() {
return WindowsPathFactory.INSTANCE;
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 90cb706..4cb175a 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -1067,6 +1067,7 @@
"//src/main/java/com/google/devtools/build/lib:io",
"//src/main/java/com/google/devtools/build/lib/actions",
"//src/main/java/com/google/devtools/build/lib/authandtls",
+ "//src/main/java/com/google/devtools/build/lib/clock",
"//src/main/java/com/google/devtools/build/lib/remote",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs",
diff --git a/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java b/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
index fddab806..259e0f2 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
@@ -55,28 +55,28 @@
final CountDownLatch barrierLatch = new CountDownLatch(2); // Used to block test threads.
final CountDownLatch readyLatch = new CountDownLatch(1); // Used to block main thread.
- FileSystem myfs = new InMemoryFileSystem(BlazeClock.instance()) {
- @Override
- protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException {
- try {
- barrierLatch.countDown();
- readyLatch.countDown();
- // Either both threads will be inside getDigest at the same time or they
- // both will be blocked.
- barrierLatch.await();
- } catch (Exception e) {
- throw new IOException(e);
+ FileSystem myfs =
+ new InMemoryFileSystem(BlazeClock.instance(), hf) {
+ @Override
+ protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException {
+ try {
+ barrierLatch.countDown();
+ readyLatch.countDown();
+ // Either both threads will be inside getDigest at the same time or they
+ // both will be blocked.
+ barrierLatch.await();
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ return super.getDigest(path, hashFunction);
}
- return super.getDigest(path, hashFunction);
- }
- @Override
- protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
- return fastDigest ? super.getDigest(path, hashFunction) : null;
- }
- };
+ @Override
+ protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
+ return fastDigest ? super.getDigest(path, hashFunction) : null;
+ }
+ };
- FileSystem.setDigestFunctionForTesting(hf);
final Path myFile1 = myfs.getPath("/f1.dat");
final Path myFile2 = myfs.getPath("/f2.dat");
FileSystemUtils.writeContentAsLatin1(myFile1, Strings.repeat("a", fileSize1));
@@ -126,18 +126,19 @@
}
public void assertRecoverFromMalformedDigest(HashFunction... hashFunctions) throws Exception {
- final byte[] malformed = {0, 0, 0};
- FileSystem myFS = new InMemoryFileSystem(BlazeClock.instance()) {
- @Override
- protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
- // Digest functions have more than 3 bytes, usually at least 16.
- return malformed;
- }
- };
- Path path = myFS.getPath("/file");
- FileSystemUtils.writeContentAsLatin1(path, "a");
for (HashFunction hf : hashFunctions) {
- FileSystem.setDigestFunctionForTesting(hf);
+ final byte[] malformed = {0, 0, 0};
+ FileSystem myFS =
+ new InMemoryFileSystem(BlazeClock.instance(), hf) {
+ @Override
+ protected byte[] getFastDigest(Path path, HashFunction hashFunction)
+ throws IOException {
+ // Digest functions have more than 3 bytes, usually at least 16.
+ return malformed;
+ }
+ };
+ Path path = myFS.getPath("/file");
+ FileSystemUtils.writeContentAsLatin1(path, "a");
byte[] result = DigestUtils.getDigestOrFail(path, 1);
assertThat(result).isEqualTo(path.getDigest());
assertThat(result).isNotSameAs(malformed);
diff --git a/src/test/java/com/google/devtools/build/lib/remote/ByteStreamUploaderTest.java b/src/test/java/com/google/devtools/build/lib/remote/ByteStreamUploaderTest.java
index e809b74..0b70f36 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/ByteStreamUploaderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/ByteStreamUploaderTest.java
@@ -26,6 +26,7 @@
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
+import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.remoteexecution.v1test.Digest;
import com.google.devtools.remoteexecution.v1test.RequestMetadata;
import com.google.protobuf.ByteString;
@@ -75,6 +76,8 @@
@RunWith(JUnit4.class)
public class ByteStreamUploaderTest {
+ private static final DigestUtil DIGEST_UTIL = new DigestUtil(HashFunction.SHA256);
+
private static final int CHUNK_SIZE = 10;
private static final String INSTANCE_NAME = "foo";
@@ -99,7 +102,7 @@
channel = InProcessChannelBuilder.forName(serverName).build();
withEmptyMetadata =
TracingMetadataUtils.contextWithMetadata(
- "none", "none", Digests.unsafeActionKeyFromDigest(Digest.getDefaultInstance()));
+ "none", "none", DIGEST_UTIL.asActionKey(Digest.getDefaultInstance()));
// Needs to be repeated in every test that uses the timeout setting, since the tests run
// on different threads than the setUp.
withEmptyMetadata.attach();
@@ -121,7 +124,7 @@
byte[] blob = new byte[CHUNK_SIZE * 2 + 1];
new Random().nextBytes(blob);
- Chunker chunker = new Chunker(blob, CHUNK_SIZE);
+ Chunker chunker = new Chunker(blob, CHUNK_SIZE, DIGEST_UTIL);
serviceRegistry.addService(new ByteStreamImplBase() {
@Override
@@ -195,7 +198,7 @@
int blobSize = rand.nextInt(CHUNK_SIZE * 10) + CHUNK_SIZE;
byte[] blob = new byte[blobSize];
rand.nextBytes(blob);
- Chunker chunker = new Chunker(blob, CHUNK_SIZE);
+ Chunker chunker = new Chunker(blob, CHUNK_SIZE, DIGEST_UTIL);
builders.add(chunker);
blobsByHash.put(chunker.digest().getHash(), blob);
}
@@ -281,7 +284,7 @@
List<Chunker> builders = new ArrayList<>(toUpload.size());
Map<String, Integer> uploadsFailed = new HashMap<>();
for (String s : toUpload) {
- Chunker chunker = new Chunker(s.getBytes(UTF_8), /* chunkSize=*/ 3);
+ Chunker chunker = new Chunker(s.getBytes(UTF_8), /* chunkSize=*/ 3, DIGEST_UTIL);
builders.add(chunker);
uploadsFailed.put(chunker.digest().getHash(), 0);
}
@@ -342,7 +345,7 @@
for (Chunker chunker : builders) {
Context ctx =
TracingMetadataUtils.contextWithMetadata(
- "build-req-id", "command-id", Digests.unsafeActionKeyFromDigest(chunker.digest()));
+ "build-req-id", "command-id", DIGEST_UTIL.asActionKey(chunker.digest()));
ctx.call(
() -> {
uploads.add(uploader.uploadBlobAsync(chunker));
@@ -367,7 +370,7 @@
new ByteStreamUploader(INSTANCE_NAME, channel, null, 3, retrier, retryService);
byte[] blob = new byte[CHUNK_SIZE * 10];
- Chunker chunker = new Chunker(blob, CHUNK_SIZE);
+ Chunker chunker = new Chunker(blob, CHUNK_SIZE, DIGEST_UTIL);
AtomicInteger numWriteCalls = new AtomicInteger();
CountDownLatch blocker = new CountDownLatch(1);
@@ -426,7 +429,7 @@
new ByteStreamUploader(INSTANCE_NAME, channel, null, 3, retrier, retryService);
byte[] blob = new byte[CHUNK_SIZE];
- Chunker chunker = new Chunker(blob, CHUNK_SIZE);
+ Chunker chunker = new Chunker(blob, CHUNK_SIZE, DIGEST_UTIL);
serviceRegistry.addService(new ByteStreamImplBase() {
@Override
@@ -477,10 +480,10 @@
serviceRegistry.addService(service);
byte[] blob1 = new byte[CHUNK_SIZE];
- Chunker chunker1 = new Chunker(blob1, CHUNK_SIZE);
+ Chunker chunker1 = new Chunker(blob1, CHUNK_SIZE, DIGEST_UTIL);
byte[] blob2 = new byte[CHUNK_SIZE + 1];
- Chunker chunker2 = new Chunker(blob2, CHUNK_SIZE);
+ Chunker chunker2 = new Chunker(blob2, CHUNK_SIZE, DIGEST_UTIL);
ListenableFuture<Void> f1 = uploader.uploadBlobAsync(chunker1);
ListenableFuture<Void> f2 = uploader.uploadBlobAsync(chunker2);
@@ -519,7 +522,7 @@
assertThat(retryService.isShutdown()).isTrue();
byte[] blob = new byte[1];
- Chunker chunker = new Chunker(blob, CHUNK_SIZE);
+ Chunker chunker = new Chunker(blob, CHUNK_SIZE, DIGEST_UTIL);
try {
uploader.uploadBlob(chunker);
fail("Should have thrown an exception.");
@@ -560,7 +563,7 @@
});
byte[] blob = new byte[1];
- Chunker chunker = new Chunker(blob, CHUNK_SIZE);
+ Chunker chunker = new Chunker(blob, CHUNK_SIZE, DIGEST_UTIL);
uploader.uploadBlob(chunker);
}
@@ -585,7 +588,7 @@
});
byte[] blob = new byte[1];
- Chunker chunker = new Chunker(blob, CHUNK_SIZE);
+ Chunker chunker = new Chunker(blob, CHUNK_SIZE, DIGEST_UTIL);
try {
uploader.uploadBlob(chunker);
diff --git a/src/test/java/com/google/devtools/build/lib/remote/CasPathConverterTest.java b/src/test/java/com/google/devtools/build/lib/remote/CasPathConverterTest.java
index 5ba4ef0..493041b 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/CasPathConverterTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/CasPathConverterTest.java
@@ -17,6 +17,7 @@
import com.google.devtools.build.lib.remote.RemoteModule.CasPathConverter;
import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
@@ -34,12 +35,20 @@
@Test
public void noOptionsShouldntCrash() {
+ converter.digestUtil = new DigestUtil(HashFunction.SHA256);
+ assertThat(converter.apply(fs.getPath("/foo"))).isNull();
+ }
+
+ @Test
+ public void noDigestUtilShouldntCrash() {
+ converter.options = Options.getDefaults(RemoteOptions.class);
assertThat(converter.apply(fs.getPath("/foo"))).isNull();
}
@Test
public void disabledRemote() {
converter.options = Options.getDefaults(RemoteOptions.class);
+ converter.digestUtil = new DigestUtil(HashFunction.SHA256);
assertThat(converter.apply(fs.getPath("/foo"))).isNull();
}
@@ -48,6 +57,7 @@
OptionsParser parser = OptionsParser.newOptionsParser(RemoteOptions.class);
parser.parse("--remote_cache=machine");
converter.options = parser.getOptions(RemoteOptions.class);
+ converter.digestUtil = new DigestUtil(HashFunction.SHA256);
Path path = fs.getPath("/foo");
FileSystemUtils.writeContentAsLatin1(path, "foobar");
assertThat(converter.apply(fs.getPath("/foo")))
@@ -59,6 +69,7 @@
OptionsParser parser = OptionsParser.newOptionsParser(RemoteOptions.class);
parser.parse("--remote_cache=machine", "--remote_instance_name=projects/bazel");
converter.options = parser.getOptions(RemoteOptions.class);
+ converter.digestUtil = new DigestUtil(HashFunction.SHA256);
Path path = fs.getPath("/foo");
FileSystemUtils.writeContentAsLatin1(path, "foobar");
assertThat(converter.apply(fs.getPath("/foo")))
diff --git a/src/test/java/com/google/devtools/build/lib/remote/ChunkerTest.java b/src/test/java/com/google/devtools/build/lib/remote/ChunkerTest.java
index fd0cbad..ed1ccf6 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/ChunkerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/ChunkerTest.java
@@ -17,6 +17,7 @@
import static junit.framework.TestCase.fail;
import com.google.devtools.build.lib.remote.Chunker.Chunk;
+import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.remoteexecution.v1test.Digest;
import com.google.protobuf.ByteString;
import java.io.ByteArrayInputStream;
@@ -36,14 +37,16 @@
@RunWith(JUnit4.class)
public class ChunkerTest {
+ private final DigestUtil digestUtil = new DigestUtil(HashFunction.SHA256);
+
@Test
public void chunkingShouldWork() throws IOException {
Random rand = new Random();
byte[] expectedData = new byte[21];
rand.nextBytes(expectedData);
- Digest expectedDigest = Digests.computeDigest(expectedData);
+ Digest expectedDigest = digestUtil.compute(expectedData);
- Chunker chunker = new Chunker(expectedData, 10);
+ Chunker chunker = new Chunker(expectedData, 10, digestUtil);
ByteArrayOutputStream actualData = new ByteArrayOutputStream();
@@ -76,7 +79,7 @@
@Test
public void nextShouldThrowIfNoMoreData() throws IOException {
byte[] data = new byte[10];
- Chunker chunker = new Chunker(data, 10);
+ Chunker chunker = new Chunker(data, 10, digestUtil);
assertThat(chunker.hasNext()).isTrue();
assertThat(chunker.next()).isNotNull();
@@ -94,7 +97,7 @@
@Test
public void emptyData() throws Exception {
byte[] data = new byte[0];
- Chunker chunker = new Chunker(data);
+ Chunker chunker = new Chunker(data, digestUtil);
assertThat(chunker.hasNext()).isTrue();
@@ -117,7 +120,7 @@
@Test
public void reset() throws Exception {
byte[] data = new byte[]{1, 2, 3};
- Chunker chunker = new Chunker(data, 1);
+ Chunker chunker = new Chunker(data, 1, digestUtil);
assertNextEquals(chunker, (byte) 1);
assertNextEquals(chunker, (byte) 2);
@@ -144,9 +147,9 @@
in.set(Mockito.spy(new ByteArrayInputStream(data)));
return in.get();
};
- Digest digest = Digests.computeDigest(data);
+ Digest digest = digestUtil.compute(data);
- Chunker chunker = new Chunker(supplier, digest, 1);
+ Chunker chunker = new Chunker(supplier, digest, 1, digestUtil);
assertThat(in.get()).isNull();
assertNextEquals(chunker, (byte) 1);
Mockito.verify(in.get(), Mockito.never()).close();
diff --git a/src/test/java/com/google/devtools/build/lib/remote/FakeActionInputFileCache.java b/src/test/java/com/google/devtools/build/lib/remote/FakeActionInputFileCache.java
index 565e5e9..3f50968 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/FakeActionInputFileCache.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/FakeActionInputFileCache.java
@@ -32,9 +32,11 @@
final class FakeActionInputFileCache implements ActionInputFileCache {
private final Path execRoot;
private final BiMap<ActionInput, String> cas = HashBiMap.create();
+ private final DigestUtil digestUtil;
FakeActionInputFileCache(Path execRoot) {
this.execRoot = execRoot;
+ this.digestUtil = new DigestUtil(execRoot.getFileSystem().getDigestFunction());
}
@Override
@@ -70,7 +72,7 @@
Path inputFile = execRoot.getRelative(input.getExecPath());
FileSystemUtils.createDirectoryAndParents(inputFile.getParentDirectory());
FileSystemUtils.writeContentAsLatin1(inputFile, content);
- Digest digest = Digests.computeDigest(inputFile);
+ Digest digest = digestUtil.compute(inputFile);
setDigest(input, digest.getHash());
return digest;
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteCacheTest.java b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteCacheTest.java
index 5cc03be..5664ea3 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteCacheTest.java
@@ -29,7 +29,8 @@
import com.google.devtools.build.lib.actions.ActionInputHelper;
import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions;
import com.google.devtools.build.lib.authandtls.GrpcUtils;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.clock.JavaClock;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.testutil.Scratch;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.FileSystem;
@@ -75,6 +76,9 @@
/** Tests for {@link GrpcRemoteCache}. */
@RunWith(JUnit4.class)
public class GrpcRemoteCacheTest {
+
+ private static final DigestUtil DIGEST_UTIL = new DigestUtil(HashFunction.SHA256);
+
private FileSystem fs;
private Path execRoot;
private FileOutErr outErr;
@@ -85,7 +89,6 @@
@Before
public final void setUp() throws Exception {
- FileSystem.setDigestFunctionForTesting(HashFunction.SHA1);
// Use a mutable service registry for later registering the service impl for each test case.
fakeServer =
InProcessServerBuilder.forName(fakeServerName)
@@ -94,7 +97,7 @@
.build()
.start();
Chunker.setDefaultChunkSizeForTesting(1000); // Enough for everything to be one chunk.
- fs = new InMemoryFileSystem();
+ fs = new InMemoryFileSystem(new JavaClock(), HashFunction.SHA256);
execRoot = fs.getPath("/exec/root");
FileSystemUtils.createDirectoryAndParents(execRoot);
fakeFileCache = new FakeActionInputFileCache(execRoot);
@@ -106,7 +109,7 @@
outErr = new FileOutErr(stdout, stderr);
Context withEmptyMetadata =
TracingMetadataUtils.contextWithMetadata(
- "none", "none", Digests.unsafeActionKeyFromDigest(Digest.getDefaultInstance()));
+ "none", "none", DIGEST_UTIL.asActionKey(Digest.getDefaultInstance()));
withEmptyMetadata.attach();
}
@@ -156,13 +159,14 @@
ImmutableList.of(new CallCredentialsInterceptor(creds))),
creds,
remoteOptions,
- retrier);
+ retrier,
+ DIGEST_UTIL);
}
@Test
public void testDownloadEmptyBlob() throws Exception {
GrpcRemoteCache client = newClient();
- Digest emptyDigest = Digests.computeDigest(new byte[0]);
+ Digest emptyDigest = DIGEST_UTIL.compute(new byte[0]);
// Will not call the mock Bytestream interface at all.
assertThat(client.downloadBlob(emptyDigest)).isEmpty();
}
@@ -170,7 +174,7 @@
@Test
public void testDownloadBlobSingleChunk() throws Exception {
final GrpcRemoteCache client = newClient();
- final Digest digest = Digests.computeDigestUtf8("abcdefg");
+ final Digest digest = DIGEST_UTIL.computeAsUtf8("abcdefg");
serviceRegistry.addService(
new ByteStreamImplBase() {
@Override
@@ -187,7 +191,7 @@
@Test
public void testDownloadBlobMultipleChunks() throws Exception {
final GrpcRemoteCache client = newClient();
- final Digest digest = Digests.computeDigestUtf8("abcdefg");
+ final Digest digest = DIGEST_UTIL.computeAsUtf8("abcdefg");
serviceRegistry.addService(
new ByteStreamImplBase() {
@Override
@@ -208,9 +212,9 @@
@Test
public void testDownloadAllResults() throws Exception {
GrpcRemoteCache client = newClient();
- Digest fooDigest = Digests.computeDigestUtf8("foo-contents");
- Digest barDigest = Digests.computeDigestUtf8("bar-contents");
- Digest emptyDigest = Digests.computeDigest(new byte[0]);
+ Digest fooDigest = DIGEST_UTIL.computeAsUtf8("foo-contents");
+ Digest barDigest = DIGEST_UTIL.computeAsUtf8("bar-contents");
+ Digest emptyDigest = DIGEST_UTIL.compute(new byte[0]);
serviceRegistry.addService(
new FakeImmutableCacheByteStreamImpl(fooDigest, "foo-contents", barDigest, "bar-contents"));
@@ -219,16 +223,16 @@
result.addOutputFilesBuilder().setPath("b/empty").setDigest(emptyDigest);
result.addOutputFilesBuilder().setPath("a/bar").setDigest(barDigest).setIsExecutable(true);
client.download(result.build(), execRoot, null);
- assertThat(Digests.computeDigest(execRoot.getRelative("a/foo"))).isEqualTo(fooDigest);
- assertThat(Digests.computeDigest(execRoot.getRelative("b/empty"))).isEqualTo(emptyDigest);
- assertThat(Digests.computeDigest(execRoot.getRelative("a/bar"))).isEqualTo(barDigest);
+ assertThat(DIGEST_UTIL.compute(execRoot.getRelative("a/foo"))).isEqualTo(fooDigest);
+ assertThat(DIGEST_UTIL.compute(execRoot.getRelative("b/empty"))).isEqualTo(emptyDigest);
+ assertThat(DIGEST_UTIL.compute(execRoot.getRelative("a/bar"))).isEqualTo(barDigest);
assertThat(execRoot.getRelative("a/bar").isExecutable()).isTrue();
}
@Test
public void testUploadBlobCacheHitWithRetries() throws Exception {
final GrpcRemoteCache client = newClient();
- final Digest digest = Digests.computeDigestUtf8("abcdefg");
+ final Digest digest = DIGEST_UTIL.computeAsUtf8("abcdefg");
serviceRegistry.addService(
new ContentAddressableStorageImplBase() {
private int numErrors = 4;
@@ -251,7 +255,7 @@
@Test
public void testUploadBlobSingleChunk() throws Exception {
final GrpcRemoteCache client = newClient();
- final Digest digest = Digests.computeDigestUtf8("abcdefg");
+ final Digest digest = DIGEST_UTIL.computeAsUtf8("abcdefg");
serviceRegistry.addService(
new ContentAddressableStorageImplBase() {
@Override
@@ -302,7 +306,7 @@
this.responseObserver = responseObserver;
this.contents = contents;
try {
- chunker = new Chunker(contents.getBytes(UTF_8), chunkSizeBytes);
+ chunker = new Chunker(contents.getBytes(UTF_8), chunkSizeBytes, DIGEST_UTIL);
} catch (IOException e) {
fail("An error occurred:" + e);
}
@@ -357,7 +361,7 @@
@Test
public void testUploadBlobMultipleChunks() throws Exception {
- final Digest digest = Digests.computeDigestUtf8("abcdef");
+ final Digest digest = DIGEST_UTIL.computeAsUtf8("abcdef");
serviceRegistry.addService(
new ContentAddressableStorageImplBase() {
@Override
@@ -446,7 +450,7 @@
}
});
- ActionKey emptyKey = Digests.computeActionKey(Action.getDefaultInstance());
+ ActionKey emptyKey = DIGEST_UTIL.computeActionKey(Action.getDefaultInstance());
Path fooFile = execRoot.getRelative("a/foo");
Path barFile = execRoot.getRelative("bar");
client.upload(emptyKey, execRoot, ImmutableList.<Path>of(fooFile, barFile), outErr, false);
@@ -464,7 +468,7 @@
final Path fooFile = execRoot.getRelative("a/foo");
final Path barFile = execRoot.getRelative("bar");
final Path bazFile = execRoot.getRelative("baz");
- ActionKey actionKey = Digests.unsafeActionKeyFromDigest(fooDigest); // Could be any key.
+ ActionKey actionKey = DIGEST_UTIL.asActionKey(fooDigest); // Could be any key.
barFile.setExecutable(true);
serviceRegistry.addService(
new ContentAddressableStorageImplBase() {
@@ -576,7 +580,7 @@
@Test
public void testGetCachedActionResultWithRetries() throws Exception {
final GrpcRemoteCache client = newClient();
- ActionKey actionKey = Digests.unsafeActionKeyFromDigest(Digests.computeDigestUtf8("key"));
+ ActionKey actionKey = DIGEST_UTIL.asActionKey(DIGEST_UTIL.computeAsUtf8("key"));
serviceRegistry.addService(
new ActionCacheImplBase() {
private int numErrors = 4;
diff --git a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java
index 35bd195..2e06805 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java
@@ -38,6 +38,7 @@
import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions;
import com.google.devtools.build.lib.authandtls.GrpcUtils;
+import com.google.devtools.build.lib.clock.JavaClock;
import com.google.devtools.build.lib.exec.SpawnExecException;
import com.google.devtools.build.lib.exec.SpawnInputExpander;
import com.google.devtools.build.lib.exec.SpawnRunner.ProgressStatus;
@@ -102,6 +103,9 @@
/** Tests for {@link RemoteSpawnRunner} in combination with {@link GrpcRemoteExecutor}. */
@RunWith(JUnit4.class)
public class GrpcRemoteExecutionClientTest {
+
+ private static final DigestUtil DIGEST_UTIL = new DigestUtil(HashFunction.SHA256);
+
private static final ArtifactExpander SIMPLE_ARTIFACT_EXPANDER =
new ArtifactExpander() {
@Override
@@ -176,7 +180,6 @@
@Before
public final void setUp() throws Exception {
- FileSystem.setDigestFunctionForTesting(HashFunction.SHA1);
String fakeServerName = "fake server for " + getClass();
// Use a mutable service registry for later registering the service impl for each test case.
fakeServer =
@@ -187,7 +190,7 @@
.start();
Chunker.setDefaultChunkSizeForTesting(1000); // Enough for everything to be one chunk.
- fs = new InMemoryFileSystem();
+ fs = new InMemoryFileSystem(new JavaClock(), HashFunction.SHA256);
execRoot = fs.getPath("/exec/root");
FileSystemUtils.createDirectoryAndParents(execRoot);
fakeFileCache = new FakeActionInputFileCache(execRoot);
@@ -236,7 +239,7 @@
CallCredentials creds =
GrpcUtils.newCallCredentials(Options.getDefaults(AuthAndTLSOptions.class));
GrpcRemoteCache remoteCache =
- new GrpcRemoteCache(channel, creds, options, retrier);
+ new GrpcRemoteCache(channel, creds, options, retrier, DIGEST_UTIL);
client =
new RemoteSpawnRunner(
execRoot,
@@ -247,7 +250,8 @@
"build-req-id",
"command-id",
remoteCache,
- executor);
+ executor,
+ DIGEST_UTIL);
inputDigest = fakeFileCache.createScratchInput(simpleSpawn.getInputFiles().get(0), "xyz");
}
@@ -277,8 +281,8 @@
@Test
public void cacheHitWithOutput() throws Exception {
- final Digest stdOutDigest = Digests.computeDigestUtf8("stdout");
- final Digest stdErrDigest = Digests.computeDigestUtf8("stderr");
+ final Digest stdOutDigest = DIGEST_UTIL.computeAsUtf8("stdout");
+ final Digest stdErrDigest = DIGEST_UTIL.computeAsUtf8("stderr");
serviceRegistry.addService(
new ActionCacheImplBase() {
@Override
@@ -326,7 +330,7 @@
}
private Answer<StreamObserver<WriteRequest>> blobWriteAnswer(final byte[] data) {
- final Digest digest = Digests.computeDigest(data);
+ final Digest digest = DIGEST_UTIL.compute(data);
return new Answer<StreamObserver<WriteRequest>>() {
@Override
public StreamObserver<WriteRequest> answer(InvocationOnMock invocation) {
@@ -444,7 +448,7 @@
.setValue("value")
.build())
.build();
- final Digest cmdDigest = Digests.computeDigest(command);
+ final Digest cmdDigest = DIGEST_UTIL.compute(command);
BindableService cas =
new ContentAddressableStorageImplBase() {
@Override
@@ -633,7 +637,7 @@
.setValue("value")
.build())
.build();
- final Digest cmdDigest = Digests.computeDigest(command);
+ final Digest cmdDigest = DIGEST_UTIL.compute(command);
serviceRegistry.addService(
new ContentAddressableStorageImplBase() {
private int numErrors = 4;
@@ -738,7 +742,7 @@
responseObserver.onError(Status.NOT_FOUND.asRuntimeException());
}
});
- Digest stdOutDigest = Digests.computeDigestUtf8("bla");
+ Digest stdOutDigest = DIGEST_UTIL.computeAsUtf8("bla");
final ActionResult actionResult =
ActionResult.newBuilder().setStdoutDigest(stdOutDigest).build();
serviceRegistry.addService(
@@ -788,7 +792,7 @@
@Test
public void remotelyReExecuteOrphanedCachedActions() throws Exception {
- final Digest stdOutDigest = Digests.computeDigestUtf8("stdout");
+ final Digest stdOutDigest = DIGEST_UTIL.computeAsUtf8("stdout");
final ActionResult actionResult =
ActionResult.newBuilder().setStdoutDigest(stdOutDigest).build();
serviceRegistry.addService(
@@ -858,7 +862,9 @@
fail("Expected an exception");
} catch (ExecException expected) {
assertThat(expected).hasMessageThat().contains("Missing digest");
- assertThat(expected).hasMessageThat().contains("476d9ec701e2de6a6c37ab5211117a7cb8333a27");
+ assertThat(expected)
+ .hasMessageThat()
+ .contains(DIGEST_UTIL.computeAsUtf8("stdout").toString());
}
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
index 3295df6..7ac5cfa 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
@@ -33,6 +33,7 @@
import com.google.devtools.build.lib.actions.SimpleSpawn;
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.actions.SpawnResult.Status;
+import com.google.devtools.build.lib.clock.JavaClock;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.build.lib.events.Reporter;
@@ -42,10 +43,11 @@
import com.google.devtools.build.lib.exec.SpawnRunner.ProgressStatus;
import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionPolicy;
import com.google.devtools.build.lib.exec.util.FakeOwner;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -80,6 +82,7 @@
};
private FileSystem fs;
+ private DigestUtil digestUtil;
private Path execRoot;
private SimpleSpawn simpleSpawn;
private FakeActionInputFileCache fakeFileCache;
@@ -147,7 +150,8 @@
@Before
public final void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- fs = new InMemoryFileSystem();
+ fs = new InMemoryFileSystem(new JavaClock(), HashFunction.SHA256);
+ digestUtil = new DigestUtil(HashFunction.SHA256);
execRoot = fs.getPath("/exec/root");
FileSystemUtils.createDirectoryAndParents(execRoot);
fakeFileCache = new FakeActionInputFileCache(execRoot);
@@ -172,7 +176,14 @@
reporter.addHandler(eventHandler);
cache =
new RemoteSpawnCache(
- execRoot, options, remoteCache, "build-req-id", "command-id", false, reporter);
+ execRoot,
+ options,
+ remoteCache,
+ "build-req-id",
+ "command-id",
+ false,
+ reporter,
+ digestUtil);
fakeFileCache.createScratchInput(simpleSpawn.getInputFiles().get(0), "xyz");
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java
index 5fec618..3168190 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java
@@ -38,6 +38,7 @@
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.actions.SpawnResult.Status;
+import com.google.devtools.build.lib.clock.JavaClock;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.build.lib.events.Reporter;
@@ -48,10 +49,11 @@
import com.google.devtools.build.lib.exec.SpawnRunner.ProgressStatus;
import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionPolicy;
import com.google.devtools.build.lib.exec.util.FakeOwner;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -81,6 +83,7 @@
ImmutableMap.of(ExecutionRequirements.NO_CACHE, "");
private Path execRoot;
+ private DigestUtil digestUtil;
private FakeActionInputFileCache fakeFileCache;
private FileOutErr outErr;
@@ -96,8 +99,8 @@
@Before
public final void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
-
- FileSystem fs = new InMemoryFileSystem();
+ digestUtil = new DigestUtil(HashFunction.SHA256);
+ FileSystem fs = new InMemoryFileSystem(new JavaClock(), HashFunction.SHA256);
execRoot = fs.getPath("/exec/root");
FileSystemUtils.createDirectoryAndParents(execRoot);
fakeFileCache = new FakeActionInputFileCache(execRoot);
@@ -130,7 +133,8 @@
"build-req-id",
"command-id",
cache,
- executor);
+ executor,
+ digestUtil);
ExecuteResponse succeeded = ExecuteResponse.newBuilder().setResult(
ActionResult.newBuilder().setExitCode(0).build()).build();
@@ -186,7 +190,8 @@
"build-req-id",
"command-id",
cache,
- null);
+ null,
+ digestUtil);
// Throw an IOException to trigger the local fallback.
when(executor.executeRemotely(any(ExecuteRequest.class))).thenThrow(IOException.class);
@@ -237,7 +242,8 @@
"build-req-id",
"command-id",
cache,
- null));
+ null,
+ digestUtil));
Spawn spawn = newSimpleSpawn();
SpawnExecutionPolicy policy = new FakeSpawnExecutionPolicy(spawn);
@@ -284,7 +290,8 @@
"build-req-id",
"command-id",
cache,
- null));
+ null,
+ digestUtil));
try {
runner.exec(spawn, policy);
@@ -317,7 +324,8 @@
"build-req-id",
"command-id",
cache,
- null);
+ null,
+ digestUtil);
Spawn spawn = newSimpleSpawn();
SpawnExecutionPolicy policy = new FakeSpawnExecutionPolicy(spawn);
@@ -367,7 +375,8 @@
"build-req-id",
"command-id",
cache,
- null);
+ null,
+ digestUtil);
Spawn spawn = newSimpleSpawn();
SpawnExecutionPolicy policy = new FakeSpawnExecutionPolicy(spawn);
@@ -405,7 +414,8 @@
"build-req-id",
"command-id",
cache,
- null);
+ null,
+ digestUtil);
Spawn spawn = newSimpleSpawn();
SpawnExecutionPolicy policy = new FakeSpawnExecutionPolicy(spawn);
@@ -440,7 +450,8 @@
"build-req-id",
"command-id",
cache,
- executor);
+ executor,
+ digestUtil);
when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
when(executor.executeRemotely(any(ExecuteRequest.class))).thenThrow(new IOException());
@@ -477,7 +488,8 @@
"build-req-id",
"command-id",
cache,
- executor);
+ executor,
+ digestUtil);
ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(cachedResult);
@@ -517,7 +529,8 @@
"build-req-id",
"command-id",
cache,
- executor);
+ executor,
+ digestUtil);
ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
@@ -552,7 +565,8 @@
"build-req-id",
"command-id",
cache,
- executor);
+ executor,
+ digestUtil);
ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
@@ -585,7 +599,8 @@
"build-req-id",
"command-id",
cache,
- executor);
+ executor,
+ digestUtil);
ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
@@ -624,7 +639,8 @@
"build-req-id",
"command-id",
cache,
- executor);
+ executor,
+ digestUtil);
when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
when(executor.executeRemotely(any(ExecuteRequest.class))).thenThrow(new IOException());
@@ -659,7 +675,8 @@
"build-req-id",
"command-id",
cache,
- executor);
+ executor,
+ digestUtil);
when(cache.getCachedActionResult(any(ActionKey.class))).thenThrow(new IOException());
diff --git a/src/test/java/com/google/devtools/build/lib/remote/TreeNodeRepositoryTest.java b/src/test/java/com/google/devtools/build/lib/remote/TreeNodeRepositoryTest.java
index 94374f6..9c0f43a 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/TreeNodeRepositoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/TreeNodeRepositoryTest.java
@@ -21,13 +21,14 @@
import com.google.devtools.build.lib.actions.ActionInputFileCache;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Root;
+import com.google.devtools.build.lib.clock.BlazeClock;
import com.google.devtools.build.lib.exec.SingleBuildFileCache;
import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
import com.google.devtools.build.lib.testutil.Scratch;
-import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
import com.google.devtools.remoteexecution.v1test.Digest;
import com.google.devtools.remoteexecution.v1test.Directory;
import java.util.ArrayList;
@@ -42,13 +43,14 @@
@RunWith(JUnit4.class)
public class TreeNodeRepositoryTest {
private Scratch scratch;
+ private DigestUtil digestUtil;
private Root rootDir;
private Path rootPath;
@Before
public final void setRootDir() throws Exception {
- FileSystem.setDigestFunctionForTesting(HashFunction.SHA1);
- scratch = new Scratch();
+ digestUtil = new DigestUtil(HashFunction.SHA256);
+ scratch = new Scratch(new InMemoryFileSystem(BlazeClock.instance(), HashFunction.SHA256));
rootDir = Root.asDerivedRoot(scratch.dir("/exec/root"));
rootPath = rootDir.getPath();
}
@@ -56,7 +58,7 @@
private TreeNodeRepository createTestTreeNodeRepository() {
ActionInputFileCache inputFileCache =
new SingleBuildFileCache(rootPath.getPathString(), scratch.getFileSystem());
- return new TreeNodeRepository(rootPath, inputFileCache);
+ return new TreeNodeRepository(rootPath, inputFileCache, digestUtil);
}
@Test
diff --git a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ActionCacheServer.java b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ActionCacheServer.java
index 008e826..8e9ba2f 100644
--- a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ActionCacheServer.java
+++ b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ActionCacheServer.java
@@ -16,8 +16,8 @@
import static java.util.logging.Level.WARNING;
-import com.google.devtools.build.lib.remote.Digests;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.remote.SimpleBlobStoreActionCache;
import com.google.devtools.remoteexecution.v1test.ActionCacheGrpc.ActionCacheImplBase;
import com.google.devtools.remoteexecution.v1test.ActionResult;
@@ -31,16 +31,18 @@
private static final Logger logger = Logger.getLogger(ActionCacheImplBase.class.getName());
private final SimpleBlobStoreActionCache cache;
+ private final DigestUtil digestUtil;
- public ActionCacheServer(SimpleBlobStoreActionCache cache) {
+ public ActionCacheServer(SimpleBlobStoreActionCache cache, DigestUtil digestUtil) {
this.cache = cache;
+ this.digestUtil = digestUtil;
}
@Override
public void getActionResult(
GetActionResultRequest request, StreamObserver<ActionResult> responseObserver) {
try {
- ActionKey actionKey = Digests.unsafeActionKeyFromDigest(request.getActionDigest());
+ ActionKey actionKey = digestUtil.asActionKey(request.getActionDigest());
ActionResult result = cache.getCachedActionResult(actionKey);
if (result == null) {
@@ -60,7 +62,7 @@
public void updateActionResult(
UpdateActionResultRequest request, StreamObserver<ActionResult> responseObserver) {
try {
- ActionKey actionKey = Digests.unsafeActionKeyFromDigest(request.getActionDigest());
+ ActionKey actionKey = digestUtil.asActionKey(request.getActionDigest());
cache.setCachedActionResult(actionKey, request.getActionResult());
responseObserver.onNext(request.getActionResult());
responseObserver.onCompleted();
diff --git a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ByteStreamServer.java b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ByteStreamServer.java
index 869d8cf..778d743 100644
--- a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ByteStreamServer.java
+++ b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ByteStreamServer.java
@@ -24,7 +24,7 @@
import com.google.bytestream.ByteStreamProto.WriteResponse;
import com.google.devtools.build.lib.remote.CacheNotFoundException;
import com.google.devtools.build.lib.remote.Chunker;
-import com.google.devtools.build.lib.remote.Digests;
+import com.google.devtools.build.lib.remote.DigestUtil;
import com.google.devtools.build.lib.remote.SimpleBlobStoreActionCache;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
@@ -44,6 +44,7 @@
private static final Logger logger = Logger.getLogger(ByteStreamServer.class.getName());
private final SimpleBlobStoreActionCache cache;
private final Path workPath;
+ private final DigestUtil digestUtil;
static @Nullable Digest parseDigestFromResourceName(String resourceName) {
try {
@@ -53,15 +54,16 @@
}
String hash = tokens[tokens.length - 2];
long size = Long.parseLong(tokens[tokens.length - 1]);
- return Digests.buildDigest(hash, size);
+ return DigestUtil.buildDigest(hash, size);
} catch (NumberFormatException e) {
return null;
}
}
- public ByteStreamServer(SimpleBlobStoreActionCache cache, Path workPath) {
+ public ByteStreamServer(SimpleBlobStoreActionCache cache, Path workPath, DigestUtil digestUtil) {
this.cache = cache;
this.workPath = workPath;
+ this.digestUtil = digestUtil;
}
@Override
@@ -78,7 +80,7 @@
try {
// This still relies on the blob size to be small enough to fit in memory.
// TODO(olaola): refactor to fix this if the need arises.
- Chunker c = new Chunker(cache.downloadBlob(digest));
+ Chunker c = new Chunker(cache.downloadBlob(digest), digestUtil);
while (c.hasNext()) {
responseObserver.onNext(
ReadResponse.newBuilder().setData(c.next().getData()).build());
@@ -227,7 +229,7 @@
}
try {
- Digest d = Digests.computeDigest(temp);
+ Digest d = digestUtil.compute(temp);
try (InputStream in = temp.getInputStream()) {
cache.uploadStream(d, in);
}
diff --git a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ExecutionServer.java b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ExecutionServer.java
index 7dd5f95..0b3f278 100644
--- a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ExecutionServer.java
+++ b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/ExecutionServer.java
@@ -25,8 +25,8 @@
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.devtools.build.lib.remote.CacheNotFoundException;
-import com.google.devtools.build.lib.remote.Digests;
-import com.google.devtools.build.lib.remote.Digests.ActionKey;
+import com.google.devtools.build.lib.remote.DigestUtil;
+import com.google.devtools.build.lib.remote.DigestUtil.ActionKey;
import com.google.devtools.build.lib.remote.SimpleBlobStoreActionCache;
import com.google.devtools.build.lib.remote.TracingMetadataUtils;
import com.google.devtools.build.lib.shell.AbnormalTerminationException;
@@ -93,18 +93,21 @@
private final SimpleBlobStoreActionCache cache;
private final ConcurrentHashMap<String, ListenableFuture<ActionResult>> operationsCache;
private final ListeningExecutorService executorService;
+ private final DigestUtil digestUtil;
public ExecutionServer(
Path workPath,
Path sandboxPath,
RemoteWorkerOptions workerOptions,
SimpleBlobStoreActionCache cache,
- ConcurrentHashMap<String, ListenableFuture<ActionResult>> operationsCache) {
+ ConcurrentHashMap<String, ListenableFuture<ActionResult>> operationsCache,
+ DigestUtil digestUtil) {
this.workPath = workPath;
this.sandboxPath = sandboxPath;
this.workerOptions = workerOptions;
this.cache = cache;
this.operationsCache = operationsCache;
+ this.digestUtil = digestUtil;
ThreadPoolExecutor realExecutor = new ThreadPoolExecutor(
// This is actually the max number of concurrent jobs.
workerOptions.jobs,
@@ -265,7 +268,7 @@
cache.uploadOutErr(result, stdout, stderr);
ActionResult finalResult = result.setExitCode(exitCode).build();
if (exitCode == 0) {
- ActionKey actionKey = Digests.computeActionKey(action);
+ ActionKey actionKey = digestUtil.computeActionKey(action);
cache.setCachedActionResult(actionKey, finalResult);
}
return finalResult;
diff --git a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/RemoteWorker.java b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/RemoteWorker.java
index 2cff8f8..0970294 100644
--- a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/RemoteWorker.java
+++ b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/RemoteWorker.java
@@ -24,6 +24,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.devtools.build.lib.remote.DigestUtil;
import com.google.devtools.build.lib.remote.RemoteOptions;
import com.google.devtools.build.lib.remote.SimpleBlobStoreActionCache;
import com.google.devtools.build.lib.remote.SimpleBlobStoreFactory;
@@ -39,11 +40,13 @@
import com.google.devtools.build.lib.util.ProcessUtils;
import com.google.devtools.build.lib.util.SingleLineFormatter;
import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.common.options.OptionsParser;
+import com.google.devtools.common.options.OptionsParsingException;
import com.google.devtools.remoteexecution.v1test.ActionCacheGrpc.ActionCacheImplBase;
import com.google.devtools.remoteexecution.v1test.ActionResult;
import com.google.devtools.remoteexecution.v1test.ContentAddressableStorageGrpc.ContentAddressableStorageImplBase;
@@ -85,15 +88,28 @@
private final ExecutionImplBase execServer;
static FileSystem getFileSystem() {
- return OS.getCurrent() == OS.WINDOWS ? new JavaIoFileSystem() : new UnixFileSystem();
+ final HashFunction hashFunction;
+ String value = null;
+ try {
+ value = System.getProperty("bazel.DigestFunction", "MD5");
+ hashFunction = new HashFunction.Converter().convert(value);
+ } catch (OptionsParsingException e) {
+ throw new Error("The specified hash function '" + value + "' is not supported.");
+ }
+ return OS.getCurrent() == OS.WINDOWS
+ ? new JavaIoFileSystem(hashFunction)
+ : new UnixFileSystem(hashFunction);
}
public RemoteWorker(
- FileSystem fs, RemoteWorkerOptions workerOptions, SimpleBlobStoreActionCache cache,
- Path sandboxPath)
+ FileSystem fs,
+ RemoteWorkerOptions workerOptions,
+ SimpleBlobStoreActionCache cache,
+ Path sandboxPath,
+ DigestUtil digestUtil)
throws IOException {
this.workerOptions = workerOptions;
- this.actionCacheServer = new ActionCacheServer(cache);
+ this.actionCacheServer = new ActionCacheServer(cache, digestUtil);
Path workPath;
if (workerOptions.workPath != null) {
workPath = fs.getPath(workerOptions.workPath);
@@ -110,7 +126,7 @@
// For now, we use a temporary path if no work path was provided.
workPath = fs.getPath("/tmp/remote-worker");
}
- this.bsServer = new ByteStreamServer(cache, workPath);
+ this.bsServer = new ByteStreamServer(cache, workPath, digestUtil);
this.casServer = new CasServer(cache);
if (workerOptions.workPath != null) {
@@ -119,7 +135,8 @@
FileSystemUtils.createDirectoryAndParents(workPath);
watchServer = new WatcherServer(operationsCache);
execServer =
- new ExecutionServer(workPath, sandboxPath, workerOptions, cache, operationsCache);
+ new ExecutionServer(
+ workPath, sandboxPath, workerOptions, cache, operationsCache, digestUtil);
} else {
watchServer = null;
execServer = null;
@@ -252,9 +269,14 @@
blobStore = new ConcurrentMapBlobStore(new ConcurrentHashMap<String, byte[]>());
}
+ DigestUtil digestUtil = new DigestUtil(HashFunction.SHA256);
RemoteWorker worker =
new RemoteWorker(
- fs, remoteWorkerOptions, new SimpleBlobStoreActionCache(blobStore), sandboxPath);
+ fs,
+ remoteWorkerOptions,
+ new SimpleBlobStoreActionCache(blobStore, digestUtil),
+ sandboxPath,
+ digestUtil);
final Server server = worker.startServer();
worker.createPidFile();