aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/java_tools/import_deps_checker/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java_tools/import_deps_checker/java')
-rw-r--r--src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/AbstractClassEntryState.java16
-rw-r--r--src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassCache.java59
-rw-r--r--src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassInfo.java12
-rw-r--r--src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/DepsCheckerClassVisitor.java19
-rw-r--r--src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ImportDepsChecker.java142
-rw-r--r--src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ResolutionFailureChain.java82
-rw-r--r--src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ResultCollector.java3
7 files changed, 235 insertions, 98 deletions
diff --git a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/AbstractClassEntryState.java b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/AbstractClassEntryState.java
index 209bf83fcb..eeb162f3ba 100644
--- a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/AbstractClassEntryState.java
+++ b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/AbstractClassEntryState.java
@@ -13,7 +13,6 @@
// limitations under the License.
package com.google.devtools.build.importdeps;
-import static com.google.common.base.Preconditions.checkArgument;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
@@ -100,20 +99,15 @@ public abstract class AbstractClassEntryState {
public abstract static class IncompleteState extends AbstractClassEntryState {
public static IncompleteState create(
- ClassInfo classInfo, ImmutableList<String> resolutionFailurePath) {
- checkArgument(
- !resolutionFailurePath.isEmpty(),
- "The resolution path should contain at least one element, the missing ancestor. %s",
- resolutionFailurePath);
+ ClassInfo classInfo, ResolutionFailureChain resolutionFailureChain) {
return new AutoValue_AbstractClassEntryState_IncompleteState(
- Optional.of(classInfo), resolutionFailurePath);
+ Optional.of(classInfo), resolutionFailureChain);
}
- public abstract ImmutableList<String> getResolutionFailurePath();
+ public abstract ResolutionFailureChain resolutionFailureChain();
- public String getMissingAncestor() {
- ImmutableList<String> path = getResolutionFailurePath();
- return path.get(path.size() - 1);
+ public ImmutableList<String> missingAncestors() {
+ return resolutionFailureChain().missingClasses();
}
@Override
diff --git a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassCache.java b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassCache.java
index 232e69b936..292d5752b2 100644
--- a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassCache.java
+++ b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassCache.java
@@ -37,6 +37,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -145,43 +146,29 @@ public final class ClassCache implements Closeable {
classEntry.zipFile.getEntry(entryName), "The zip entry %s is null.", entryName);
try (InputStream inputStream = classEntry.zipFile.getInputStream(zipEntry)) {
ClassReader classReader = new ClassReader(inputStream);
- ImmutableList<String> resolutionFailurePath = null;
+ ImmutableList.Builder<ResolutionFailureChain> resolutionFailureChainsBuilder =
+ ImmutableList.builder();
for (String superName :
combineWithoutNull(classReader.getSuperName(), classReader.getInterfaces())) {
- LazyClassEntry superClassEntry = lazyClasspath.getLazyEntry(superName);
-
- if (superClassEntry == null) {
- resolutionFailurePath = ImmutableList.of(superName);
- break;
- } else {
- resolveClassEntry(superClassEntry, lazyClasspath);
- AbstractClassEntryState superState = superClassEntry.state;
- if (superState instanceof ExistingState) {
- // Do nothing. Good to proceed.
- continue;
- } else if (superState instanceof IncompleteState) {
- resolutionFailurePath =
- ImmutableList.<String>builder()
- .add(superName)
- .addAll(((IncompleteState) superState).getResolutionFailurePath())
- .build();
- break;
- } else {
- throw new RuntimeException("Cannot reach here. superState is " + superState);
- }
- }
+ Optional<ResolutionFailureChain> failurePath =
+ resolveSuperClassEntry(superName, lazyClasspath);
+ failurePath.map(resolutionFailureChainsBuilder::add);
}
ClassInfoBuilder classInfoBuilder =
new ClassInfoBuilder().setJarPath(classEntry.jarPath).setDirect(classEntry.direct);
classReader.accept(classInfoBuilder, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
- if (resolutionFailurePath == null) {
+
+ ImmutableList<ResolutionFailureChain> resolutionFailureChains =
+ resolutionFailureChainsBuilder.build();
+ if (resolutionFailureChains.isEmpty()) {
classEntry.state =
ExistingState.create(classInfoBuilder.build(lazyClasspath, /*incomplete=*/ false));
} else {
+ ClassInfo classInfo = classInfoBuilder.build(lazyClasspath, /*incomplete=*/ true);
classEntry.state =
IncompleteState.create(
- classInfoBuilder.build(lazyClasspath, /*incomplete=*/ true),
- resolutionFailurePath);
+ classInfo,
+ ResolutionFailureChain.createWithParent(classInfo, resolutionFailureChains));
}
} catch (IOException e) {
throw new RuntimeException("Error when resolving class entry " + entryName);
@@ -193,6 +180,26 @@ public final class ClassCache implements Closeable {
throw e;
}
}
+
+ private static Optional<ResolutionFailureChain> resolveSuperClassEntry(
+ String superName, LazyClasspath lazyClasspath) {
+ LazyClassEntry superClassEntry = lazyClasspath.getLazyEntry(superName);
+
+ if (superClassEntry == null) {
+ return Optional.of(ResolutionFailureChain.createMissingClass(superName));
+ } else {
+ resolveClassEntry(superClassEntry, lazyClasspath);
+ AbstractClassEntryState superState = superClassEntry.state;
+ if (superState instanceof ExistingState) {
+ // Do nothing. Good to proceed.
+ return Optional.empty();
+ } else if (superState instanceof IncompleteState) {
+ return Optional.of(superState.asIncompleteState().resolutionFailureChain());
+ } else {
+ throw new RuntimeException("Cannot reach here. superState is " + superState);
+ }
+ }
+ }
}
private static ImmutableList<String> combineWithoutNull(
diff --git a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassInfo.java b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassInfo.java
index 0a1e921ad0..3d5a5a6432 100644
--- a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassInfo.java
+++ b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ClassInfo.java
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.base.Strings;
+import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
@@ -27,7 +28,7 @@ import java.nio.file.Path;
* classes.
*/
@AutoValue
-public abstract class ClassInfo {
+public abstract class ClassInfo implements Comparable<ClassInfo> {
public static ClassInfo create(
String internalName,
@@ -64,6 +65,15 @@ public abstract class ClassInfo {
return false;
}
+ @Override
+ public int compareTo(ClassInfo other) {
+ return ComparisonChain.start()
+ .compare(internalName(), other.internalName())
+ .compare(jarPath(), other.jarPath())
+ .compareFalseFirst(directDep(), other.directDep())
+ .result();
+ }
+
/** A member is either a method or a field. */
@AutoValue
public abstract static class MemberInfo {
diff --git a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/DepsCheckerClassVisitor.java b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/DepsCheckerClassVisitor.java
index b57c273e10..b536a39633 100644
--- a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/DepsCheckerClassVisitor.java
+++ b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/DepsCheckerClassVisitor.java
@@ -174,12 +174,19 @@ public class DepsCheckerClassVisitor extends ClassVisitor {
resultCollector.addMissingOrIncompleteClass(internalName, state);
} else {
if (state.isIncompleteState()) {
- String missingAncestor = state.asIncompleteState().getMissingAncestor();
- AbstractClassEntryState ancestorState = classCache.getClassState(missingAncestor);
- checkState(
- ancestorState.isMissingState(), "The ancestor should be missing. %s", ancestorState);
- resultCollector.addMissingOrIncompleteClass(missingAncestor, ancestorState);
- resultCollector.addMissingOrIncompleteClass(internalName, state);
+ state
+ .asIncompleteState()
+ .missingAncestors()
+ .forEach(
+ missingAncestor -> {
+ AbstractClassEntryState ancestorState = classCache.getClassState(missingAncestor);
+ checkState(
+ ancestorState.isMissingState(),
+ "The ancestor should be missing. %s",
+ ancestorState);
+ resultCollector.addMissingOrIncompleteClass(missingAncestor, ancestorState);
+ resultCollector.addMissingOrIncompleteClass(internalName, state);
+ });
}
ClassInfo info = state.classInfo().get();
if (!info.directDep()) {
diff --git a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ImportDepsChecker.java b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ImportDepsChecker.java
index 9276d92eb6..7e837a8f53 100644
--- a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ImportDepsChecker.java
+++ b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ImportDepsChecker.java
@@ -13,8 +13,8 @@
// limitations under the License.
package com.google.devtools.build.importdeps;
-import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.importdeps.AbstractClassEntryState.IncompleteState;
import com.google.devtools.build.importdeps.ResultCollector.MissingMember;
@@ -27,9 +27,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Path;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.Objects;
import java.util.jar.JarFile;
-import java.util.stream.Collectors;
import java.util.zip.ZipFile;
import javax.annotation.Nullable;
import org.objectweb.asm.ClassReader;
@@ -108,61 +109,21 @@ public final class ImportDepsChecker implements Closeable {
public String computeResultOutput(String ruleLabel) {
StringBuilder builder = new StringBuilder();
ImmutableList<String> missingClasses = resultCollector.getSortedMissingClassInternalNames();
- for (String missing : missingClasses) {
- builder.append("Missing ").append(missing.replace('/', '.')).append('\n');
- }
+ outputMissingClasses(builder, missingClasses);
ImmutableList<IncompleteState> incompleteClasses = resultCollector.getSortedIncompleteClasses();
- for (IncompleteState incomplete : incompleteClasses) {
- builder
- .append("Incomplete ancestor classpath for ")
- .append(incomplete.classInfo().get().internalName().replace('/', '.'))
- .append('\n');
+ outputIncompleteClasses(builder, incompleteClasses);
- ImmutableList<String> failurePath = incomplete.getResolutionFailurePath();
- checkState(!failurePath.isEmpty(), "The resolution failure path is empty. %s", failurePath);
- builder
- .append(INDENT)
- .append("missing ancestor: ")
- .append(failurePath.get(failurePath.size() - 1).replace('/', '.'))
- .append('\n');
- builder
- .append(INDENT)
- .append("resolution failure path: ")
- .append(
- failurePath
- .stream()
- .map(internalName -> internalName.replace('/', '.'))
- .collect(Collectors.joining(" -> ")))
- .append('\n');
- }
ImmutableList<MissingMember> missingMembers = resultCollector.getSortedMissingMembers();
- for (MissingMember missing : missingMembers) {
- builder
- .append("Missing member '")
- .append(missing.memberName())
- .append("' in class ")
- .append(missing.owner().replace('/', '.'))
- .append(" : name=")
- .append(missing.memberName())
- .append(", descriptor=")
- .append(missing.descriptor())
- .append('\n');
- }
- if (missingClasses.size() + incompleteClasses.size() + missingMembers.size() != 0) {
- builder
- .append("===Total===\n")
- .append("missing=")
- .append(missingClasses.size())
- .append('\n')
- .append("incomplete=")
- .append(incompleteClasses.size())
- .append('\n')
- .append("missing_members=")
- .append(missingMembers.size())
- .append('\n');
- }
+ outputMissingMembers(builder, missingMembers);
+
+ outputStatistics(builder, missingClasses, incompleteClasses, missingMembers);
+
+ emitAddDepCommandForIndirectJars(ruleLabel, builder);
+ return builder.toString();
+ }
+ private void emitAddDepCommandForIndirectJars(String ruleLabel, StringBuilder builder) {
ImmutableList<Path> indirectJars = resultCollector.getSortedIndirectDeps();
if (!indirectJars.isEmpty()) {
ImmutableList<String> labels = extractLabels(indirectJars);
@@ -186,7 +147,82 @@ public final class ImportDepsChecker implements Closeable {
builder.append(ruleLabel).append('\n');
}
}
- return builder.toString();
+ }
+
+ private void outputStatistics(
+ StringBuilder builder,
+ ImmutableList<String> missingClasses,
+ ImmutableList<IncompleteState> incompleteClasses,
+ ImmutableList<MissingMember> missingMembers) {
+ if (missingClasses.size() + incompleteClasses.size() + missingMembers.size() != 0) {
+ builder
+ .append("===Total===\n")
+ .append("missing=")
+ .append(missingClasses.size())
+ .append('\n')
+ .append("incomplete=")
+ .append(incompleteClasses.size())
+ .append('\n')
+ .append("missing_members=")
+ .append(missingMembers.size())
+ .append('\n');
+ }
+ }
+
+ private void outputMissingMembers(
+ StringBuilder builder, ImmutableList<MissingMember> missingMembers) {
+ for (MissingMember missing : missingMembers) {
+ builder
+ .append("Missing member '")
+ .append(missing.memberName())
+ .append("' in class ")
+ .append(missing.owner().replace('/', '.'))
+ .append(" : name=")
+ .append(missing.memberName())
+ .append(", descriptor=")
+ .append(missing.descriptor())
+ .append('\n');
+ }
+ }
+
+ private void outputIncompleteClasses(
+ StringBuilder builder, ImmutableList<IncompleteState> incompleteClasses) {
+ new LinkedHashMap<>();
+ HashMultimap<String, ClassInfo> map = HashMultimap.create();
+ for (IncompleteState incomplete : incompleteClasses) {
+ ResolutionFailureChain chain = incomplete.resolutionFailureChain();
+ map.putAll(chain.getMissingClassesWithSubclasses());
+ }
+ map.asMap()
+ .entrySet()
+ .stream()
+ .sorted(Map.Entry.comparingByKey())
+ .forEach(
+ entry -> {
+ builder
+ .append("Indirectly missing class ")
+ .append(entry.getKey().replace('/', '.'))
+ .append(". Referenced by:")
+ .append('\n');
+ entry
+ .getValue()
+ .stream()
+ .distinct()
+ .sorted()
+ .forEach(
+ reference -> {
+ builder
+ .append(INDENT)
+ .append(reference.internalName().replace('/', '.'))
+ .append('\n');
+ });
+ });
+ }
+
+ private void outputMissingClasses(StringBuilder builder, ImmutableList<String> missingClasses) {
+ for (String missing : missingClasses) {
+ builder.append("Missing ").append(missing.replace('/', '.')).append('\n');
+ }
}
private static ImmutableList<String> extractLabels(ImmutableList<Path> jars) {
diff --git a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ResolutionFailureChain.java b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ResolutionFailureChain.java
new file mode 100644
index 0000000000..3ac67c1daf
--- /dev/null
+++ b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ResolutionFailureChain.java
@@ -0,0 +1,82 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// 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.google.devtools.build.importdeps;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import javax.annotation.Nullable;
+
+/** The resolution failure path. */
+@AutoValue
+public abstract class ResolutionFailureChain {
+
+ public static ResolutionFailureChain createMissingClass(String missingClass) {
+ return new AutoValue_ResolutionFailureChain(
+ ImmutableList.of(missingClass),
+ /*resolutionStartClass=*/ null,
+ /*parentChains=*/ ImmutableList.of());
+ }
+
+ public static ResolutionFailureChain createWithParent(
+ ClassInfo resolutionStartClass, ImmutableList<ResolutionFailureChain> parentChains) {
+ Preconditions.checkArgument(!parentChains.isEmpty(), "The parentChains cannot be empty.");
+ return new AutoValue_ResolutionFailureChain(
+ parentChains
+ .stream()
+ .flatMap(chain -> chain.missingClasses().stream())
+ .sorted()
+ .distinct()
+ .collect(ImmutableList.toImmutableList()),
+ resolutionStartClass,
+ parentChains);
+ }
+
+ /** The missing class that causes the resolution failure. */
+ public abstract ImmutableList<String> missingClasses();
+
+ /** The start of this resolution chain. */
+ @Nullable
+ public abstract ClassInfo resolutionStartClass();
+
+ /** The resolution chain of the parent class. */
+ public abstract ImmutableList<ResolutionFailureChain> parentChains();
+
+ /** For all the missing classes, represent the first chains that lead to the missing classes. */
+ public ImmutableMultimap<String, ClassInfo> getMissingClassesWithSubclasses() {
+ ImmutableMultimap.Builder<String, ClassInfo> result = ImmutableMultimap.builder();
+ getMissingClassesWithSubclasses(resolutionStartClass(), this.parentChains(), result);
+ return result.build();
+ }
+
+ private static void getMissingClassesWithSubclasses(
+ ClassInfo subclass,
+ ImmutableList<ResolutionFailureChain> parentChains,
+ ImmutableMultimap.Builder<String, ClassInfo> result) {
+ for (ResolutionFailureChain parentChain : parentChains) {
+ if (parentChain.resolutionStartClass() == null) {
+ checkState(
+ parentChain.parentChains().isEmpty() && parentChain.missingClasses().size() == 1);
+ result.put(parentChain.missingClasses().get(0), subclass);
+ } else {
+ checkState(!parentChain.parentChains().isEmpty());
+ getMissingClassesWithSubclasses(
+ parentChain.resolutionStartClass(), parentChain.parentChains(), result);
+ }
+ }
+ }
+}
diff --git a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ResultCollector.java b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ResultCollector.java
index 5288799f07..7a1fe39b0a 100644
--- a/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ResultCollector.java
+++ b/src/java_tools/import_deps_checker/java/com/google/devtools/build/importdeps/ResultCollector.java
@@ -48,7 +48,8 @@ public class ResultCollector {
"The old value and the new value are not the same object. old=%s, new=%s",
oldValue,
state);
- missingClasss.add(state.asIncompleteState().getMissingAncestor()); // Add the real missing.
+ // Add the real missing.
+ state.asIncompleteState().missingAncestors().forEach(missingClasss::add);
} else if (state.isMissingState()) {
missingClasss.add(internalName);
} else {