/*
* Copyright 2016-present Facebook, Inc.
*
* 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.facebook.buck.versions;
import com.facebook.buck.cli.BuckConfig;
import com.facebook.buck.event.BuckEvent;
import com.facebook.buck.event.BuckEventBus;
import com.facebook.buck.log.Logger;
import com.facebook.buck.rules.TargetGraphAndBuildTargets;
import com.facebook.buck.rules.coercer.TypeCoercerFactory;
import com.facebook.buck.util.immutables.BuckStyleTuple;
import com.google.common.collect.ImmutableMap;
import java.util.concurrent.ForkJoinPool;
import javax.annotation.Nullable;
import org.immutables.value.Value;
public class VersionedTargetGraphCache {
private static final Logger LOG = Logger.get(VersionedTargetGraphCache.class);
@Nullable private CachedVersionedTargetGraph cachedVersionedTargetGraph = null;
/** @return a new versioned target graph. */
private TargetGraphAndBuildTargets createdVersionedTargetGraph(
TargetGraphAndBuildTargets targetGraphAndBuildTargets,
ImmutableMap<String, VersionUniverse> versionUniverses,
ForkJoinPool pool,
TypeCoercerFactory typeCoercerFactory)
throws VersionException, InterruptedException {
return VersionedTargetGraphBuilder.transform(
new VersionUniverseVersionSelector(
targetGraphAndBuildTargets.getTargetGraph(), versionUniverses),
targetGraphAndBuildTargets,
pool,
typeCoercerFactory);
}
private VersionedTargetGraphCacheResult getVersionedTargetGraph(
TargetGraphAndBuildTargets targetGraphAndBuildTargets,
ImmutableMap<String, VersionUniverse> versionUniverses,
ForkJoinPool pool,
TypeCoercerFactory typeCoercerFactory)
throws VersionException, InterruptedException {
// If new inputs match old ones, we can used the cached graph, if present.
VersionedTargetGraphInputs newInputs =
VersionedTargetGraphInputs.of(targetGraphAndBuildTargets, versionUniverses);
if (cachedVersionedTargetGraph != null
&& newInputs.equals(cachedVersionedTargetGraph.getInputs())) {
return VersionedTargetGraphCacheResult.of(
ResultType.HIT, cachedVersionedTargetGraph.getTargetGraphAndBuildTargets());
}
// Build and cache new versioned target graph.
ResultType resultType =
cachedVersionedTargetGraph == null ? ResultType.EMPTY : ResultType.MISMATCH;
TargetGraphAndBuildTargets newVersionedTargetGraph =
createdVersionedTargetGraph(
targetGraphAndBuildTargets, versionUniverses, pool, typeCoercerFactory);
cachedVersionedTargetGraph = CachedVersionedTargetGraph.of(newInputs, newVersionedTargetGraph);
return VersionedTargetGraphCacheResult.of(resultType, newVersionedTargetGraph);
}
/**
* @return a versioned target graph, either generated from the parameters or retrieved from a
* cache.
*/
public VersionedTargetGraphCacheResult getVersionedTargetGraph(
BuckEventBus eventBus,
TypeCoercerFactory typeCoercerFactory,
TargetGraphAndBuildTargets targetGraphAndBuildTargets,
ImmutableMap<String, VersionUniverse> versionUniverses,
ForkJoinPool pool)
throws VersionException, InterruptedException {
VersionedTargetGraphEvent.Started started = VersionedTargetGraphEvent.started();
eventBus.post(started);
try {
VersionedTargetGraphCacheResult result =
getVersionedTargetGraph(
targetGraphAndBuildTargets, versionUniverses, pool, typeCoercerFactory);
LOG.info("versioned target graph " + result.getType().getDescription());
eventBus.post(result.getType().getEvent());
return result;
} finally {
eventBus.post(VersionedTargetGraphEvent.finished(started));
}
}
public TargetGraphAndBuildTargets toVersionedTargetGraph(
BuckEventBus eventBus,
BuckConfig buckConfig,
TypeCoercerFactory typeCoercerFactory,
TargetGraphAndBuildTargets targetGraphAndBuildTargets)
throws VersionException, InterruptedException {
return getVersionedTargetGraph(
eventBus,
typeCoercerFactory,
targetGraphAndBuildTargets,
new VersionBuckConfig(buckConfig).getVersionUniverses(),
new ForkJoinPool(buckConfig.getNumThreads()))
.getTargetGraphAndBuildTargets();
}
/**
* A collection of anything which affects/changes how the versioned target graph is generated. If
* any of these items changes between runs, we cannot use the cached versioned target graph and
* must re-generate it.
*/
@Value.Immutable
@BuckStyleTuple
interface AbstractVersionedTargetGraphInputs {
/** @return the un-versioned target graph to be transformed. */
TargetGraphAndBuildTargets getTargetGraphAndBuildTargets();
/** @return the version universes used when generating the versioned target graph. */
ImmutableMap<String, VersionUniverse> getVersionUniverses();
}
/**
* Tuple to store the previously cached versioned target graph along with all inputs that affect
* how it's generated (for invalidation detection).
*/
@Value.Immutable
@BuckStyleTuple
interface AbstractCachedVersionedTargetGraph {
/** @return any inputs which, when changed, may produce a different versioned target graph. */
VersionedTargetGraphInputs getInputs();
/** @return a versioned target graph. */
TargetGraphAndBuildTargets getTargetGraphAndBuildTargets();
}
@Value.Immutable
@BuckStyleTuple
interface AbstractVersionedTargetGraphCacheResult {
/** @return the type of result. */
ResultType getType();
/** @return a versioned target graph. */
TargetGraphAndBuildTargets getTargetGraphAndBuildTargets();
}
/** The possible result types using the cache. */
public enum ResultType {
/** A miss in the cache due to the inputs changing. */
MISMATCH {
@Override
BuckEvent getEvent() {
return VersionedTargetGraphEvent.Cache.miss();
}
@Override
String getDescription() {
return "cache miss (mismatch)";
}
},
/** A miss in the cache due to the cache being empty. */
EMPTY {
@Override
BuckEvent getEvent() {
return VersionedTargetGraphEvent.Cache.miss();
}
@Override
String getDescription() {
return "cache miss (empty)";
}
},
/** A hit in the cache. */
HIT {
@Override
BuckEvent getEvent() {
return VersionedTargetGraphEvent.Cache.hit();
}
@Override
String getDescription() {
return "cache hit";
}
},
;
abstract BuckEvent getEvent();
abstract String getDescription();
}
}