< prev index next >

jdk/make/src/classes/build/tools/jigsaw/GenGraphs.java

Print this page

        

*** 23,293 **** * questions. */ 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; 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 * after transitive reduction. */ public class GenGraphs { public static void main(String[] args) throws Exception { ! if (args.length != 1) { ! System.err.println("ERROR: specify the output directory"); System.exit(1); } - Path dir = Paths.get(args[0]); - Files.createDirectories(dir); ! ModuleFinder finder = ModuleFinder.ofSystem(); ! Set<ModuleDescriptor> javaSEModules ! = new TreeSet<>(finder.findAll().stream() ! .map(ModuleReference::descriptor) ! .filter(m -> (m.name().startsWith("java.") && ! !m.name().equals("java.smartcardio"))) ! .collect(toSet())); ! Set<ModuleDescriptor> jdkModules ! = new TreeSet<>(finder.findAll().stream() ! .map(ModuleReference::descriptor) ! .filter(m -> !javaSEModules.contains(m)) ! .collect(toSet())); ! GenGraphs genGraphs = new GenGraphs(dir, javaSEModules, jdkModules); ! Set<String> mods = new HashSet<>(); ! for (ModuleReference mref: finder.findAll()) { ! mods.add(mref.descriptor().name()); ! genGraphs.genDotFile(mref); } - - // 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<String,Integer> weights = new HashMap<>(); ! private static final List<Set<String>> ranks = new ArrayList<>(); ! ! private static void weight(String s, String t, int w) { ! weights.put(s + ":" + t, w); } ! 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; } ! static { 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")); ! } 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().resolve(ModuleFinder.ofSystem(), ! ModuleFinder.of(), ! roots); ! Set<ModuleDescriptor> 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())) ! ); ! } ! ! 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); ! 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<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); ! }); ! }); ! ! 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<String> gengraph(Configuration cf) { ! Graph.Builder<String> 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<String> rpg = requiresTransitiveGraph(cf, false); ! return builder.build().reduce(rpg); ! } ! ! /** ! * Returns a Graph containing only requires transitive edges ! * with transitive reduction. ! */ ! 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) ! || (includeBase && d.name().equals("java.base"))) ! .map(d -> d.name()) ! .forEach(d -> builder.addEdge(mn, d)); ! } ! return builder.build().reduce(); } } --- 23,147 ---- * questions. */ package build.tools.jigsaw; + import com.sun.tools.jdeps.ModuleDotGraph; + import com.sun.tools.jdeps.ModuleDotGraph.DotGraphBuilder; + import java.io.IOException; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Generate the DOT file for a module graph for each module in the JDK * after transitive reduction. */ 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 (dir == null) { ! System.err.println("ERROR: must specify --output argument"); System.exit(1); } ! // setup and configure the dot graph attributes ! initDotGraphAttributes(); ! Files.createDirectories(dir); ! GenGraphs genGraphs = new GenGraphs(dir, spec); ! // print dot file for each module ! Map<String, Configuration> configurations = new HashMap<>(); ! Set<String> 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))); } } ! if (genGraphs.accept("jdk", null)) { ! // print a graph of all JDK modules ! configurations.put("jdk", Configuration.empty() ! .resolve(finder, ! ModuleFinder.of(), ! modules)); } ! genGraphs.genDotFiles(configurations); } ! static void initDotGraphAttributes() { int h = 1000; ! 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 boolean spec; ! GenGraphs(Path dir, boolean spec) { this.dir = dir; ! this.spec = spec; } ! void genDotFiles(Map<String, Configuration> configurations) throws IOException { ! ModuleDotGraph dotGraph = new ModuleDotGraph(configurations, spec); ! dotGraph.genDotFiles(dir); } ! boolean accept(String name, ModuleDescriptor descriptor) { ! if (!spec) return true; ! if (name.equals("jdk")) ! return false; ! ! if (name.equals("java.se") || name.equals("java.se.ee")) ! return true; ! ! // only the module that has exported API ! return descriptor.exports().stream() ! .filter(e -> !e.isQualified()) ! .findAny().isPresent(); } } \ No newline at end of file
< prev index next >