8189202: (jdeps) Need jdeps output format easy for jlink --add-modules to use

Reviewed-by: sundar
This commit is contained in:
Mandy Chung 2017-10-17 10:32:01 -07:00
parent 8315ac39cc
commit 9ebc72545b
4 changed files with 136 additions and 105 deletions

View File

@ -38,8 +38,6 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.*; import java.util.*;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -157,6 +155,7 @@ class JdepsTask {
GENERATE_OPEN_MODULE("--generate-open-module"), GENERATE_OPEN_MODULE("--generate-open-module"),
LIST_DEPS("--list-deps"), LIST_DEPS("--list-deps"),
LIST_REDUCED_DEPS("--list-reduced-deps"), LIST_REDUCED_DEPS("--list-reduced-deps"),
PRINT_MODULE_DEPS("--print-module-deps"),
CHECK_MODULES("--check"); CHECK_MODULES("--check");
private final String[] names; private final String[] names;
@ -339,7 +338,7 @@ class JdepsTask {
if (task.command != null) { if (task.command != null) {
throw new BadArgs("err.command.set", task.command, opt); throw new BadArgs("err.command.set", task.command, opt);
} }
task.command = task.listModuleDeps(false); task.command = task.listModuleDeps(CommandOption.LIST_DEPS);
} }
}, },
new Option(false, CommandOption.LIST_REDUCED_DEPS) { new Option(false, CommandOption.LIST_REDUCED_DEPS) {
@ -347,7 +346,15 @@ class JdepsTask {
if (task.command != null) { if (task.command != null) {
throw new BadArgs("err.command.set", task.command, opt); throw new BadArgs("err.command.set", task.command, opt);
} }
task.command = task.listModuleDeps(true); task.command = task.listModuleDeps(CommandOption.LIST_REDUCED_DEPS);
}
},
new Option(false, CommandOption.PRINT_MODULE_DEPS) {
void process(JdepsTask task, String opt, String arg) throws BadArgs {
if (task.command != null) {
throw new BadArgs("err.command.set", task.command, opt);
}
task.command = task.listModuleDeps(CommandOption.PRINT_MODULE_DEPS);
} }
}, },
@ -534,14 +541,15 @@ class JdepsTask {
boolean run() throws IOException { boolean run() throws IOException {
try (JdepsConfiguration config = buildConfig(command.allModules())) { try (JdepsConfiguration config = buildConfig(command.allModules())) {
if (!options.nowarning) {
// detect split packages // detect split packages
config.splitPackages().entrySet() config.splitPackages().entrySet()
.stream() .stream()
.sorted(Map.Entry.comparingByKey()) .sorted(Map.Entry.comparingByKey())
.forEach(e -> log.println(getMessage("split.package", .forEach(e -> warning("warn.split.package",
e.getKey(), e.getKey(),
e.getValue().toString()))); e.getValue().stream().collect(joining(" "))));
}
// check if any module specified in --add-modules, --require, and -m is missing // check if any module specified in --add-modules, --require, and -m is missing
options.addmods.stream() options.addmods.stream()
@ -606,9 +614,17 @@ class JdepsTask {
return new GenModuleInfo(dir, openModule); return new GenModuleInfo(dir, openModule);
} }
private ListModuleDeps listModuleDeps(boolean reduced) throws BadArgs { private ListModuleDeps listModuleDeps(CommandOption option) throws BadArgs {
return reduced ? new ListReducedDeps() switch (option) {
: new ListModuleDeps(); case LIST_DEPS:
return new ListModuleDeps(option, true, false);
case LIST_REDUCED_DEPS:
return new ListModuleDeps(option, true, true);
case PRINT_MODULE_DEPS:
return new ListModuleDeps(option, false, true, ",");
default:
throw new IllegalArgumentException(option.toString());
}
} }
private CheckModuleDeps checkModuleDeps(Set<String> mods) throws BadArgs { private CheckModuleDeps checkModuleDeps(Set<String> mods) throws BadArgs {
@ -964,20 +980,18 @@ class JdepsTask {
} }
} }
class ListReducedDeps extends ListModuleDeps {
ListReducedDeps() {
super(CommandOption.LIST_REDUCED_DEPS, true);
}
}
class ListModuleDeps extends Command { class ListModuleDeps extends Command {
final boolean jdkinternals;
final boolean reduced; final boolean reduced;
ListModuleDeps() { final String separator;
this(CommandOption.LIST_DEPS, false); ListModuleDeps(CommandOption option, boolean jdkinternals, boolean reduced) {
this(option, jdkinternals, reduced, System.getProperty("line.separator"));
} }
ListModuleDeps(CommandOption option, boolean reduced) { ListModuleDeps(CommandOption option, boolean jdkinternals, boolean reduced, String sep) {
super(option); super(option);
this.jdkinternals = jdkinternals;
this.reduced = reduced; this.reduced = reduced;
this.separator = sep;
} }
@Override @Override
@ -1007,8 +1021,10 @@ class JdepsTask {
boolean run(JdepsConfiguration config) throws IOException { boolean run(JdepsConfiguration config) throws IOException {
return new ModuleExportsAnalyzer(config, return new ModuleExportsAnalyzer(config,
dependencyFilter(config), dependencyFilter(config),
jdkinternals,
reduced, reduced,
log).run(); log,
separator).run();
} }
} }

View File

@ -33,11 +33,10 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
/** /**
* Analyze module dependences and any reference to JDK internal APIs. * Analyze module dependences and any reference to JDK internal APIs.
* It can apply transition reduction on the resulting module graph. * It can apply transition reduction on the resulting module graph.
@ -50,17 +49,23 @@ import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
public class ModuleExportsAnalyzer extends DepsAnalyzer { public class ModuleExportsAnalyzer extends DepsAnalyzer {
// source archive to its dependences and JDK internal APIs it references // source archive to its dependences and JDK internal APIs it references
private final Map<Archive, Map<Archive,Set<String>>> deps = new HashMap<>(); private final Map<Archive, Map<Archive,Set<String>>> deps = new HashMap<>();
private final boolean showJdkInternals;
private final boolean reduced; private final boolean reduced;
private final PrintWriter writer; private final PrintWriter writer;
private final String separator;
public ModuleExportsAnalyzer(JdepsConfiguration config, public ModuleExportsAnalyzer(JdepsConfiguration config,
JdepsFilter filter, JdepsFilter filter,
boolean showJdkInternals,
boolean reduced, boolean reduced,
PrintWriter writer) { PrintWriter writer,
String separator) {
super(config, filter, null, super(config, filter, null,
Analyzer.Type.PACKAGE, Analyzer.Type.PACKAGE,
false /* all classes */); false /* all classes */);
this.showJdkInternals = showJdkInternals;
this.reduced = reduced; this.reduced = reduced;
this.writer = writer; this.writer = writer;
this.separator = separator;
} }
@Override @Override
@ -76,7 +81,7 @@ public class ModuleExportsAnalyzer extends DepsAnalyzer {
.computeIfAbsent(targetArchive, _k -> new HashSet<>()); .computeIfAbsent(targetArchive, _k -> new HashSet<>());
Module module = targetArchive.getModule(); Module module = targetArchive.getModule();
if (originArchive.getModule() != module && if (showJdkInternals && originArchive.getModule() != module &&
module.isJDK() && !module.isExported(target)) { module.isJDK() && !module.isExported(target)) {
// use of JDK internal APIs // use of JDK internal APIs
jdkInternals.add(target); jdkInternals.add(target);
@ -89,35 +94,19 @@ public class ModuleExportsAnalyzer extends DepsAnalyzer {
.sorted(Comparator.comparing(Archive::getName)) .sorted(Comparator.comparing(Archive::getName))
.forEach(archive -> analyzer.visitDependences(archive, visitor)); .forEach(archive -> analyzer.visitDependences(archive, visitor));
Set<Module> modules = modules();
// print the dependences on named modules if (showJdkInternals) {
printDependences(); // print modules and JDK internal API dependences
printDependences(modules);
// print the dependences on unnamed module } else {
deps.values().stream() // print module dependences
.flatMap(map -> map.keySet().stream()) writer.println(modules.stream().map(Module::name).sorted()
.filter(archive -> !archive.getModule().isNamed()) .collect(Collectors.joining(separator)));
.map(archive -> archive != NOT_FOUND }
? "unnamed module: " + archive.getPathName()
: archive.getPathName())
.distinct()
.sorted()
.forEach(archive -> writer.format(" %s%n", archive));
return rc; return rc;
} }
private void printDependences() { private Set<Module> modules() {
// find use of JDK internals
Map<Module, Set<String>> jdkinternals = new HashMap<>();
dependenceStream()
.flatMap(map -> map.entrySet().stream())
.filter(e -> e.getValue().size() > 0)
.forEach(e -> jdkinternals.computeIfAbsent(e.getKey().getModule(),
_k -> new HashSet<>())
.addAll(e.getValue()));
// build module graph // build module graph
ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration); ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration);
Module root = new RootModule("root"); Module root = new RootModule("root");
@ -126,43 +115,38 @@ public class ModuleExportsAnalyzer extends DepsAnalyzer {
dependenceStream() dependenceStream()
.flatMap(map -> map.keySet().stream()) .flatMap(map -> map.keySet().stream())
.filter(m -> m.getModule().isNamed() .filter(m -> m.getModule().isNamed()
&& !configuration.rootModules().contains(m)) && !configuration.rootModules().contains(m))
.map(Archive::getModule) .map(Archive::getModule)
.forEach(m -> builder.addEdge(root, m)); .forEach(m -> builder.addEdge(root, m));
// module dependences // build module dependence graph
Set<Module> modules = builder.build().adjacentNodes(root);
// if reduced is set, apply transition reduction // if reduced is set, apply transition reduction
Set<Module> reducedSet; Graph<Module> g = reduced ? builder.reduced() : builder.build();
if (reduced) { return g.adjacentNodes(root);
Set<Module> nodes = builder.reduced().adjacentNodes(root); }
if (nodes.size() == 1) {
// java.base only
reducedSet = nodes;
} else {
// java.base is mandated and can be excluded from the reduced graph
reducedSet = nodes.stream()
.filter(m -> !"java.base".equals(m.name()) ||
jdkinternals.containsKey("java.base"))
.collect(Collectors.toSet());
}
} else {
reducedSet = modules;
}
modules.stream() private void printDependences(Set<Module> modules) {
.sorted(Comparator.comparing(Module::name)) // find use of JDK internals
.forEach(m -> { Map<Module, Set<String>> jdkinternals = new HashMap<>();
if (jdkinternals.containsKey(m)) { dependenceStream()
jdkinternals.get(m).stream() .flatMap(map -> map.entrySet().stream())
.sorted() .filter(e -> e.getValue().size() > 0)
.forEach(pn -> writer.format(" %s/%s%n", m, pn)); .forEach(e -> jdkinternals.computeIfAbsent(e.getKey().getModule(),
} else if (reducedSet.contains(m)){ _k -> new TreeSet<>())
// if the transition reduction is applied, show the reduced graph .addAll(e.getValue()));
writer.format(" %s%n", m);
} // print modules and JDK internal API dependences
}); Stream.concat(modules.stream(), jdkinternals.keySet().stream())
.sorted(Comparator.comparing(Module::name))
.distinct()
.forEach(m -> {
if (jdkinternals.containsKey(m)) {
jdkinternals.get(m).stream()
.forEach(pn -> writer.format(" %s/%s%s", m, pn, separator));
} else {
writer.format(" %s%s", m, separator);
}
});
} }
/* /*

View File

@ -157,23 +157,31 @@ main.opt.jdkinternals=\
\ WARNING: JDK internal APIs are inaccessible. \ WARNING: JDK internal APIs are inaccessible.
main.opt.list-deps=\ main.opt.list-deps=\
\ --list-deps Lists the module dependences and also the\n\ \ --list-deps Lists the module dependences. It also prints\n\
\ package names of JDK internal APIs if referenced. \ any JDK internal API packages if referenced.\n\
\ This option does not show dependences on the\n\
\ class path or not found.
main.opt.list-reduced-deps=\ main.opt.list-reduced-deps=\
\ --list-reduced-deps Same as --list-deps with not listing\n\ \ --list-reduced-deps Same as --list-deps with not listing\n\
\ the implied reads edges from the module graph\n\ \ the implied reads edges from the module graph.\n\
\ If module M1 reads M2, and M2 requires\n\ \ If module M1 reads M2, and M2 requires\n\
\ transitive on M3, then M1 reading M3 is implied\n\ \ transitive on M3, then M1 reading M3 is implied\n\
\ and is not shown in the graph. \ and is not shown in the graph.
main.opt.print-module-deps=\
\ --print-module-deps Same as --list-reduced-deps with printing\n\
\ a comma-separated list of module dependences.\n\
\ This output can be used by jlink --add-modules\n\
\ in order to create a custom image containing\n\
\ those modules and their transitive dependences.
main.opt.depth=\ main.opt.depth=\
\ -depth=<depth> Specify the depth of the transitive\n\ \ -depth=<depth> Specify the depth of the transitive\n\
\ dependency analysis \ dependency analysis
main.opt.q=\ main.opt.q=\
\ -q -quiet Do not show missing dependences from \n\ \ -q -quiet Suppress warning messages
\ --generate-module-info output.
main.opt.multi-release=\ main.opt.multi-release=\
\ --multi-release <version> Specifies the version when processing\n\ \ --multi-release <version> Specifies the version when processing\n\
@ -202,7 +210,7 @@ err.multirelease.version.associated=class {0} already associated with version {1
err.multirelease.jar.malformed=malformed multi-release jar, {0}, bad entry: {1} err.multirelease.jar.malformed=malformed multi-release jar, {0}, bad entry: {1}
warn.invalid.arg=Path does not exist: {0} warn.invalid.arg=Path does not exist: {0}
warn.skipped.entry={0} warn.skipped.entry={0}
warn.split.package=package {0} defined in {1} {2} warn.split.package=split package: {0} {1}
warn.replace.useJDKInternals=\ warn.replace.useJDKInternals=\
JDK internal APIs are unsupported and private to JDK implementation that are\n\ JDK internal APIs are unsupported and private to JDK implementation that are\n\
subject to be removed or changed incompatibly and could break your application.\n\ subject to be removed or changed incompatibly and could break your application.\n\
@ -210,7 +218,6 @@ Please modify your code to eliminate dependence on any JDK internal APIs.\n\
For the most recent update on JDK internal API replacements, please check:\n\ For the most recent update on JDK internal API replacements, please check:\n\
{0} {0}
split.package=split package: {0} {1}\n
inverse.transitive.dependencies.on=Inverse transitive dependences on {0} inverse.transitive.dependencies.on=Inverse transitive dependences on {0}
inverse.transitive.dependencies.matching=Inverse transitive dependences matching {0} inverse.transitive.dependencies.matching=Inverse transitive dependences matching {0}
internal.api.column.header=JDK Internal API internal.api.column.header=JDK Internal API

View File

@ -24,7 +24,7 @@
/* /*
* @test * @test
* @bug 8167057 * @bug 8167057
* @summary Tests --list-deps and --list-reduced-deps options * @summary Tests --list-deps, --list-reduced-deps, --print-module-deps options
* @modules java.logging * @modules java.logging
* java.xml * java.xml
* jdk.compiler * jdk.compiler
@ -38,6 +38,7 @@
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Arrays; import java.util.Arrays;
import java.util.stream.Collectors;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider; import org.testng.annotations.DataProvider;
@ -139,8 +140,7 @@ public class ListModuleDeps {
"java.logging", "java.logging",
"java.sql", "java.sql",
"java.xml/jdk.xml.internal", "java.xml/jdk.xml.internal",
"jdk.unsupported", "jdk.unsupported"
"unnamed module: lib"
} }
}, },
@ -153,8 +153,7 @@ public class ListModuleDeps {
"java.base", "java.base",
"java.logging", "java.logging",
"java.sql", "java.sql",
"java.xml", "java.xml"
"unnamed module: lib"
} }
}, },
@ -166,7 +165,7 @@ public class ListModuleDeps {
{ UNSAFE_CLASS, new String[] { { UNSAFE_CLASS, new String[] {
"java.base/jdk.internal.misc", "java.base/jdk.internal.misc",
"jdk.unsupported", "jdk.unsupported"
} }
}, },
}; };
@ -182,8 +181,7 @@ public class ListModuleDeps {
"java.base/sun.security.util", "java.base/sun.security.util",
"java.sql", "java.sql",
"java.xml/jdk.xml.internal", "java.xml/jdk.xml.internal",
"jdk.unsupported", "jdk.unsupported"
"unnamed module: lib"
} }
}, },
@ -193,8 +191,8 @@ public class ListModuleDeps {
}, },
{ FOO_CLASS, new String[] { { FOO_CLASS, new String[] {
"java.sql", "java.base",
"unnamed module: lib" "java.sql"
} }
}, },
@ -206,10 +204,36 @@ public class ListModuleDeps {
{ UNSAFE_CLASS, new String[] { { UNSAFE_CLASS, new String[] {
"java.base/jdk.internal.misc", "java.base/jdk.internal.misc",
"jdk.unsupported", "jdk.unsupported"
} }
}, },
}; };
} }
@Test(dataProvider = "moduledeps")
public void testPrintModuleDeps(Path classes, String expected) {
JdepsRunner jdeps = JdepsRunner.run(
"--class-path", LIB_DIR.toString(),
"--print-module-deps", classes.toString()
);
String output = Arrays.stream(jdeps.output())
.map(s -> s.trim())
.collect(Collectors.joining(","));
assertEquals(output, expected);
}
@DataProvider(name = "moduledeps")
public Object[][] moduledeps() {
Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class");
return new Object[][] {
// java.xml is an implied reads edge from java.sql
{ CLASSES_DIR, "java.base,java.sql,jdk.unsupported"},
{ HI_CLASS, "java.base"},
{ FOO_CLASS, "java.base,java.sql"},
{ BAR_CLASS, "java.base,java.xml"},
{ UNSAFE_CLASS, "java.base,jdk.unsupported"},
};
}
} }