Navigation Menu

Skip to content

Commit

Permalink
8225773: jdeps --check produces NPE if there are missing module depen…
Browse files Browse the repository at this point in the history
…dences

Reviewed-by: alanb
  • Loading branch information
Mandy Chung committed Jan 10, 2020
1 parent b7e74ef commit fe8e1aa
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 27 deletions.
Expand Up @@ -986,7 +986,7 @@ boolean run(JdepsConfiguration config) throws IOException {
throw new UncheckedBadArgs(new BadArgs("err.invalid.options",
list, "--check"));
}
return new ModuleAnalyzer(config, log, modules).run();
return new ModuleAnalyzer(config, log, modules).run(options.ignoreMissingDeps);
}

/*
Expand Down
76 changes: 53 additions & 23 deletions src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleAnalyzer.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -24,22 +24,16 @@
*/
package com.sun.tools.jdeps;

import static com.sun.tools.jdeps.Graph.*;
import static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
import static com.sun.tools.jdeps.Module.*;
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
import static java.util.stream.Collectors.*;

import com.sun.tools.classfile.Dependency;
import com.sun.tools.jdeps.JdepsTask.BadArgs;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.module.ModuleDescriptor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -80,23 +74,32 @@ public ModuleAnalyzer(JdepsConfiguration config,
}
}

public boolean run() throws IOException {
public boolean run(boolean ignoreMissingDeps) throws IOException {
try {
// compute "requires transitive" dependences
modules.values().forEach(ModuleDeps::computeRequiresTransitive);

modules.values().forEach(md -> {
for (ModuleDeps md: modules.values()) {
// compute "requires transitive" dependences
md.computeRequiresTransitive(ignoreMissingDeps);
// compute "requires" dependences
md.computeRequires();
md.computeRequires(ignoreMissingDeps);
// print module descriptor
md.printModuleDescriptor();

// apply transitive reduction and reports recommended requires.
md.analyzeDeps();
});
boolean ok = md.analyzeDeps();
if (!ok) return false;

if (ignoreMissingDeps && md.hasMissingDependencies()) {
log.format("Warning: --ignore-missing-deps specified. Missing dependencies from %s are ignored%n",
md.root.name());
}
}
} finally {
dependencyFinder.shutdown();
}
return true;
}


class ModuleDeps {
final Module root;
Set<Module> requiresTransitive;
Expand All @@ -110,23 +113,22 @@ class ModuleDeps {
/**
* Compute 'requires transitive' dependences by analyzing API dependencies
*/
private void computeRequiresTransitive() {
private void computeRequiresTransitive(boolean ignoreMissingDeps) {
// record requires transitive
this.requiresTransitive = computeRequires(true)
this.requiresTransitive = computeRequires(true, ignoreMissingDeps)
.filter(m -> !m.name().equals(JAVA_BASE))
.collect(toSet());

trace("requires transitive: %s%n", requiresTransitive);
}

private void computeRequires() {
this.requires = computeRequires(false).collect(toSet());
private void computeRequires(boolean ignoreMissingDeps) {
this.requires = computeRequires(false, ignoreMissingDeps).collect(toSet());
trace("requires: %s%n", requires);
}

private Stream<Module> computeRequires(boolean apionly) {
private Stream<Module> computeRequires(boolean apionly, boolean ignoreMissingDeps) {
// analyze all classes

if (apionly) {
dependencyFinder.parseExportedAPIs(Stream.of(root));
} else {
Expand All @@ -135,9 +137,14 @@ private Stream<Module> computeRequires(boolean apionly) {

// find the modules of all the dependencies found
return dependencyFinder.getDependences(root)
.filter(a -> !(ignoreMissingDeps && Analyzer.notFound(a)))
.map(Archive::getModule);
}

boolean hasMissingDependencies() {
return dependencyFinder.getDependences(root).anyMatch(Analyzer::notFound);
}

ModuleDescriptor descriptor() {
return descriptor(requiresTransitive, requires);
}
Expand Down Expand Up @@ -196,12 +203,30 @@ ModuleDescriptor reduced() {
return descriptor(requiresTransitive, g.adjacentNodes(root));
}

private void showMissingDeps() {
// build the analyzer if there are missing dependences
Analyzer analyzer = new Analyzer(configuration, Analyzer.Type.CLASS, DEFAULT_FILTER);
analyzer.run(Set.of(root), dependencyFinder.locationToArchive());
log.println("Error: Missing dependencies: classes not found from the module path.");
Analyzer.Visitor visitor = new Analyzer.Visitor() {
@Override
public void visitDependence(String origin, Archive originArchive, String target, Archive targetArchive) {
log.format(" %-50s -> %-50s %s%n", origin, target, targetArchive.getName());
}
};
analyzer.visitDependences(root, visitor, Analyzer.Type.VERBOSE, Analyzer::notFound);
log.println();
}

/**
* Apply transitive reduction on the resulting graph and reports
* recommended requires.
*/
private void analyzeDeps() {
printModuleDescriptor(log, root);
private boolean analyzeDeps() {
if (requires.stream().anyMatch(m -> m == UNNAMED_MODULE)) {
showMissingDeps();
return false;
}

ModuleDescriptor analyzedDescriptor = descriptor();
if (!matches(root.descriptor(), analyzedDescriptor)) {
Expand All @@ -223,6 +248,7 @@ private void analyzeDeps() {

checkQualifiedExports();
log.println();
return true;
}

private void checkQualifiedExports() {
Expand All @@ -239,6 +265,10 @@ private void checkQualifiedExports() {
.collect(joining(","))));
}

void printModuleDescriptor() {
printModuleDescriptor(log, root);
}

private void printModuleDescriptor(PrintWriter out, Module module) {
ModuleDescriptor descriptor = module.descriptor();
out.format("%s (%s)%n", descriptor.name(), module.location());
Expand Down
8 changes: 8 additions & 0 deletions test/langtools/tools/jdeps/missingDeps/MissingDepsTest.java
Expand Up @@ -83,6 +83,14 @@ public void compileAll() throws Exception {
"--release", VERSION, "-C", CLASSES_DIR.toString(), "p/internal/X.class");
}

@Test
public void checkModuleDeps() {
JdepsTest test = new JdepsTest();
test.options(List.of("--module-path", "m1.jar", "--multi-release", VERSION, "--check", "m1"));
test.checkMissingDeps();
test.ignoreMissingDeps("requires java.management");
}

@Test
public void genModuleInfo() {
JdepsTest test = new JdepsTest();
Expand Down
6 changes: 3 additions & 3 deletions test/langtools/tools/jdeps/modules/CheckModuleTest.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -86,7 +86,7 @@ public void testJavaBase(String name, ModuleMetaData data) throws Exception {
jdeps.appModulePath(MODS_DIR.toString());

ModuleAnalyzer analyzer = jdeps.getModuleAnalyzer(Set.of(name));
assertTrue(analyzer.run());
assertTrue(analyzer.run(false));
jdeps.dumpOutput(System.err);

ModuleDescriptor[] descriptors = analyzer.descriptors(name);
Expand Down Expand Up @@ -146,7 +146,7 @@ public void modularTest(String name, ModuleMetaData[] data) throws Exception {
jdeps.appModulePath(MODS_DIR.toString());

ModuleAnalyzer analyzer = jdeps.getModuleAnalyzer(Set.of(name));
assertTrue(analyzer.run());
assertTrue(analyzer.run(false));
jdeps.dumpOutput(System.err);

// compare the module descriptors and the suggested versions
Expand Down

0 comments on commit fe8e1aa

Please sign in to comment.