org.gradle.api.internalivyservice.DefaultLenientConfiguration Maven / Gradle / Ivy
/*
* Copyright 2011 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.artifacts.ivyservice;
import com.google.common.collect.Sets;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.FileCollectionDependency;
import org.gradle.api.artifacts.LenientConfiguration;
import org.gradle.api.artifacts.ResolveException;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedDependency;
import org.gradle.api.artifacts.UnresolvedDependency;
import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.attributes.AttributeContainer;
import org.gradle.api.capabilities.Capability;
import org.gradle.api.internal.artifacts.DependencyGraphNodeResult;
import org.gradle.api.internal.artifacts.ResolveArtifactsBuildOperationType;
import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification.DependencyVerificationOverride;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.CompositeResolvedArtifactSet;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.LocalDependencyFiles;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ParallelResolveArtifactSet;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.SelectedArtifactResults;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.SelectedArtifactSet;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactSet;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactsResults;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedFileDependencyResults;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResults;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResultsLoader;
import org.gradle.api.internal.artifacts.transform.ArtifactTransforms;
import org.gradle.api.internal.artifacts.transform.VariantSelector;
import org.gradle.api.internal.artifacts.verification.DependencyVerificationException;
import org.gradle.api.internal.attributes.AttributeContainerInternal;
import org.gradle.api.internal.file.FileCollectionInternal;
import org.gradle.api.internal.file.FileCollectionStructureVisitor;
import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
import org.gradle.api.specs.Spec;
import org.gradle.api.specs.Specs;
import org.gradle.internal.DisplayName;
import org.gradle.internal.UncheckedException;
import org.gradle.internal.graph.CachingDirectedGraphWalker;
import org.gradle.internal.graph.DirectedGraphWithEdgeValues;
import org.gradle.internal.operations.BuildOperationContext;
import org.gradle.internal.operations.BuildOperationDescriptor;
import org.gradle.internal.operations.BuildOperationExecutor;
import org.gradle.internal.operations.RunnableBuildOperation;
import org.gradle.internal.work.WorkerLeaseService;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DefaultLenientConfiguration implements LenientConfiguration, VisitedArtifactSet {
private final static ResolveArtifactsBuildOperationType.Result RESULT = new ResolveArtifactsBuildOperationType.Result() {
};
private final ConfigurationInternal configuration;
private final Set unresolvedDependencies;
private final VisitedArtifactsResults artifactResults;
private final VisitedFileDependencyResults fileDependencyResults;
private final TransientConfigurationResultsLoader transientConfigurationResultsFactory;
private final ArtifactTransforms artifactTransforms;
private final AttributeContainerInternal implicitAttributes;
private final BuildOperationExecutor buildOperationExecutor;
private final DependencyVerificationOverride dependencyVerificationOverride;
private final WorkerLeaseService workerLeaseService;
// Selected for the configuration
private SelectedArtifactResults artifactsForThisConfiguration;
private DependencyVerificationException dependencyVerificationException;
public DefaultLenientConfiguration(ConfigurationInternal configuration, Set unresolvedDependencies, VisitedArtifactsResults artifactResults, VisitedFileDependencyResults fileDependencyResults, TransientConfigurationResultsLoader transientConfigurationResultsLoader, ArtifactTransforms artifactTransforms, BuildOperationExecutor buildOperationExecutor, DependencyVerificationOverride dependencyVerificationOverride, WorkerLeaseService workerLeaseService) {
this.configuration = configuration;
this.implicitAttributes = configuration.getAttributes().asImmutable();
this.unresolvedDependencies = unresolvedDependencies;
this.artifactResults = artifactResults;
this.fileDependencyResults = fileDependencyResults;
this.transientConfigurationResultsFactory = transientConfigurationResultsLoader;
this.artifactTransforms = artifactTransforms;
this.buildOperationExecutor = buildOperationExecutor;
this.dependencyVerificationOverride = dependencyVerificationOverride;
this.workerLeaseService = workerLeaseService;
}
private SelectedArtifactResults getSelectedArtifacts() {
if (artifactsForThisConfiguration == null) {
artifactsForThisConfiguration = artifactResults.select(Specs.satisfyAll(), artifactTransforms.variantSelector(implicitAttributes, false, configuration.getDependenciesResolver()));
}
return artifactsForThisConfiguration;
}
public SelectedArtifactSet select() {
return select(Specs.satisfyAll(), implicitAttributes, Specs.satisfyAll(), false);
}
public SelectedArtifactSet select(final Spec super Dependency> dependencySpec) {
return select(dependencySpec, implicitAttributes, Specs.satisfyAll(), false);
}
@Override
public SelectedArtifactSet select(final Spec super Dependency> dependencySpec, final AttributeContainerInternal requestedAttributes, final Spec super ComponentIdentifier> componentSpec, boolean allowNoMatchingVariants) {
VariantSelector selector = artifactTransforms.variantSelector(requestedAttributes, allowNoMatchingVariants, configuration.getDependenciesResolver());
SelectedArtifactResults artifactResults = this.artifactResults.select(componentSpec, selector);
return new SelectedArtifactSet() {
@Override
public void visitDependencies(TaskDependencyResolveContext context) {
for (UnresolvedDependency unresolvedDependency : unresolvedDependencies) {
context.visitFailure(unresolvedDependency.getProblem());
}
context.add(artifactResults.getArtifacts());
}
@Override
public void visitArtifacts(ArtifactVisitor visitor, boolean continueOnSelectionFailure) {
if (!unresolvedDependencies.isEmpty()) {
for (UnresolvedDependency unresolvedDependency : unresolvedDependencies) {
visitor.visitFailure(unresolvedDependency.getProblem());
}
if (!continueOnSelectionFailure) {
return;
}
}
// This may be called from an unmanaged thread, so temporarily enlist the current thread as a worker if it is not already so that it can visit the results
// It would be better to instead to memoize the results on the first visit so that this is not required
workerLeaseService.runAsUnmanagedWorkerThread(() -> visitArtifactsWithBuildOperation(dependencySpec, artifactResults, fileDependencyResults, visitor));
}
};
}
private ResolveException getFailure() {
List failures = new ArrayList<>();
for (UnresolvedDependency unresolvedDependency : unresolvedDependencies) {
failures.add(unresolvedDependency.getProblem());
}
return new ResolveException(configuration.getDisplayName(), failures);
}
public boolean hasError() {
return unresolvedDependencies.size() > 0;
}
@Override
public Set getUnresolvedModuleDependencies() {
return unresolvedDependencies;
}
public void rethrowFailure() throws ResolveException {
if (hasError()) {
throw getFailure();
}
}
private TransientConfigurationResults loadTransientGraphResults(SelectedArtifactResults artifactResults) {
return transientConfigurationResultsFactory.create(artifactResults);
}
@Override
public Set getFirstLevelModuleDependencies(Spec super Dependency> dependencySpec) {
Set matches = new LinkedHashSet<>();
for (DependencyGraphNodeResult node : getFirstLevelNodes(dependencySpec)) {
matches.add(node.getPublicView());
}
return matches;
}
private Set getFirstLevelNodes(Spec super Dependency> dependencySpec) {
Set matches = new LinkedHashSet<>();
TransientConfigurationResults graphResults = loadTransientGraphResults(getSelectedArtifacts());
for (Map.Entry entry : graphResults.getFirstLevelDependencies().entrySet()) {
if (dependencySpec.isSatisfiedBy(entry.getKey())) {
matches.add(entry.getValue());
}
}
return matches;
}
@Override
public Set getAllModuleDependencies() {
Set resolvedElements = new LinkedHashSet<>();
Deque workQueue = new LinkedList<>(loadTransientGraphResults(getSelectedArtifacts()).getRootNode().getPublicView().getChildren());
while (!workQueue.isEmpty()) {
ResolvedDependency item = workQueue.removeFirst();
if (resolvedElements.add(item)) {
final Set children = item.getChildren();
workQueue.addAll(children);
}
}
return resolvedElements;
}
@Override
public Set getFiles() {
return getFiles(Specs.satisfyAll());
}
/**
* Recursive but excludes unsuccessfully resolved artifacts.
*/
@Override
public Set getFiles(Spec super Dependency> dependencySpec) {
LenientFilesAndArtifactResolveVisitor visitor = new LenientFilesAndArtifactResolveVisitor();
visitArtifactsWithBuildOperation(dependencySpec, getSelectedArtifacts(), fileDependencyResults, visitor);
return visitor.files;
}
@Override
public Set getArtifacts() {
return getArtifacts(Specs.satisfyAll());
}
/**
* Recursive but excludes unsuccessfully resolved artifacts.
*/
@Override
public Set getArtifacts(Spec super Dependency> dependencySpec) {
LenientArtifactCollectingVisitor visitor = new LenientArtifactCollectingVisitor();
visitArtifactsWithBuildOperation(dependencySpec, getSelectedArtifacts(), fileDependencyResults, visitor);
return visitor.artifacts;
}
private void visitArtifactsWithBuildOperation(final Spec super Dependency> dependencySpec, final SelectedArtifactResults artifactResults, final VisitedFileDependencyResults fileDependencyResults, final ArtifactVisitor visitor) {
buildOperationExecutor.run(new RunnableBuildOperation() {
@Override
public void run(BuildOperationContext context) {
visitArtifacts(dependencySpec, artifactResults, fileDependencyResults, visitor);
// With input validation, we sometimes may suppress this exception and not see it on second time
// Caching it takes care of this
if (dependencyVerificationException != null) {
throw dependencyVerificationException;
} else {
try {
dependencyVerificationOverride.artifactsAccessed(configuration.getDisplayName());
} catch (DependencyVerificationException e) {
dependencyVerificationException = e;
throw e;
}
}
context.setResult(RESULT);
}
@Override
public BuildOperationDescriptor.Builder description() {
String displayName = "Resolve files of " + configuration.getIdentityPath();
return BuildOperationDescriptor
.displayName(displayName)
.progressDisplayName(displayName)
.details(new ResolveArtifactsDetails(configuration.getPath()));
}
});
}
private static class ResolveArtifactsDetails implements ResolveArtifactsBuildOperationType.Details {
private final String configuration;
public ResolveArtifactsDetails(String configuration) {
this.configuration = configuration;
}
@Override
public String getConfigurationPath() {
return configuration;
}
}
/**
* Recursive, includes unsuccessfully resolved artifacts
*
* @param dependencySpec dependency spec
*/
private void visitArtifacts(Spec super Dependency> dependencySpec, SelectedArtifactResults artifactResults, VisitedFileDependencyResults fileDependencyResults, ArtifactVisitor visitor) {
//this is not very nice might be good enough until we get rid of ResolvedConfiguration and friends
//avoid traversing the graph causing the full ResolvedDependency graph to be loaded for the most typical scenario
if (dependencySpec == Specs.SATISFIES_ALL) {
ParallelResolveArtifactSet.wrap(artifactResults.getArtifacts(), buildOperationExecutor).visit(visitor);
return;
}
List artifactSets = new ArrayList<>();
for (Map.Entry entry : fileDependencyResults.getFirstLevelFiles().entrySet()) {
if (dependencySpec.isSatisfiedBy(entry.getKey())) {
artifactSets.add(artifactResults.getArtifactsWithId(entry.getValue()));
}
}
CachingDirectedGraphWalker walker = new CachingDirectedGraphWalker<>(new ResolvedDependencyArtifactsGraph(artifactSets));
for (DependencyGraphNodeResult node : getFirstLevelNodes(dependencySpec)) {
walker.add(node);
}
walker.findValues();
ParallelResolveArtifactSet.wrap(CompositeResolvedArtifactSet.of(artifactSets), buildOperationExecutor).visit(visitor);
}
public ConfigurationInternal getConfiguration() {
return configuration;
}
@Override
public Set getFirstLevelModuleDependencies() {
return getFirstLevelModuleDependencies(Specs.SATISFIES_ALL);
}
private static class LenientArtifactCollectingVisitor implements ArtifactVisitor {
final Set artifacts = Sets.newLinkedHashSet();
final Set files = Sets.newLinkedHashSet();
@Override
public void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, List extends Capability> capabilities, ResolvableArtifact artifact) {
try {
ResolvedArtifact resolvedArtifact = artifact.toPublicView();
files.add(resolvedArtifact.getFile());
artifacts.add(resolvedArtifact);
} catch (org.gradle.internal.resolve.ArtifactResolveException e) {
//ignore
}
}
@Override
public FileCollectionStructureVisitor.VisitType prepareForVisit(FileCollectionInternal.Source source) {
if (source instanceof LocalDependencyFiles) {
return FileCollectionStructureVisitor.VisitType.NoContents;
}
return FileCollectionStructureVisitor.VisitType.Visit;
}
@Override
public boolean requireArtifactFiles() {
return false;
}
@Override
public void visitFailure(Throwable failure) {
throw UncheckedException.throwAsUncheckedException(failure);
}
}
private static class LenientFilesAndArtifactResolveVisitor extends LenientArtifactCollectingVisitor {
@Override
public FileCollectionStructureVisitor.VisitType prepareForVisit(FileCollectionInternal.Source source) {
return FileCollectionStructureVisitor.VisitType.Visit;
}
}
public static class ArtifactResolveException extends ResolveException {
private final String type;
private final String displayName;
public ArtifactResolveException(String type, String path, String displayName, Iterable failures) {
super(path, failures);
this.type = type;
this.displayName = displayName;
}
// Need to override as error message is hardcoded in constructor of public type ResolveException
@Override
public String getMessage() {
return String.format("Could not resolve all %s for %s.", type, displayName);
}
}
private static class ResolvedDependencyArtifactsGraph implements DirectedGraphWithEdgeValues {
private final List dest;
ResolvedDependencyArtifactsGraph(List dest) {
this.dest = dest;
}
@Override
public void getNodeValues(DependencyGraphNodeResult node, Collection super ResolvedArtifact> values, Collection super DependencyGraphNodeResult> connectedNodes) {
connectedNodes.addAll(node.getOutgoingEdges());
dest.add(node.getArtifactsForNode());
}
@Override
public void getEdgeValues(DependencyGraphNodeResult from, DependencyGraphNodeResult to, Collection values) {
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy