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

View File

@ -33,11 +33,10 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
/**
* Analyze module dependences and any reference to JDK internal APIs.
* 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 {
// source archive to its dependences and JDK internal APIs it references
private final Map<Archive, Map<Archive,Set<String>>> deps = new HashMap<>();
private final boolean showJdkInternals;
private final boolean reduced;
private final PrintWriter writer;
private final String separator;
public ModuleExportsAnalyzer(JdepsConfiguration config,
JdepsFilter filter,
boolean showJdkInternals,
boolean reduced,
PrintWriter writer) {
PrintWriter writer,
String separator) {
super(config, filter, null,
Analyzer.Type.PACKAGE,
false /* all classes */);
this.showJdkInternals = showJdkInternals;
this.reduced = reduced;
this.writer = writer;
this.separator = separator;
}
@Override
@ -76,7 +81,7 @@ public class ModuleExportsAnalyzer extends DepsAnalyzer {
.computeIfAbsent(targetArchive, _k -> new HashSet<>());
Module module = targetArchive.getModule();
if (originArchive.getModule() != module &&
if (showJdkInternals && originArchive.getModule() != module &&
module.isJDK() && !module.isExported(target)) {
// use of JDK internal APIs
jdkInternals.add(target);
@ -89,35 +94,19 @@ public class ModuleExportsAnalyzer extends DepsAnalyzer {
.sorted(Comparator.comparing(Archive::getName))
.forEach(archive -> analyzer.visitDependences(archive, visitor));
// print the dependences on named modules
printDependences();
// print the dependences on unnamed module
deps.values().stream()
.flatMap(map -> map.keySet().stream())
.filter(archive -> !archive.getModule().isNamed())
.map(archive -> archive != NOT_FOUND
? "unnamed module: " + archive.getPathName()
: archive.getPathName())
.distinct()
.sorted()
.forEach(archive -> writer.format(" %s%n", archive));
Set<Module> modules = modules();
if (showJdkInternals) {
// print modules and JDK internal API dependences
printDependences(modules);
} else {
// print module dependences
writer.println(modules.stream().map(Module::name).sorted()
.collect(Collectors.joining(separator)));
}
return rc;
}
private void printDependences() {
// 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()));
private Set<Module> modules() {
// build module graph
ModuleGraphBuilder builder = new ModuleGraphBuilder(configuration);
Module root = new RootModule("root");
@ -126,43 +115,38 @@ public class ModuleExportsAnalyzer extends DepsAnalyzer {
dependenceStream()
.flatMap(map -> map.keySet().stream())
.filter(m -> m.getModule().isNamed()
&& !configuration.rootModules().contains(m))
&& !configuration.rootModules().contains(m))
.map(Archive::getModule)
.forEach(m -> builder.addEdge(root, m));
// module dependences
Set<Module> modules = builder.build().adjacentNodes(root);
// build module dependence graph
// if reduced is set, apply transition reduction
Set<Module> reducedSet;
if (reduced) {
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;
}
Graph<Module> g = reduced ? builder.reduced() : builder.build();
return g.adjacentNodes(root);
}
modules.stream()
.sorted(Comparator.comparing(Module::name))
.forEach(m -> {
if (jdkinternals.containsKey(m)) {
jdkinternals.get(m).stream()
.sorted()
.forEach(pn -> writer.format(" %s/%s%n", m, pn));
} else if (reducedSet.contains(m)){
// if the transition reduction is applied, show the reduced graph
writer.format(" %s%n", m);
}
});
private void printDependences(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 TreeSet<>())
.addAll(e.getValue()));
// 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.
main.opt.list-deps=\
\ --list-deps Lists the module dependences and also the\n\
\ package names of JDK internal APIs if referenced.
\ --list-deps Lists the module dependences. It also prints\n\
\ 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=\
\ --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\
\ transitive on M3, then M1 reading M3 is implied\n\
\ 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=\
\ -depth=<depth> Specify the depth of the transitive\n\
\ dependency analysis
main.opt.q=\
\ -q -quiet Do not show missing dependences from \n\
\ --generate-module-info output.
\ -q -quiet Suppress warning messages
main.opt.multi-release=\
\ --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}
warn.invalid.arg=Path does not exist: {0}
warn.skipped.entry={0}
warn.split.package=package {0} defined in {1} {2}
warn.split.package=split package: {0} {1}
warn.replace.useJDKInternals=\
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\
@ -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\
{0}
split.package=split package: {0} {1}\n
inverse.transitive.dependencies.on=Inverse transitive dependences on {0}
inverse.transitive.dependencies.matching=Inverse transitive dependences matching {0}
internal.api.column.header=JDK Internal API

View File

@ -24,7 +24,7 @@
/*
* @test
* @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
* java.xml
* jdk.compiler
@ -38,6 +38,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
@ -139,8 +140,7 @@ public class ListModuleDeps {
"java.logging",
"java.sql",
"java.xml/jdk.xml.internal",
"jdk.unsupported",
"unnamed module: lib"
"jdk.unsupported"
}
},
@ -153,8 +153,7 @@ public class ListModuleDeps {
"java.base",
"java.logging",
"java.sql",
"java.xml",
"unnamed module: lib"
"java.xml"
}
},
@ -166,7 +165,7 @@ public class ListModuleDeps {
{ UNSAFE_CLASS, new String[] {
"java.base/jdk.internal.misc",
"jdk.unsupported",
"jdk.unsupported"
}
},
};
@ -182,8 +181,7 @@ public class ListModuleDeps {
"java.base/sun.security.util",
"java.sql",
"java.xml/jdk.xml.internal",
"jdk.unsupported",
"unnamed module: lib"
"jdk.unsupported"
}
},
@ -193,8 +191,8 @@ public class ListModuleDeps {
},
{ FOO_CLASS, new String[] {
"java.sql",
"unnamed module: lib"
"java.base",
"java.sql"
}
},
@ -206,10 +204,36 @@ public class ListModuleDeps {
{ UNSAFE_CLASS, new String[] {
"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"},
};
}
}