// 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.exec; import com.google.devtools.build.lib.actions.ActionContext; import com.google.devtools.build.lib.actions.ExecException; import com.google.devtools.build.lib.actions.ExecutionStrategy; import com.google.devtools.build.lib.actions.Spawn; import com.google.devtools.build.lib.actions.SpawnResult; import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext; import java.io.Closeable; import java.io.IOException; import java.util.NoSuchElementException; /** * A cache that can lookup a {@link SpawnResult} given a {@link Spawn}, and can also upload the * results of an executed spawn to the cache. * *

This is an experimental interface to implement caching with sandboxed local execution. */ public interface SpawnCache extends ActionContext { /** A no-op implementation that has no result, and performs no upload. */ public static CacheHandle NO_RESULT_NO_STORE = new CacheHandle() { @Override public boolean hasResult() { return false; } @Override public SpawnResult getResult() { throw new NoSuchElementException(); } @Override public boolean willStore() { return false; } @Override public void store(SpawnResult result) throws InterruptedException, IOException { // Do nothing. } @Override public void close() {} }; /** * Helper method to create a {@link CacheHandle} from a successful {@link SpawnResult} instance. */ public static CacheHandle success(final SpawnResult result) { return new CacheHandle() { @Override public boolean hasResult() { return true; } @Override public SpawnResult getResult() { return result; } @Override public boolean willStore() { return false; } @Override public void store(SpawnResult result) throws InterruptedException, IOException { throw new IllegalStateException(); } @Override public void close() {} }; } /** A no-op spawn cache. */ @ExecutionStrategy( name = {"no-cache"}, contextType = SpawnCache.class ) public static class NoSpawnCache implements SpawnCache { @Override public CacheHandle lookup(Spawn spawn, SpawnExecutionContext context) { return SpawnCache.NO_RESULT_NO_STORE; } } /** A no-op implementation that has no results and performs no stores. */ public static SpawnCache NO_CACHE = new NoSpawnCache(); /** * This object represents both a successful and an unsuccessful cache lookup. If * {@link #hasResult} returns true, then {@link #getResult} must successfully return a non-null * instance (use the {@link #success} helper method). Otherwise {@link #getResult} should throw an * {@link IllegalStateException}. * *

If {@link #hasResult} returns false, then {@link #store} may upload the result to the cache * after successful execution. * *

Note that this interface extends {@link Closeable}, and callers must guarantee that * {@link #close} is called on this entry (e.g., by using try-with-resources) to free up any * acquired resources. */ interface CacheHandle extends Closeable { /** Returns whether the cache lookup was successful. */ boolean hasResult(); /** * Returns the cached result. * * @throws NoSuchElementException if there is no result in this cache entry */ SpawnResult getResult(); /** * Returns true if the store call will actually do work. Use this to avoid unnecessary work * before store if it won't do anything. */ boolean willStore(); /** * Called after successful {@link Spawn} execution, which may or may not store the result in the * cache. * *

A cache may silently return from a failed store operation. We recommend to err on the side * of raising an exception rather than returning silently, and to offer command-line flags to * tweak this default policy as needed. * *

If the current thread is interrupted, then this method should return as quickly as * possible with an {@link InterruptedException}. */ void store(SpawnResult result) throws ExecException, InterruptedException, IOException; } /** * Perform a spawn lookup. This method is similar to {@link SpawnRunner#exec}, taking the same * parameters and being allowed to throw the same exceptions. The intent for this method is to * compute a cache lookup key for the given spawn, looking it up in an implementation-dependent * cache (can be either on the local or remote machine), and returning a non-null {@link * CacheHandle} instance. * *

If the lookup was successful, this method should write the cached outputs to their * corresponding output locations in the output tree, as well as stdout and stderr, after * notifying {@link SpawnExecutionContext#lockOutputFiles}. * *

If the lookup was unsuccessful, this method can return a {@link CacheHandle} instance that * has no result, but uploads the results of the execution to the cache. The reason for a callback * object is for the cache to store expensive intermediate values (such as the cache key) that are * needed both for the lookup and the subsequent store operation. * *

The lookup must not succeed for non-cachable spawns. See {@link Spawns#mayBeCached()}. * *

Note that cache stores may be disabled, in which case the returned {@link CacheHandle} * instance's {@link CacheHandle#store} is a no-op. */ CacheHandle lookup(Spawn spawn, SpawnExecutionContext context) throws ExecException, IOException, InterruptedException; }