org.gradle.api.internal.tasks.BaseSnapshotInputsBuildOperationResult Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2023 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.api.internal.tasks;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps;
import org.gradle.api.NonNullApi;
import org.gradle.caching.BuildCacheKey;
import org.gradle.internal.execution.caching.CachingState;
import org.gradle.internal.execution.history.BeforeExecutionState;
import org.gradle.internal.execution.history.ExecutionInputState;
import org.gradle.internal.execution.model.InputNormalizer;
import org.gradle.internal.fingerprint.DirectorySensitivity;
import org.gradle.internal.fingerprint.FileNormalizer;
import org.gradle.internal.fingerprint.LineEndingSensitivity;
import org.gradle.internal.hash.HashCode;
import org.gradle.internal.hash.Hashing;
import org.gradle.internal.operations.trace.CustomOperationTraceSerialization;
import org.gradle.internal.scan.UsedByScanPlugin;
import org.gradle.internal.snapshot.impl.ImplementationSnapshot;
import org.gradle.operations.execution.FilePropertyVisitor;
import javax.annotation.Nullable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
@NonNullApi
public abstract class BaseSnapshotInputsBuildOperationResult implements CustomOperationTraceSerialization {
@VisibleForTesting
final CachingState cachingState;
public BaseSnapshotInputsBuildOperationResult(CachingState cachingState) {
this.cachingState = cachingState;
}
protected abstract Map fileProperties();
@Nullable
public Map getInputValueHashesBytes() {
return getBeforeExecutionState()
.map(ExecutionInputState::getInputProperties)
.filter(inputValueFingerprints -> !inputValueFingerprints.isEmpty())
.map(inputValueFingerprints -> inputValueFingerprints.entrySet().stream()
.collect(toLinkedHashMap(valueSnapshot -> Hashing.hashHashable(valueSnapshot).toByteArray())))
.orElse(null);
}
@Nullable
public byte[] getClassLoaderHashBytes() {
return getBeforeExecutionState()
.map(ExecutionInputState::getImplementation)
.map(BaseSnapshotInputsBuildOperationResult::getClassLoaderHashBytesOrNull)
.orElse(null);
}
@Nullable
public String getImplementationClassName() {
return getBeforeExecutionState()
.map(ExecutionInputState::getImplementation)
.map(ImplementationSnapshot::getClassIdentifier)
.orElse(null);
}
@Nullable
public List getActionClassLoaderHashesBytes() {
return getBeforeExecutionState()
.map(BeforeExecutionState::getAdditionalImplementations)
.filter(additionalImplementation -> !additionalImplementation.isEmpty())
.map(additionalImplementations -> additionalImplementations.stream()
.map(BaseSnapshotInputsBuildOperationResult::getClassLoaderHashBytesOrNull) // preserve nulls
.collect(Collectors.toList()))
.orElse(null);
}
@Nullable
private static byte[] getClassLoaderHashBytesOrNull(ImplementationSnapshot implementation) {
HashCode hash = implementation.getClassLoaderHash();
return hash == null ? null : hash.toByteArray();
}
@Nullable
public List getActionClassNames() {
return getBeforeExecutionState()
.map(BeforeExecutionState::getAdditionalImplementations)
.filter(additionalImplementations -> !additionalImplementations.isEmpty())
.map(additionalImplementations -> additionalImplementations.stream()
.map(ImplementationSnapshot::getClassIdentifier)
.collect(Collectors.toList())
)
.orElse(null);
}
@Nullable
public List getOutputPropertyNames() {
return getBeforeExecutionState()
.map(BeforeExecutionState::getOutputFileLocationSnapshots)
.map(ImmutableSortedMap::keySet)
.filter(outputPropertyNames -> !outputPropertyNames.isEmpty())
.map(ImmutableSet::asList)
.orElse(null);
}
@Nullable
public byte[] getHashBytes() {
return getKey()
.map(BuildCacheKey::toByteArray)
.orElse(null);
}
@Override
public Object getCustomOperationTraceSerializableModel() {
Map model = new TreeMap<>();
List actionClassLoaderHashesBytes = getActionClassLoaderHashesBytes();
if (actionClassLoaderHashesBytes != null) {
List actionClassloaderHashes = getActionClassLoaderHashesBytes().stream()
.map(hash -> hash == null ? null : HashCode.fromBytes(hash).toString())
.collect(Collectors.toList());
model.put("actionClassLoaderHashes", actionClassloaderHashes);
} else {
model.put("actionClassLoaderHashes", null);
}
model.put("actionClassNames", getActionClassNames());
byte[] hashBytes = getHashBytes();
if (hashBytes != null) {
model.put("hash", HashCode.fromBytes(hashBytes).toString());
} else {
model.put("hash", null);
}
byte[] classLoaderHashBytes = getClassLoaderHashBytes();
if (classLoaderHashBytes != null) {
model.put("classLoaderHash", HashCode.fromBytes(classLoaderHashBytes).toString());
} else {
model.put("classLoaderHash", null);
}
model.put("implementationClassName", getImplementationClassName());
model.put("inputFileProperties", fileProperties());
Map inputValueHashesBytes = getInputValueHashesBytes();
if (inputValueHashesBytes != null) {
Map inputValueHashes = inputValueHashesBytes.entrySet().stream()
.collect(toLinkedHashMap(value -> value == null ? null : HashCode.fromBytes(value).toString()));
model.put("inputValueHashes", inputValueHashes);
} else {
model.put("inputValueHashes", null);
}
model.put("outputPropertyNames", getOutputPropertyNames());
return model;
}
private static Collector, ?, LinkedHashMap> toLinkedHashMap(Function super V, ? extends U> valueMapper) {
return Collectors.toMap(
Map.Entry::getKey,
entry -> valueMapper.apply(entry.getValue()),
(a, b) -> b,
LinkedHashMap::new
);
}
protected Optional getBeforeExecutionState() {
return cachingState.getCacheKeyCalculatedState()
.map(CachingState.CacheKeyCalculatedState::getBeforeExecutionState);
}
private Optional getKey() {
return cachingState.getCacheKeyCalculatedState()
.map(CachingState.CacheKeyCalculatedState::getKey);
}
@UsedByScanPlugin("The value names are used as part of build scan data and cannot be changed - new values can be added")
enum FilePropertyAttribute {
// When adding new values, be sure to comment which Gradle version started emitting the attribute.
// Additionally, indicate any other relevant constraints with regard to other attributes or changes.
// Every property has exactly one of the following
FINGERPRINTING_STRATEGY_ABSOLUTE_PATH,
FINGERPRINTING_STRATEGY_NAME_ONLY,
FINGERPRINTING_STRATEGY_RELATIVE_PATH,
FINGERPRINTING_STRATEGY_IGNORED_PATH,
FINGERPRINTING_STRATEGY_CLASSPATH,
FINGERPRINTING_STRATEGY_COMPILE_CLASSPATH,
// Every property has exactly one of the following
DIRECTORY_SENSITIVITY_DEFAULT,
DIRECTORY_SENSITIVITY_IGNORE_DIRECTORIES,
// Every property has exactly one of the following
LINE_ENDING_SENSITIVITY_DEFAULT,
LINE_ENDING_SENSITIVITY_NORMALIZE_LINE_ENDINGS;
private static final Map BY_NORMALIZER = ImmutableMap.builder()
.put(InputNormalizer.RUNTIME_CLASSPATH, FINGERPRINTING_STRATEGY_CLASSPATH)
.put(InputNormalizer.COMPILE_CLASSPATH, FINGERPRINTING_STRATEGY_COMPILE_CLASSPATH)
.put(InputNormalizer.ABSOLUTE_PATH, FINGERPRINTING_STRATEGY_ABSOLUTE_PATH)
.put(InputNormalizer.RELATIVE_PATH, FINGERPRINTING_STRATEGY_RELATIVE_PATH)
.put(InputNormalizer.NAME_ONLY, FINGERPRINTING_STRATEGY_NAME_ONLY)
.put(InputNormalizer.IGNORE_PATH, FINGERPRINTING_STRATEGY_IGNORED_PATH)
.build();
private static final Map BY_DIRECTORY_SENSITIVITY = Maps.immutableEnumMap(ImmutableMap.builder()
.put(DirectorySensitivity.DEFAULT, DIRECTORY_SENSITIVITY_DEFAULT)
.put(DirectorySensitivity.IGNORE_DIRECTORIES, DIRECTORY_SENSITIVITY_IGNORE_DIRECTORIES)
.build());
private static final Map BY_LINE_ENDING_SENSITIVITY = Maps.immutableEnumMap(ImmutableMap.builder()
.put(LineEndingSensitivity.DEFAULT, LINE_ENDING_SENSITIVITY_DEFAULT)
.put(LineEndingSensitivity.NORMALIZE_LINE_ENDINGS, LINE_ENDING_SENSITIVITY_NORMALIZE_LINE_ENDINGS)
.build());
private static SnapshotTaskInputsBuildOperationResult.FilePropertyAttribute findFor(T value, Map mapping) {
SnapshotTaskInputsBuildOperationResult.FilePropertyAttribute attribute = mapping.get(value);
if (attribute == null) {
throw new IllegalStateException("Did not find property attribute mapping for '" + value + "' (from: " + mapping.keySet() + ")");
}
return attribute;
}
static SnapshotTaskInputsBuildOperationResult.FilePropertyAttribute fromNormalizer(FileNormalizer normalizer) {
return findFor(normalizer, BY_NORMALIZER);
}
static SnapshotTaskInputsBuildOperationResult.FilePropertyAttribute from(DirectorySensitivity directorySensitivity) {
return findFor(directorySensitivity, BY_DIRECTORY_SENSITIVITY);
}
static SnapshotTaskInputsBuildOperationResult.FilePropertyAttribute from(LineEndingSensitivity lineEndingSensitivity) {
return findFor(lineEndingSensitivity, BY_LINE_ENDING_SENSITIVITY);
}
}
protected abstract static class BaseFilePropertyCollectingVisitor {
private final Map fileProperties;
Property property;
final Deque dirStack;
public BaseFilePropertyCollectingVisitor() {
this.fileProperties = new TreeMap<>();
this.dirStack = new ArrayDeque<>();
}
public Map getFileProperties() {
return fileProperties;
}
protected abstract Property createProperty(STATE state);
protected static class Property {
private final String hash;
private final Set attributes;
private final List roots = new ArrayList<>();
public Property(String hash, Set attributes) {
this.hash = hash;
this.attributes = attributes;
}
public String getHash() {
return hash;
}
public Set getAttributes() {
return attributes;
}
public Collection getRoots() {
return roots;
}
}
public abstract static class Entry {
private final String path;
public Entry(String path) {
this.path = path;
}
public String getPath() {
return path;
}
}
static class FileEntry extends Entry {
private final String hash;
FileEntry(String path, String hash) {
super(path);
this.hash = hash;
}
public String getHash() {
return hash;
}
}
static class DirEntry extends Entry {
private final List children = new ArrayList<>();
DirEntry(String path) {
super(path);
}
public Collection getChildren() {
return children;
}
}
public void preProperty(STATE state) {
property = createProperty(state);
fileProperties.put(state.getPropertyName(), property);
}
public void preRoot(STATE state) {
}
public void preDirectory(STATE state) {
boolean isRoot = dirStack.isEmpty();
DirEntry dir = new DirEntry(isRoot ? state.getPath() : state.getName());
if (isRoot) {
property.roots.add(dir);
} else {
//noinspection ConstantConditions
dirStack.peek().children.add(dir);
}
dirStack.push(dir);
}
public void file(STATE state) {
boolean isRoot = dirStack.isEmpty();
FileEntry file = new FileEntry(isRoot ? state.getPath() : state.getName(), HashCode.fromBytes(state.getHashBytes()).toString());
if (isRoot) {
property.roots.add(file);
} else {
//noinspection ConstantConditions
dirStack.peek().children.add(file);
}
}
public void postDirectory() {
dirStack.pop();
}
public void postRoot() {
}
public void postProperty() {
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy