8171323: generate dot file for java.se and java.se.ee with only API dependences
authormchung
Thu, 15 Dec 2016 17:49:13 -0800
changeset 42701 274434f1297c
parent 42700 c6fd469c7585
child 42702 03bf81fb558c
8171323: generate dot file for java.se and java.se.ee with only API dependences Reviewed-by: psandoz
jdk/make/src/classes/build/tools/jigsaw/GenGraphs.java
--- a/jdk/make/src/classes/build/tools/jigsaw/GenGraphs.java	Thu Dec 15 16:24:33 2016 -0500
+++ b/jdk/make/src/classes/build/tools/jigsaw/GenGraphs.java	Thu Dec 15 17:49:13 2016 -0800
@@ -26,6 +26,7 @@
 package build.tools.jigsaw;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintStream;
 import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
@@ -44,7 +45,8 @@
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.function.Function;
-import java.util.stream.Collectors;
+
+import static java.util.stream.Collectors.*;
 import static java.lang.module.ModuleDescriptor.Requires.Modifier.TRANSITIVE;
 
 /**
@@ -69,42 +71,25 @@
                                   .map(ModuleReference::descriptor)
                                   .filter(m -> (m.name().startsWith("java.") &&
                                                !m.name().equals("java.smartcardio")))
-                                  .collect(Collectors.toSet()));
+                                  .collect(toSet()));
         Set<ModuleDescriptor> jdkModules
             = new TreeSet<>(finder.findAll().stream()
                                   .map(ModuleReference::descriptor)
                                   .filter(m -> !javaSEModules.contains(m))
-                                  .collect(Collectors.toSet()));
+                                  .collect(toSet()));
 
-        GenGraphs genGraphs = new GenGraphs(javaSEModules, jdkModules);
+        GenGraphs genGraphs = new GenGraphs(dir, javaSEModules, jdkModules);
         Set<String> mods = new HashSet<>();
         for (ModuleReference mref: finder.findAll()) {
-            ModuleDescriptor descriptor = mref.descriptor();
-            String name = descriptor.name();
-            mods.add(name);
-            Configuration cf = Configuration.empty()
-                    .resolveRequires(finder,
-                                     ModuleFinder.of(),
-                                     Set.of(name));
-            genGraphs.genDotFile(dir, name, cf);
+            mods.add(mref.descriptor().name());
+            genGraphs.genDotFile(mref);
         }
 
-        Configuration cf = Configuration.empty()
-                .resolveRequires(finder,
-                                 ModuleFinder.of(),
-                                 mods);
-        genGraphs.genDotFile(dir, "jdk", cf);
+        // all modules
+        genGraphs.genDotFile("jdk", mods);
 
     }
 
-    private final Set<ModuleDescriptor> javaGroup;
-    private final Set<ModuleDescriptor> jdkGroup;
-
-    GenGraphs(Set<ModuleDescriptor> javaGroup, Set<ModuleDescriptor> jdkGroup) {
-        this.javaGroup = Collections.unmodifiableSet(javaGroup);
-        this.jdkGroup = Collections.unmodifiableSet(jdkGroup);
-    }
-
     private static final String ORANGE = "#e76f00";
     private static final String BLUE = "#437291";
     private static final String GRAY = "#dddddd";
@@ -145,23 +130,70 @@
 
     }
 
-    private void genDotFile(Path dir, String name, Configuration cf) throws IOException {
-        try (PrintStream out
-                 = new PrintStream(Files.newOutputStream(dir.resolve(name + ".dot")))) {
+    private final Path dir;
+    private final Set<ModuleDescriptor> javaGroup;
+    private final Set<ModuleDescriptor> jdkGroup;
+
+    GenGraphs(Path dir, Set<ModuleDescriptor> javaGroup, Set<ModuleDescriptor> jdkGroup) {
+        this.dir = dir;
+        this.javaGroup = Collections.unmodifiableSet(javaGroup);
+        this.jdkGroup = Collections.unmodifiableSet(jdkGroup);
+    }
+
+    /**
+     * Generates a dot file for the given module reference as the root.
+     */
+    void genDotFile(ModuleReference mref) throws IOException {
+        String name = mref.descriptor().name();
+        genDotFile(name, Set.of(name));
+    }
+
+    /**
+     * Generates a dot file for the given set of root modules.
+     */
+    void genDotFile(String name, Set<String> roots) throws IOException {
+        Configuration cf =
+            Configuration.empty().resolveRequires(ModuleFinder.ofSystem(),
+                                                  ModuleFinder.of(),
+                                                  roots);
+
+        Set<ModuleDescriptor> mds = cf.modules().stream()
+                .map(ResolvedModule::reference)
+                .map(ModuleReference::descriptor)
+                .collect(toSet());
 
-            Map<String, ModuleDescriptor> nameToModule;
-            if (name.equals("java.se.ee")) {
-                nameToModule = cf.modules().stream()
-                    .map(ResolvedModule::reference)
-                    .map(ModuleReference::descriptor)
-                    .filter(md -> !md.name().startsWith("jdk."))
-                    .collect(Collectors.toMap(ModuleDescriptor::name, Function.identity()));
-            } else {
-                nameToModule = cf.modules().stream()
-                    .map(ResolvedModule::reference)
-                    .map(ModuleReference::descriptor)
-                    .collect(Collectors.toMap(ModuleDescriptor::name, Function.identity()));
+        // generate a dot file for the resolved graph
+        try (OutputStream os = Files.newOutputStream(dir.resolve(name + ".dot"));
+             PrintStream out = new PrintStream(os)) {
+            printGraph(out, name, gengraph(cf),
+                       mds.stream()
+                          .collect(toMap(ModuleDescriptor::name, Function.identity()))
+            );
+        }
+
+        if (name.equals("java.se") || name.equals("java.se.ee")) {
+            // generate a dot file for Java SE module graph
+            try (OutputStream os = Files.newOutputStream(dir.resolve(name + "-spec.dot"));
+                 PrintStream out = new PrintStream(os)) {
+                // transitive reduction on the graph of `requires transitive` edges
+                // filter out jdk.* modules which are implementation dependences
+                Graph<String> graph = requiresTransitiveGraph(cf, true);
+                printGraph(out, name, graph,
+                           mds.stream()
+                              .filter(md -> !md.name().startsWith("jdk.") &&
+                                                graph.nodes().contains(md.name()))
+                              .collect(toMap(ModuleDescriptor::name, Function.identity()))
+                );
             }
+        }
+    }
+
+    private void printGraph(PrintStream out,
+                            String name,
+                            Graph<String> graph,
+                            Map<String, ModuleDescriptor> nameToModule)
+        throws IOException
+    {
             Set<ModuleDescriptor> descriptors = new TreeSet<>(nameToModule.values());
 
             out.format("digraph \"%s\" {%n", name);
@@ -187,7 +219,7 @@
                         .map(ModuleDescriptor::name)
                         .filter(group::contains)
                         .map(mn -> "\"" + mn + "\"")
-                        .collect(Collectors.joining(","))
+                        .collect(joining(","))
                 ));
 
             descriptors.stream()
