-
Notifications
You must be signed in to change notification settings - Fork 80
Adding guard when annotating linkage errors #2196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,13 +19,17 @@ | |
| import static com.google.common.base.Preconditions.checkNotNull; | ||
|
|
||
| import com.google.cloud.tools.opensource.dependencies.DependencyPath; | ||
| import com.google.common.collect.ImmutableList; | ||
| import java.io.IOException; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.logging.Logger; | ||
| import org.eclipse.aether.artifact.Artifact; | ||
|
|
||
| /** Annotates {@link LinkageProblem}s with {@link LinkageProblemCause}s. */ | ||
| public final class LinkageProblemCauseAnnotator { | ||
| private static final Logger logger = | ||
| Logger.getLogger(LinkageProblemCauseAnnotator.class.getName()); | ||
|
|
||
| private LinkageProblemCauseAnnotator() {} | ||
|
|
||
|
|
@@ -35,74 +39,100 @@ private LinkageProblemCauseAnnotator() {} | |
| * @param classPathBuilder class path builder to resolve dependency graphs | ||
| * @param rootResult the class path used for generating the linkage problems | ||
| * @param linkageProblems linkage problems to annotate | ||
| * @throws IOException when there is a problem reading JAR files | ||
| */ | ||
| public static void annotate( | ||
| ClassPathBuilder classPathBuilder, | ||
| ClassPathResult rootResult, | ||
| Iterable<LinkageProblem> linkageProblems) | ||
| throws IOException { | ||
| Iterable<LinkageProblem> linkageProblems) { | ||
| checkNotNull(classPathBuilder); | ||
| checkNotNull(rootResult); | ||
| checkNotNull(linkageProblems); | ||
|
|
||
| Map<Artifact, ClassPathResult> cache = new HashMap<>(); | ||
| Map<Artifact, ClassPathResult> artifactResolutionCache = new HashMap<>(); | ||
| for (LinkageProblem linkageProblem : linkageProblems) { | ||
| ClassFile sourceClass = linkageProblem.getSourceClass(); | ||
| ClassPathEntry sourceEntry = sourceClass.getClassPathEntry(); | ||
| // Annotating linkage errors is a nice-to-have feature for Linkage Checker plugins. Let's not | ||
| // fail the entire process if there are problems, such as classPathBuilder unable to resolve | ||
| // one artifact or to return correct dependency tree. | ||
| try { | ||
| annotateProblem(classPathBuilder, rootResult, artifactResolutionCache, linkageProblem); | ||
| } catch (Exception ex) { | ||
| logger.warning("Failed to annotate: " + linkageProblem); | ||
| linkageProblem.setCause(UnknownCause.getInstance()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Artifact sourceArtifact = sourceEntry.getArtifact(); | ||
| private static void annotateProblem( | ||
| ClassPathBuilder classPathBuilder, | ||
| ClassPathResult rootResult, | ||
| Map<Artifact, ClassPathResult> artifactResolutionCache, | ||
| LinkageProblem linkageProblem) | ||
| throws IOException { | ||
| ClassFile sourceClass = linkageProblem.getSourceClass(); | ||
| ClassPathEntry sourceEntry = sourceClass.getClassPathEntry(); | ||
|
|
||
| ClassPathResult subtreeResult = cache.get(sourceArtifact); | ||
| if (subtreeResult == null) { | ||
| // Resolves the dependency graph with the source artifact at the root. | ||
| subtreeResult = classPathBuilder.resolveWithMaven(sourceArtifact); | ||
| cache.put(sourceArtifact, subtreeResult); | ||
| } | ||
| Artifact sourceArtifact = sourceEntry.getArtifact(); | ||
|
|
||
| Symbol symbol = linkageProblem.getSymbol(); | ||
| ClassPathEntry entryInSubtree = subtreeResult.findEntryBySymbol(symbol); | ||
| if (entryInSubtree == null) { | ||
| linkageProblem.setCause(UnknownCause.getInstance()); | ||
| } else { | ||
| Artifact artifactInSubtree = entryInSubtree.getArtifact(); | ||
| DependencyPath pathToSourceEntry = rootResult.getDependencyPaths(sourceEntry).get(0); | ||
| DependencyPath pathFromSourceEntryToUnselectedEntry = | ||
| subtreeResult.getDependencyPaths(entryInSubtree).get(0); | ||
| DependencyPath pathToUnselectedEntry = | ||
| pathToSourceEntry.concat(pathFromSourceEntryToUnselectedEntry); | ||
| ClassPathResult subtreeResult = artifactResolutionCache.get(sourceArtifact); | ||
| if (subtreeResult == null) { | ||
| // Resolves the dependency graph with the source artifact at the root. | ||
| subtreeResult = classPathBuilder.resolveWithMaven(sourceArtifact); | ||
| artifactResolutionCache.put(sourceArtifact, subtreeResult); | ||
| } | ||
|
|
||
| ClassPathEntry selectedEntry = | ||
| rootResult.findEntryById( | ||
| artifactInSubtree.getGroupId(), artifactInSubtree.getArtifactId()); | ||
| if (selectedEntry != null) { | ||
| Artifact selectedArtifact = selectedEntry.getArtifact(); | ||
| if (!selectedArtifact.getVersion().equals(artifactInSubtree.getVersion())) { | ||
| // Different version of that artifact is selected in rootResult | ||
| linkageProblem.setCause( | ||
| new DependencyConflict( | ||
| linkageProblem, | ||
| rootResult.getDependencyPaths(selectedEntry).get(0), | ||
| pathToUnselectedEntry)); | ||
| } else { | ||
| // A linkage error was already there when sourceArtifact was built. | ||
| linkageProblem.setCause(UnknownCause.getInstance()); | ||
| } | ||
| } else { | ||
| // No artifact that matches groupId and artifactId in rootResult. | ||
| Symbol symbol = linkageProblem.getSymbol(); | ||
| ClassPathEntry entryInSubtree = subtreeResult.findEntryBySymbol(symbol); | ||
| if (entryInSubtree == null) { | ||
| linkageProblem.setCause(UnknownCause.getInstance()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be more clear to early
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice idea. Updated. |
||
| return; | ||
| } | ||
| Artifact artifactInSubtree = entryInSubtree.getArtifact(); | ||
| ImmutableList<DependencyPath> dependencyPathsToSource = | ||
| rootResult.getDependencyPaths(sourceEntry); | ||
| if (dependencyPathsToSource.isEmpty()) { | ||
| // When an artifact is excluded, it's possible to have no dependency path to sourceEntry. | ||
| linkageProblem.setCause(UnknownCause.getInstance()); | ||
| return; | ||
| } | ||
| DependencyPath pathToSourceEntry = dependencyPathsToSource.get(0); | ||
| DependencyPath pathFromSourceEntryToUnselectedEntry = | ||
| subtreeResult.getDependencyPaths(entryInSubtree).get(0); | ||
| DependencyPath pathToUnselectedEntry = | ||
| pathToSourceEntry.concat(pathFromSourceEntryToUnselectedEntry); | ||
|
|
||
| // Checking exclusion elements in the dependency path | ||
| Artifact excludingArtifact = | ||
| pathToSourceEntry.findExclusion( | ||
| artifactInSubtree.getGroupId(), artifactInSubtree.getArtifactId()); | ||
| if (excludingArtifact != null) { | ||
| linkageProblem.setCause( | ||
| new ExcludedDependency(pathToUnselectedEntry, excludingArtifact)); | ||
| } else { | ||
| linkageProblem.setCause(new MissingDependency(pathToUnselectedEntry)); | ||
| } | ||
| ClassPathEntry selectedEntry = | ||
| rootResult.findEntryById( | ||
| artifactInSubtree.getGroupId(), artifactInSubtree.getArtifactId()); | ||
| if (selectedEntry != null) { | ||
| Artifact selectedArtifact = selectedEntry.getArtifact(); | ||
| if (!selectedArtifact.getVersion().equals(artifactInSubtree.getVersion())) { | ||
| // Different version of that artifact is selected in rootResult | ||
| ImmutableList<DependencyPath> pathToSelectedArtifact = | ||
| rootResult.getDependencyPaths(selectedEntry); | ||
| if (pathToSelectedArtifact.isEmpty()) { | ||
| linkageProblem.setCause(UnknownCause.getInstance()); | ||
| return; | ||
| } | ||
| linkageProblem.setCause( | ||
| new DependencyConflict( | ||
| linkageProblem, pathToSelectedArtifact.get(0), pathToUnselectedEntry)); | ||
| } else { | ||
| // A linkage error was already there when sourceArtifact was built. | ||
| linkageProblem.setCause(UnknownCause.getInstance()); | ||
| } | ||
| } else { | ||
| // No artifact that matches groupId and artifactId in rootResult. | ||
|
|
||
| // Checking exclusion elements in the dependency path | ||
| Artifact excludingArtifact = | ||
| pathToSourceEntry.findExclusion( | ||
| artifactInSubtree.getGroupId(), artifactInSubtree.getArtifactId()); | ||
| if (excludingArtifact != null) { | ||
| linkageProblem.setCause(new ExcludedDependency(pathToUnselectedEntry, excludingArtifact)); | ||
| } else { | ||
| linkageProblem.setCause(new MissingDependency(pathToUnselectedEntry)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,7 @@ | |
| import com.google.common.collect.ImmutableSet; | ||
| import com.google.common.collect.ListMultimap; | ||
| import com.google.common.collect.MultimapBuilder; | ||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.nio.file.Path; | ||
| import java.nio.file.Paths; | ||
|
|
@@ -314,6 +315,20 @@ private String dependencyPathToArtifacts( | |
| return output.toString(); | ||
| } | ||
|
|
||
| private static Artifact artifactWithPomFrom(ResolvedDependency resolvedDependency) { | ||
| ModuleVersionIdentifier moduleVersionId = resolvedDependency.getModule().getId(); | ||
| DefaultArtifact artifact = | ||
| new DefaultArtifact( | ||
| moduleVersionId.getGroup(), | ||
| moduleVersionId.getName(), | ||
| null, | ||
| "pom", | ||
| moduleVersionId.getVersion(), | ||
| null, | ||
| (File) null); // no JAR file | ||
| return artifact; | ||
| } | ||
|
|
||
| private static Artifact artifactFrom( | ||
| ResolvedDependency resolvedDependency, ResolvedArtifact resolvedArtifact) { | ||
| ModuleVersionIdentifier moduleVersionId = resolvedDependency.getModule().getId(); | ||
|
|
@@ -357,6 +372,7 @@ private DependencyGraph createDependencyGraph(ResolvedConfiguration configuratio | |
| PathToNode<ResolvedDependency> item = queue.poll(); | ||
| ResolvedDependency node = item.getNode(); | ||
|
|
||
| // Never null | ||
| DependencyPath parentPath = item.getParentPath(); | ||
|
|
||
| // If there are multiple artifacts (with different classifiers) in this node, then the path is | ||
|
|
@@ -372,18 +388,15 @@ private DependencyGraph createDependencyGraph(ResolvedConfiguration configuratio | |
| getLogger() | ||
| .warn( | ||
| "The dependency node " + node.getName() + " does not have any artifact. Skipping."); | ||
| continue; | ||
| path = parentPath.append(new Dependency(artifactWithPomFrom(node), "compile")); | ||
| } else { | ||
| // For artifacts with classifiers, there can be multiple resolved artifacts for one node | ||
| for (ResolvedArtifact artifact : moduleArtifacts) { | ||
| path = parentPath.append(dependencyFrom(node, artifact)); | ||
| graph.addPath(path); | ||
| } | ||
| } | ||
|
|
||
| // For artifacts with classifiers, there can be multiple resolved artifacts for one node | ||
| for (ResolvedArtifact artifact : moduleArtifacts) { | ||
| // parentPath is null for the first item | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found the first item's parentPath is actually set line 352. It's never null. |
||
| path = | ||
| parentPath == null | ||
| ? new DependencyPath(artifactFrom(node, artifact)) | ||
| : parentPath.append(dependencyFrom(node, artifact)); | ||
| graph.addPath(path); | ||
| } | ||
|
|
||
| for (ResolvedDependency child : node.getChildren()) { | ||
| if (visited.add(child)) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A user reported that this threw ArrayIndexOutOfBoundsException.