8189202: (jdeps) Need jdeps output format easy for jlink --add-modules to use
authormchung
Tue, 17 Oct 2017 10:32:01 -0700
changeset 47357 74700c8e39e9
parent 47356 c30033467073
child 47358 d07d5f7cab35
child 47390 1a818b395dba
8189202: (jdeps) Need jdeps output format easy for jlink --add-modules to use Reviewed-by: sundar
src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java
src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleExportsAnalyzer.java
src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties
test/langtools/tools/jdeps/listdeps/ListModuleDeps.java
--- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Tue Oct 17 07:11:05 2017 -0700
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java	Tue Oct 17 10:32:01 2017 -0700
@@ -38,8 +38,6 @@
 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 @@
         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 @@
                 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 @@
                 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 @@
 
     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 @@
         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 ListReducedDeps extends ListModuleDeps {
-        ListReducedDeps() {
-            super(CommandOption.LIST_REDUCED_DEPS, true);
+    class ListModuleDeps extends Command {
+        final boolean jdkinternals;
+        final boolean reduced;
+        final String separator;
+        ListModuleDeps(CommandOption option, boolean jdkinternals, boolean reduced) {
+            this(option, jdkinternals, reduced, System.getProperty("line.separator"));
         }
-    }
-
-    class ListModuleDeps extends Command {
-        final boolean reduced;
-        ListModuleDeps() {
-            this(CommandOption.LIST_DEPS, false);
-        }
-        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 @@
         boolean run(JdepsConfiguration config) throws IOException {
             return new ModuleExportsAnalyzer(config,
                                              dependencyFilter(config),
+                                             jdkinternals,
                                              reduced,
-                                             log).run();
+                                             log,
+                                             separator).run();
         }
     }
 
--- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleExportsAnalyzer.java	Tue Oct 17 07:11:05 2017 -0700
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleExportsAnalyzer.java	Tue Oct 17 10:32:01 2017 -0700
@@ -33,11 +33,10 @@
 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 @@
 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 @@
                     .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 @@
             .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 @@
         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);
+                  }
+              });
     }
 
     /*
--- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Tue Oct 17 07:11:05 2017 -0700
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Tue Oct 17 10:32:01 2017 -0700
@@ -157,23 +157,31 @@
 \                                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.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 @@
 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
--- a/test/langtools/tools/jdeps/listdeps/ListModuleDeps.java	Tue Oct 17 07:11:05 2017 -0700
+++ b/test/langtools/tools/jdeps/listdeps/ListModuleDeps.java	Tue Oct 17 10:32:01 2017 -0700
@@ -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 @@
                                 "java.logging",
                                 "java.sql",
                                 "java.xml/jdk.xml.internal",
-                                "jdk.unsupported",
-                                "unnamed module: lib"
+                                "jdk.unsupported"
                             }
             },
 
@@ -153,8 +153,7 @@
                                 "java.base",
                                 "java.logging",
                                 "java.sql",
-                                "java.xml",
-                                "unnamed module: lib"
+                                "java.xml"
                             }
             },
 
@@ -166,7 +165,7 @@
 
             { UNSAFE_CLASS, new String[] {
                                 "java.base/jdk.internal.misc",
-                                "jdk.unsupported",
+                                "jdk.unsupported"
                             }
             },
         };
@@ -182,8 +181,7 @@
                                 "java.base/sun.security.util",
                                 "java.sql",
                                 "java.xml/jdk.xml.internal",
-                                "jdk.unsupported",
-                                "unnamed module: lib"
+                                "jdk.unsupported"
                             }
             },
 
@@ -193,8 +191,8 @@
             },
 
             { FOO_CLASS,    new String[] {
-                                "java.sql",
-                                "unnamed module: lib"
+                                "java.base",
+                                "java.sql"
                             }
             },
 
@@ -206,10 +204,36 @@
 
             { 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"},
+        };
+    }
 }