@@ -196,30 +228,28 @@
                 .forEach(mn -> out.format("  \"%s\" [fontcolor=\"%s\", group=%s];%n",
                                           mn, BLUE, "jdk"));
 
-            // transitive reduction
-            Graph<String> graph = gengraph(cf);
-            descriptors.forEach(md -> {
-                String mn = md.name();
-                Set<String> requiresTransitive = md.requires().stream()
-                        .filter(d -> d.modifiers().contains(TRANSITIVE))
-                        .map(d -> d.name())
-                        .collect(Collectors.toSet());
+            descriptors.stream()
+                .forEach(md -> {
+                    String mn = md.name();
+                    Set<String> requiresTransitive = md.requires().stream()
+                            .filter(d -> d.modifiers().contains(TRANSITIVE))
+                            .map(d -> d.name())
+                            .collect(toSet());
 
-                graph.adjacentNodes(mn)
-                     .stream()
-                     .filter(nameToModule::containsKey)
-                     .forEach(dn -> {
-                         String attr = dn.equals("java.base") ? REQUIRES_BASE
-                                : (requiresTransitive.contains(dn) ? REEXPORTS : REQUIRES);
-                         int w = weightOf(mn, dn);
-                         if (w > 1)
-                             attr += "weight=" + w;
-                         out.format("  \"%s\" -> \"%s\" [%s];%n", mn, dn, attr);
-                     });
-            });
+                    graph.adjacentNodes(mn)
+                         .stream()
+                         .filter(nameToModule::containsKey)
+                         .forEach(dn -> {
+                             String attr = dn.equals("java.base") ? REQUIRES_BASE
+                                    : (requiresTransitive.contains(dn) ? REEXPORTS : REQUIRES);
+                             int w = weightOf(mn, dn);
+                             if (w > 1)
+                                 attr += "weight=" + w;
+                             out.format("  \"%s\" -> \"%s\" [%s];%n", mn, dn, attr);
+                         });
+                });
 
             out.println("}");
-        }
     }
 
     /**
@@ -239,7 +269,7 @@
                     .map(ResolvedModule::name)
                     .forEach(target -> builder.addEdge(mn, target));
         }
-        Graph<String> rpg = requiresTransitiveGraph(cf);
+        Graph<String> rpg = requiresTransitiveGraph(cf, false);
         return builder.build().reduce(rpg);
     }
 
@@ -247,13 +277,14 @@
      * Returns a Graph containing only requires transitive edges
      * with transitive reduction.
      */
-    private Graph<String> requiresTransitiveGraph(Configuration cf) {
+    private Graph<String> requiresTransitiveGraph(Configuration cf, boolean includeBase) {
         Graph.Builder<String> builder = new Graph.Builder<>();
         for (ResolvedModule resolvedModule : cf.modules()) {
             ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
             String mn = descriptor.name();
             descriptor.requires().stream()
-                    .filter(d -> d.modifiers().contains(TRANSITIVE))
+                    .filter(d -> d.modifiers().contains(TRANSITIVE)
+                                    || (includeBase && d.name().equals("java.base")))
                     .map(d -> d.name())
                     .forEach(d -> builder.addEdge(mn, d));
         }