--- old/jdk/make/src/classes/build/tools/jigsaw/GenGraphs.java 2017-02-15 11:31:25.000000000 -0800 +++ new/jdk/make/src/classes/build/tools/jigsaw/GenGraphs.java 2017-02-15 11:31:25.000000000 -0800 @@ -25,29 +25,21 @@ package build.tools.jigsaw; +import com.sun.tools.jdeps.ModuleDotGraph; +import com.sun.tools.jdeps.ModuleDotGraph.DotGraphBuilder; + import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; -import java.lang.module.ResolvedModule; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; -import java.util.function.Function; - -import static java.util.stream.Collectors.*; -import static java.lang.module.ModuleDescriptor.Requires.Modifier.TRANSITIVE; /** * Generate the DOT file for a module graph for each module in the JDK @@ -56,238 +48,100 @@ public class GenGraphs { public static void main(String[] args) throws Exception { + Path dir = null; + boolean spec = false; + for (int i=0; i < args.length; i++) { + String arg = args[i]; + if (arg.equals("--spec")) { + spec = true; + } else if (arg.equals("--output")) { + i++; + dir = i < args.length ? Paths.get(args[i]) : null; + } else if (arg.startsWith("-")) { + throw new IllegalArgumentException("Invalid option: " + arg); + } + } - if (args.length != 1) { - System.err.println("ERROR: specify the output directory"); + if (dir == null) { + System.err.println("ERROR: must specify --output argument"); System.exit(1); } - Path dir = Paths.get(args[0]); + + // setup and configure the dot graph attributes + initDotGraphAttributes(); Files.createDirectories(dir); - ModuleFinder finder = ModuleFinder.ofSystem(); + GenGraphs genGraphs = new GenGraphs(dir, spec); - Set javaSEModules - = new TreeSet<>(finder.findAll().stream() - .map(ModuleReference::descriptor) - .filter(m -> (m.name().startsWith("java.") && - !m.name().equals("java.smartcardio"))) - .collect(toSet())); - Set jdkModules - = new TreeSet<>(finder.findAll().stream() - .map(ModuleReference::descriptor) - .filter(m -> !javaSEModules.contains(m)) - .collect(toSet())); - - GenGraphs genGraphs = new GenGraphs(dir, javaSEModules, jdkModules); - Set mods = new HashSet<>(); - for (ModuleReference mref: finder.findAll()) { - mods.add(mref.descriptor().name()); - genGraphs.genDotFile(mref); + // print dot file for each module + Map configurations = new HashMap<>(); + Set modules = new HashSet<>(); + ModuleFinder finder = ModuleFinder.ofSystem(); + for (ModuleReference mref : finder.findAll()) { + String name = (mref.descriptor().name()); + modules.add(name); + if (genGraphs.accept(name, mref.descriptor())) { + configurations.put(name, Configuration.empty() + .resolve(finder, + ModuleFinder.of(), + Set.of(name))); + } } - // all modules - genGraphs.genDotFile("jdk", mods); - - } - - private static final String ORANGE = "#e76f00"; - private static final String BLUE = "#437291"; - private static final String GRAY = "#dddddd"; - - private static final String REEXPORTS = ""; - private static final String REQUIRES = "style=\"dashed\""; - private static final String REQUIRES_BASE = "color=\"" + GRAY + "\""; - - private static final Map weights = new HashMap<>(); - private static final List> ranks = new ArrayList<>(); - - private static void weight(String s, String t, int w) { - weights.put(s + ":" + t, w); - } + if (genGraphs.accept("jdk", null)) { + // print a graph of all JDK modules + configurations.put("jdk", Configuration.empty() + .resolve(finder, + ModuleFinder.of(), + modules)); + } - private static int weightOf(String s, String t) { - int w = weights.getOrDefault(s + ":" + t, 1); - if (w != 1) - return w; - if (s.startsWith("java.") && t.startsWith("java.")) - return 10; - return 1; + genGraphs.genDotFiles(configurations); } - static { + static void initDotGraphAttributes() { int h = 1000; - weight("java.se", "java.sql.rowset", h * 10); - weight("java.sql.rowset", "java.sql", h * 10); - weight("java.sql", "java.xml", h * 10); - weight("java.xml", "java.base", h * 10); - - ranks.add(Set.of("java.logging", "java.scripting", "java.xml")); - ranks.add(Set.of("java.sql")); - ranks.add(Set.of("java.compiler", "java.instrument")); - ranks.add(Set.of("java.desktop", "java.management")); - ranks.add(Set.of("java.corba", "java.xml.ws")); - ranks.add(Set.of("java.xml.bind", "java.xml.ws.annotation")); - + DotGraphBuilder.weight("java.se", "java.sql.rowset", h * 10); + DotGraphBuilder.weight("java.sql.rowset", "java.sql", h * 10); + DotGraphBuilder.weight("java.sql", "java.xml", h * 10); + DotGraphBuilder.weight("java.xml", "java.base", h * 10); + + DotGraphBuilder.sameRankNodes(Set.of("java.logging", "java.scripting", "java.xml")); + DotGraphBuilder.sameRankNodes(Set.of("java.sql")); + DotGraphBuilder.sameRankNodes(Set.of("java.compiler", "java.instrument")); + DotGraphBuilder.sameRankNodes(Set.of("java.desktop", "java.management")); + DotGraphBuilder.sameRankNodes(Set.of("java.corba", "java.xml.ws")); + DotGraphBuilder.sameRankNodes(Set.of("java.xml.bind", "java.xml.ws.annotation")); + DotGraphBuilder.setRankSep(0.7); + DotGraphBuilder.setFontSize(12); + DotGraphBuilder.setArrowSize(1); + DotGraphBuilder.setArrowWidth(2); } private final Path dir; - private final Set javaGroup; - private final Set jdkGroup; - - GenGraphs(Path dir, Set javaGroup, Set jdkGroup) { + private final boolean spec; + GenGraphs(Path dir, boolean spec) { this.dir = dir; - this.javaGroup = Collections.unmodifiableSet(javaGroup); - this.jdkGroup = Collections.unmodifiableSet(jdkGroup); + this.spec = spec; } - /** - * 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)); + void genDotFiles(Map configurations) throws IOException { + ModuleDotGraph dotGraph = new ModuleDotGraph(configurations, spec); + dotGraph.genDotFiles(dir); } - /** - * Generates a dot file for the given set of root modules. - */ - void genDotFile(String name, Set roots) throws IOException { - Configuration cf = - Configuration.empty().resolve(ModuleFinder.ofSystem(), - ModuleFinder.of(), - roots); - - Set mds = cf.modules().stream() - .map(ResolvedModule::reference) - .map(ModuleReference::descriptor) - .collect(toSet()); - - // 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())) - ); - } + boolean accept(String name, ModuleDescriptor descriptor) { + if (!spec) return true; - 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 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())) - ); - } - } - } + if (name.equals("jdk")) + return false; - private void printGraph(PrintStream out, - String name, - Graph graph, - Map nameToModule) - throws IOException - { - Set descriptors = new TreeSet<>(nameToModule.values()); - - out.format("digraph \"%s\" {%n", name); - out.format("size=\"25,25\";"); - out.format("nodesep=.5;%n"); - out.format("ranksep=1.5;%n"); - out.format("pencolor=transparent;%n"); - out.format("node [shape=plaintext, fontname=\"DejaVuSans\", fontsize=36, margin=\".2,.2\"];%n"); - out.format("edge [penwidth=4, color=\"#999999\", arrowhead=open, arrowsize=2];%n"); - - out.format("subgraph %sse {%n", name.equals("jdk") ? "cluster_" : ""); - descriptors.stream() - .filter(javaGroup::contains) - .map(ModuleDescriptor::name) - .forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n", - mn, ORANGE, "java")); - out.format("}%n"); - - // same ranks - ranks.stream() - .map(group -> descriptors.stream() - .map(ModuleDescriptor::name) - .filter(group::contains) - .map(mn -> "\"" + mn + "\"") - .collect(joining(","))) - .filter(group -> group.length() > 0) - .forEach(group -> out.format("{rank=same %s}%n", group)); - - descriptors.stream() - .filter(jdkGroup::contains) - .map(ModuleDescriptor::name) - .forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n", - mn, BLUE, "jdk")); - - descriptors.stream() - .forEach(md -> { - String mn = md.name(); - Set 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); - }); - }); + if (name.equals("java.se") || name.equals("java.se.ee")) + return true; - out.println("}"); - } - - /** - * Returns a Graph of the given Configuration after transitive reduction. - * - * Transitive reduction of requires transitive edge and requires edge have - * to be applied separately to prevent the requires transitive edges - * (e.g. U -> V) from being reduced by a path (U -> X -> Y -> V) - * in which V would not be re-exported from U. - */ - private Graph gengraph(Configuration cf) { - Graph.Builder builder = new Graph.Builder<>(); - for (ResolvedModule resolvedModule : cf.modules()) { - String mn = resolvedModule.reference().descriptor().name(); - builder.addNode(mn); - resolvedModule.reads().stream() - .map(ResolvedModule::name) - .forEach(target -> builder.addEdge(mn, target)); - } - Graph rpg = requiresTransitiveGraph(cf, false); - return builder.build().reduce(rpg); - } - - /** - * Returns a Graph containing only requires transitive edges - * with transitive reduction. - */ - private Graph requiresTransitiveGraph(Configuration cf, boolean includeBase) { - Graph.Builder 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) - || (includeBase && d.name().equals("java.base"))) - .map(d -> d.name()) - .forEach(d -> builder.addEdge(mn, d)); - } - return builder.build().reduce(); + // only the module that has exported API + return descriptor.exports().stream() + .filter(e -> !e.isQualified()) + .findAny().isPresent(); } -} +} \ No newline at end of file