< prev index next >

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

Print this page




   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package build.tools.jigsaw;
  27 



  28 import java.io.IOException;
  29 import java.io.OutputStream;
  30 import java.io.PrintStream;
  31 import java.lang.module.Configuration;
  32 import java.lang.module.ModuleDescriptor;
  33 import java.lang.module.ModuleFinder;
  34 import java.lang.module.ModuleReference;
  35 import java.lang.module.ResolvedModule;
  36 import java.nio.file.Files;
  37 import java.nio.file.Path;
  38 import java.nio.file.Paths;
  39 import java.util.ArrayList;
  40 import java.util.Collections;
  41 import java.util.HashMap;
  42 import java.util.HashSet;
  43 import java.util.List;
  44 import java.util.Map;
  45 import java.util.Set;
  46 import java.util.TreeSet;
  47 import java.util.function.Function;
  48 
  49 import static java.util.stream.Collectors.*;
  50 import static java.lang.module.ModuleDescriptor.Requires.Modifier.TRANSITIVE;
  51 
  52 /**
  53  * Generate the DOT file for a module graph for each module in the JDK
  54  * after transitive reduction.
  55  */
  56 public class GenGraphs {
  57 
  58     public static void main(String[] args) throws Exception {













  59 
  60         if (args.length != 1) {
  61             System.err.println("ERROR: specify the output directory");
  62             System.exit(1);
  63         }
  64         Path dir = Paths.get(args[0]);
  65         Files.createDirectories(dir);
  66 
  67         ModuleFinder finder = ModuleFinder.ofSystem();


  68 
  69         Set<ModuleDescriptor> javaSEModules
  70             = new TreeSet<>(finder.findAll().stream()
  71                                   .map(ModuleReference::descriptor)
  72                                   .filter(m -> (m.name().startsWith("java.") &&
  73                                                !m.name().equals("java.smartcardio")))
  74                                   .collect(toSet()));
  75         Set<ModuleDescriptor> jdkModules
  76             = new TreeSet<>(finder.findAll().stream()
  77                                   .map(ModuleReference::descriptor)
  78                                   .filter(m -> !javaSEModules.contains(m))
  79                                   .collect(toSet()));
  80 
  81         GenGraphs genGraphs = new GenGraphs(dir, javaSEModules, jdkModules);
  82         Set<String> mods = new HashSet<>();
  83         for (ModuleReference mref: finder.findAll()) {
  84             mods.add(mref.descriptor().name());
  85             genGraphs.genDotFile(mref);







  86         }
  87 
  88         // all modules
  89         genGraphs.genDotFile("jdk", mods);
  90 
  91     }
  92 
  93     private static final String ORANGE = "#e76f00";
  94     private static final String BLUE = "#437291";
  95     private static final String GRAY = "#dddddd";
  96 
  97     private static final String REEXPORTS = "";
  98     private static final String REQUIRES = "style=\"dashed\"";
  99     private static final String REQUIRES_BASE = "color=\"" + GRAY + "\"";
 100 
 101     private static final Map<String,Integer> weights = new HashMap<>();
 102     private static final List<Set<String>> ranks = new ArrayList<>();
 103 
 104     private static void weight(String s, String t, int w) {
 105         weights.put(s + ":" + t, w);
 106     }
 107 
 108     private static int weightOf(String s, String t) {
 109         int w = weights.getOrDefault(s + ":" + t, 1);
 110         if (w != 1)
 111             return w;
 112         if (s.startsWith("java.") && t.startsWith("java."))
 113             return 10;
 114         return 1;
 115     }
 116 
 117     static {
 118         int h = 1000;
 119         weight("java.se", "java.sql.rowset", h * 10);
 120         weight("java.sql.rowset", "java.sql", h * 10);
 121         weight("java.sql", "java.xml", h * 10);
 122         weight("java.xml", "java.base", h * 10);
 123 
 124         ranks.add(Set.of("java.logging", "java.scripting", "java.xml"));
 125         ranks.add(Set.of("java.sql"));
 126         ranks.add(Set.of("java.compiler", "java.instrument"));
 127         ranks.add(Set.of("java.desktop", "java.management"));
 128         ranks.add(Set.of("java.corba", "java.xml.ws"));
 129         ranks.add(Set.of("java.xml.bind", "java.xml.ws.annotation"));
 130 



 131     }
 132 
 133     private final Path dir;
 134     private final Set<ModuleDescriptor> javaGroup;
 135     private final Set<ModuleDescriptor> jdkGroup;
 136 
 137     GenGraphs(Path dir, Set<ModuleDescriptor> javaGroup, Set<ModuleDescriptor> jdkGroup) {
 138         this.dir = dir;
 139         this.javaGroup = Collections.unmodifiableSet(javaGroup);
 140         this.jdkGroup = Collections.unmodifiableSet(jdkGroup);
 141     }
 142 
 143     /**
 144      * Generates a dot file for the given module reference as the root.
 145      */
 146     void genDotFile(ModuleReference mref) throws IOException {
 147         String name = mref.descriptor().name();
 148         genDotFile(name, Set.of(name));
 149     }
 150 
 151     /**
 152      * Generates a dot file for the given set of root modules.
 153      */
 154     void genDotFile(String name, Set<String> roots) throws IOException {
 155         Configuration cf =
 156             Configuration.empty().resolve(ModuleFinder.ofSystem(),
 157                                           ModuleFinder.of(),
 158                                           roots);
 159 
 160         Set<ModuleDescriptor> mds = cf.modules().stream()
 161                 .map(ResolvedModule::reference)
 162                 .map(ModuleReference::descriptor)
 163                 .collect(toSet());
 164 
 165         // generate a dot file for the resolved graph
 166         try (OutputStream os = Files.newOutputStream(dir.resolve(name + ".dot"));
 167              PrintStream out = new PrintStream(os)) {
 168             printGraph(out, name, gengraph(cf),
 169                        mds.stream()
 170                           .collect(toMap(ModuleDescriptor::name, Function.identity()))
 171             );
 172         }
 173 
 174         if (name.equals("java.se") || name.equals("java.se.ee")) {
 175             // generate a dot file for Java SE module graph
 176             try (OutputStream os = Files.newOutputStream(dir.resolve(name + "-spec.dot"));
 177                  PrintStream out = new PrintStream(os)) {
 178                 // transitive reduction on the graph of `requires transitive` edges
 179                 // filter out jdk.* modules which are implementation dependences
 180                 Graph<String> graph = requiresTransitiveGraph(cf, true);
 181                 printGraph(out, name, graph,
 182                            mds.stream()
 183                               .filter(md -> !md.name().startsWith("jdk.") &&
 184                                                 graph.nodes().contains(md.name()))
 185                               .collect(toMap(ModuleDescriptor::name, Function.identity()))
 186                 );
 187             }
 188         }
 189     }
 190 
 191     private void printGraph(PrintStream out,
 192                             String name,
 193                             Graph<String> graph,
 194                             Map<String, ModuleDescriptor> nameToModule)
 195         throws IOException
 196     {
 197             Set<ModuleDescriptor> descriptors = new TreeSet<>(nameToModule.values());
 198 
 199             out.format("digraph \"%s\" {%n", name);
 200             out.format("size=\"25,25\";");
 201             out.format("nodesep=.5;%n");
 202             out.format("ranksep=1.5;%n");
 203             out.format("pencolor=transparent;%n");
 204             out.format("node [shape=plaintext, fontname=\"DejaVuSans\", fontsize=36, margin=\".2,.2\"];%n");
 205             out.format("edge [penwidth=4, color=\"#999999\", arrowhead=open, arrowsize=2];%n");
 206 
 207             out.format("subgraph %sse {%n", name.equals("jdk") ? "cluster_" : "");
 208             descriptors.stream()
 209                 .filter(javaGroup::contains)
 210                 .map(ModuleDescriptor::name)
 211                 .forEach(mn -> out.format("  \"%s\" [fontcolor=\"%s\", group=%s];%n",
 212                                           mn, ORANGE, "java"));
 213             out.format("}%n");
 214 
 215             // same ranks
 216             ranks.stream()
 217                 .map(group -> descriptors.stream()
 218                                          .map(ModuleDescriptor::name)
 219                                          .filter(group::contains)
 220                                          .map(mn -> "\"" + mn + "\"")
 221                                          .collect(joining(",")))
 222                 .filter(group -> group.length() > 0)
 223                 .forEach(group -> out.format("{rank=same %s}%n", group));
 224 
 225             descriptors.stream()
 226                 .filter(jdkGroup::contains)
 227                 .map(ModuleDescriptor::name)
 228                 .forEach(mn -> out.format("  \"%s\" [fontcolor=\"%s\", group=%s];%n",
 229                                           mn, BLUE, "jdk"));
 230 
 231             descriptors.stream()
 232                 .forEach(md -> {
 233                     String mn = md.name();
 234                     Set<String> requiresTransitive = md.requires().stream()
 235                             .filter(d -> d.modifiers().contains(TRANSITIVE))
 236                             .map(d -> d.name())
 237                             .collect(toSet());
 238 
 239                     graph.adjacentNodes(mn)
 240                          .stream()
 241                          .filter(nameToModule::containsKey)
 242                          .forEach(dn -> {
 243                              String attr = dn.equals("java.base") ? REQUIRES_BASE
 244                                     : (requiresTransitive.contains(dn) ? REEXPORTS : REQUIRES);
 245                              int w = weightOf(mn, dn);
 246                              if (w > 1)
 247                                  attr += "weight=" + w;
 248                              out.format("  \"%s\" -> \"%s\" [%s];%n", mn, dn, attr);
 249                          });
 250                 });
 251 
 252             out.println("}");
 253     }
 254 
 255     /**
 256      * Returns a Graph of the given Configuration after transitive reduction.
 257      *
 258      * Transitive reduction of requires transitive edge and requires edge have
 259      * to be applied separately to prevent the requires transitive edges
 260      * (e.g. U -> V) from being reduced by a path (U -> X -> Y -> V)
 261      * in which  V would not be re-exported from U.
 262      */
 263     private Graph<String> gengraph(Configuration cf) {
 264         Graph.Builder<String> builder = new Graph.Builder<>();
 265         for (ResolvedModule resolvedModule : cf.modules()) {
 266             String mn = resolvedModule.reference().descriptor().name();
 267             builder.addNode(mn);
 268             resolvedModule.reads().stream()
 269                     .map(ResolvedModule::name)
 270                     .forEach(target -> builder.addEdge(mn, target));
 271         }
 272         Graph<String> rpg = requiresTransitiveGraph(cf, false);
 273         return builder.build().reduce(rpg);
 274     }
 275 
 276     /**
 277      * Returns a Graph containing only requires transitive edges
 278      * with transitive reduction.
 279      */
 280     private Graph<String> requiresTransitiveGraph(Configuration cf, boolean includeBase) {
 281         Graph.Builder<String> builder = new Graph.Builder<>();
 282         for (ResolvedModule resolvedModule : cf.modules()) {
 283             ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
 284             String mn = descriptor.name();
 285             descriptor.requires().stream()
 286                     .filter(d -> d.modifiers().contains(TRANSITIVE)
 287                                     || (includeBase && d.name().equals("java.base")))
 288                     .map(d -> d.name())
 289                     .forEach(d -> builder.addEdge(mn, d));
 290         }
 291         return builder.build().reduce();
 292     }
 293 }


   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package build.tools.jigsaw;
  27 
  28 import com.sun.tools.jdeps.ModuleDotGraph;
  29 import com.sun.tools.jdeps.ModuleDotGraph.DotGraphBuilder;
  30 
  31 import java.io.IOException;


  32 import java.lang.module.Configuration;
  33 import java.lang.module.ModuleDescriptor;
  34 import java.lang.module.ModuleFinder;
  35 import java.lang.module.ModuleReference;

  36 import java.nio.file.Files;
  37 import java.nio.file.Path;
  38 import java.nio.file.Paths;


  39 import java.util.HashMap;
  40 import java.util.HashSet;

  41 import java.util.Map;
  42 import java.util.Set;





  43 
  44 /**
  45  * Generate the DOT file for a module graph for each module in the JDK
  46  * after transitive reduction.
  47  */
  48 public class GenGraphs {
  49 
  50     public static void main(String[] args) throws Exception {
  51         Path dir = null;
  52         boolean spec = false;
  53         for (int i=0; i < args.length; i++) {
  54             String arg = args[i];
  55             if (arg.equals("--spec")) {
  56                 spec = true;
  57             } else if (arg.equals("--output")) {
  58                 i++;
  59                 dir = i < args.length ? Paths.get(args[i]) : null;
  60             } else if (arg.startsWith("-")) {
  61                 throw new IllegalArgumentException("Invalid option: " + arg);
  62             }
  63         }
  64 
  65         if (dir == null) {
  66             System.err.println("ERROR: must specify --output argument");
  67             System.exit(1);
  68         }


  69 
  70         // setup and configure the dot graph attributes
  71         initDotGraphAttributes();
  72         Files.createDirectories(dir);
  73 
  74         GenGraphs genGraphs = new GenGraphs(dir, spec);










  75 
  76         // print dot file for each module
  77         Map<String, Configuration> configurations = new HashMap<>();
  78         Set<String> modules = new HashSet<>();
  79         ModuleFinder finder = ModuleFinder.ofSystem();
  80         for (ModuleReference mref : finder.findAll()) {
  81             String name = (mref.descriptor().name());
  82             modules.add(name);
  83             if (genGraphs.accept(name, mref.descriptor())) {
  84                 configurations.put(name, Configuration.empty()
  85                                                       .resolve(finder,
  86                                                                ModuleFinder.of(),
  87                                                                Set.of(name)));
  88             }




  89         }
  90 
  91         if (genGraphs.accept("jdk", null)) {
  92             // print a graph of all JDK modules
  93             configurations.put("jdk", Configuration.empty()
  94                                                    .resolve(finder,
  95                                                             ModuleFinder.of(),
  96                                                             modules));







  97         }
  98 
  99         genGraphs.genDotFiles(configurations);






 100     }
 101 
 102     static void initDotGraphAttributes() {
 103         int h = 1000;
 104         DotGraphBuilder.weight("java.se", "java.sql.rowset", h * 10);
 105         DotGraphBuilder.weight("java.sql.rowset", "java.sql", h * 10);
 106         DotGraphBuilder.weight("java.sql", "java.xml", h * 10);
 107         DotGraphBuilder.weight("java.xml", "java.base", h * 10);
 108 
 109         DotGraphBuilder.sameRankNodes(Set.of("java.logging", "java.scripting", "java.xml"));
 110         DotGraphBuilder.sameRankNodes(Set.of("java.sql"));
 111         DotGraphBuilder.sameRankNodes(Set.of("java.compiler", "java.instrument"));
 112         DotGraphBuilder.sameRankNodes(Set.of("java.desktop", "java.management"));
 113         DotGraphBuilder.sameRankNodes(Set.of("java.corba", "java.xml.ws"));
 114         DotGraphBuilder.sameRankNodes(Set.of("java.xml.bind", "java.xml.ws.annotation"));
 115         DotGraphBuilder.setRankSep(0.7);
 116         DotGraphBuilder.setFontSize(12);
 117         DotGraphBuilder.setArrowSize(1);
 118         DotGraphBuilder.setArrowWidth(2);
 119     }
 120 
 121     private final Path dir;
 122     private final boolean spec;
 123     GenGraphs(Path dir, boolean spec) {


 124         this.dir = dir;
 125         this.spec = spec;

 126     }
 127 
 128     void genDotFiles(Map<String, Configuration> configurations) throws IOException {
 129         ModuleDotGraph dotGraph = new ModuleDotGraph(configurations, spec);
 130         dotGraph.genDotFiles(dir);



 131     }
 132 
 133     boolean accept(String name, ModuleDescriptor descriptor) {
 134         if (!spec) return true;






 135 
 136         if (name.equals("jdk"))
 137             return false;
 138 
 139         if (name.equals("java.se") || name.equals("java.se.ee"))
 140             return true;
 141 
 142         // only the module that has exported API
 143         return descriptor.exports().stream()
 144                          .filter(e -> !e.isQualified())
 145                          .findAny().isPresent();


























































































































 146     }
 147 }
< prev index next >