/*
* Copyright 2017 the original author or authors.
*
* 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 org.gradle.caching.internal.tasks;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.hash.HashCode;
import org.gradle.api.Nullable;
import org.gradle.api.internal.changedetection.state.ImplementationSnapshot;
import org.gradle.caching.internal.BuildCacheHasher;
import org.gradle.caching.internal.DefaultBuildCacheHasher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class DefaultTaskOutputCachingBuildCacheKeyBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTaskOutputCachingBuildCacheKeyBuilder.class);
public static final TaskOutputCachingBuildCacheKey NO_CACHE_KEY = new DefaultTaskOutputCachingBuildCacheKeyBuilder().build();
private BuildCacheHasher hasher = new DefaultBuildCacheHasher();
private String taskClass;
private HashCode classLoaderHash;
private List<HashCode> actionClassLoaderHashes;
private ImmutableList<String> actionTypes;
private final ImmutableSortedMap.Builder<String, HashCode> inputHashes = ImmutableSortedMap.naturalOrder();
private final ImmutableSortedSet.Builder<String> outputPropertyNames = ImmutableSortedSet.naturalOrder();
public DefaultTaskOutputCachingBuildCacheKeyBuilder appendTaskImplementation(ImplementationSnapshot taskImplementation) {
this.taskClass = taskImplementation.getTypeName();
hasher.putString(taskClass);
log("taskClass", taskClass);
if (!taskImplementation.hasUnknownClassLoader()) {
HashCode hashCode = taskImplementation.getClassLoaderHash();
this.classLoaderHash = hashCode;
hasher.putHash(hashCode);
log("classLoaderHash", hashCode);
}
return this;
}
public DefaultTaskOutputCachingBuildCacheKeyBuilder appendTaskActionImplementations(Collection<ImplementationSnapshot> taskActionImplementations) {
ImmutableList.Builder<String> actionTypes = ImmutableList.builder();
List<HashCode> actionClassLoaderHashes = Lists.newArrayListWithCapacity(taskActionImplementations.size());
for (ImplementationSnapshot actionImpl : taskActionImplementations) {
String actionType = actionImpl.getTypeName();
actionTypes.add(actionType);
hasher.putString(actionType);
log("actionType", actionType);
HashCode hashCode;
if (actionImpl.hasUnknownClassLoader()) {
hashCode = null;
} else {
hashCode = actionImpl.getClassLoaderHash();
hasher.putHash(hashCode);
}
actionClassLoaderHashes.add(hashCode);
log("actionClassLoaderHash", hashCode);
}
this.actionTypes = actionTypes.build();
this.actionClassLoaderHashes = Collections.unmodifiableList(actionClassLoaderHashes);
return this;
}
public DefaultTaskOutputCachingBuildCacheKeyBuilder appendInputPropertyHash(String propertyName, HashCode hashCode) {
hasher.putString(propertyName);
hasher.putHash(hashCode);
inputHashes.put(propertyName, hashCode);
LOGGER.debug("Appending inputPropertyHash for '{}' to build cache key: {}", propertyName, hashCode);
return this;
}
public DefaultTaskOutputCachingBuildCacheKeyBuilder appendOutputPropertyName(String propertyName) {
outputPropertyNames.add(propertyName);
hasher.putString(propertyName);
log("outputPropertyName", propertyName);
return this;
}
private static void log(String name, Object value) {
LOGGER.debug("Appending {} to build cache key: {}", name, value);
}
public TaskOutputCachingBuildCacheKey build() {
BuildCacheKeyInputs inputs = new BuildCacheKeyInputs(taskClass, classLoaderHash, actionClassLoaderHashes, actionTypes, inputHashes.build(), outputPropertyNames.build());
if (classLoaderHash == null || actionClassLoaderHashes.contains(null)) {
return new DefaultTaskOutputCachingBuildCacheKey(null, inputs);
}
return new DefaultTaskOutputCachingBuildCacheKey(hasher.hash(), inputs);
}
private static class DefaultTaskOutputCachingBuildCacheKey implements TaskOutputCachingBuildCacheKey {
private final HashCode hashCode;
private final BuildCacheKeyInputs inputs;
private DefaultTaskOutputCachingBuildCacheKey(@Nullable HashCode hashCode, BuildCacheKeyInputs inputs) {
this.hashCode = hashCode;
this.inputs = inputs;
}
@Override
public String getHashCode() {
return Preconditions.checkNotNull(hashCode, "Cannot determine hash code for invalid build cache key").toString();
}
@Override
public BuildCacheKeyInputs getInputs() {
return inputs;
}
@Override
public boolean isValid() {
return hashCode != null;
}
@Override
public String toString() {
return String.valueOf(hashCode);
}
}
}