1 /*
   2  * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   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 
  30 import java.io.IOException;
  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.nio.file.Files;
  36 import java.nio.file.Path;
  37 import java.nio.file.Paths;
  38 import java.util.ArrayList;
  39 import java.util.HashMap;
  40 import java.util.HashSet;
  41 import java.util.List;
  42 import java.util.Map;
  43 import java.util.Set;
  44 
  45 /**
  46  * Generate the DOT file for a module graph for each module in the JDK
  47  * after transitive reduction.
  48  */
  49 public class GenGraphs {
  50 
  51     public static void main(String[] args) throws Exception {
  52         Path dir = null;
  53         boolean spec = false;
  54         for (int i=0; i < args.length; i++) {
  55             String arg = args[i];
  56             if (arg.equals("--spec")) {
  57                 spec = true;
  58             } else if (arg.equals("--output")) {
  59                 i++;
  60                 dir = i < args.length ? Paths.get(args[i]) : null;
  61             } else if (arg.startsWith("-")) {
  62                 throw new IllegalArgumentException("Invalid option: " + arg);
  63             }
  64         }
  65 
  66         if (dir == null) {
  67             System.err.println("ERROR: must specify --output argument");
  68             System.exit(1);
  69         }
  70 
  71         Files.createDirectories(dir);
  72         GenGraphs genGraphs = new GenGraphs(dir, spec);
  73 
  74         // print dot file for each module
  75         Map<String, Configuration> configurations = new HashMap<>();
  76         Set<String> modules = new HashSet<>();
  77         ModuleFinder finder = ModuleFinder.ofSystem();
  78         for (ModuleReference mref : finder.findAll()) {
  79             String name = (mref.descriptor().name());
  80             modules.add(name);
  81             if (genGraphs.accept(name, mref.descriptor())) {
  82                 configurations.put(name, Configuration.empty()
  83                                                       .resolve(finder,
  84                                                                ModuleFinder.of(),
  85                                                                Set.of(name)));
  86             }
  87         }
  88 
  89         if (genGraphs.accept("jdk", null)) {
  90             // print a graph of all JDK modules
  91             configurations.put("jdk", Configuration.empty()
  92                                                    .resolve(finder,
  93                                                             ModuleFinder.of(),
  94                                                             modules));
  95         }
  96 
  97         genGraphs.genDotFiles(configurations);
  98     }
  99 
 100     /**
 101      * Custom dot file attributes.
 102      */
 103     static class ModuleGraphAttributes implements ModuleDotGraph.Attributes {
 104         final Map<String, Integer> weights = new HashMap<>();
 105         final List<Set<String>> ranks = new ArrayList<>();
 106 
 107         ModuleGraphAttributes() {
 108             int h = 1000;
 109             weight("java.se", "java.sql.rowset", h * 10);
 110             weight("java.sql.rowset", "java.sql", h * 10);
 111             weight("java.sql", "java.xml", h * 10);
 112             weight("java.xml", "java.base", h * 10);
 113 
 114             ranks.add(Set.of("java.logging", "java.scripting", "java.xml"));
 115             ranks.add(Set.of("java.sql"));
 116             ranks.add(Set.of("java.compiler", "java.instrument"));
 117             ranks.add(Set.of("java.desktop", "java.management"));
 118             ranks.add(Set.of("java.corba", "java.xml.ws"));
 119             ranks.add(Set.of("java.xml.bind", "java.xml.ws.annotation"));
 120         }
 121 
 122         @Override
 123         public double rankSep() {
 124             return 0.6;
 125         }
 126 
 127         @Override
 128         public int fontSize() {
 129             return 12;
 130         }
 131 
 132         @Override
 133         public String fontName() {
 134             return "DejaVuSans bold";
 135         }
 136 
 137         @Override
 138         public String fontColor() {
 139             return BLACK;
 140         }
 141 
 142         @Override
 143         public int arrowSize() {
 144             return 1;
 145         }
 146 
 147         @Override
 148         public int arrowWidth() {
 149             return 2;
 150         }
 151 
 152         @Override
 153         public String arrowColor() {
 154             return DARK_GRAY;
 155         }
 156 
 157         @Override
 158         public List<Set<String>> ranks() {
 159             return ranks;
 160         }
 161 
 162         @Override
 163         public String requiresMandatedColor() {
 164             return DARK_GRAY;
 165         }
 166 
 167         @Override
 168         public String javaSubgraphColor() {
 169             return ORANGE;
 170         }
 171 
 172         @Override
 173         public String jdkSubgraphColor() {
 174             return BLUE;
 175         }
 176 
 177         @Override
 178         public int weightOf(String s, String t) {
 179             int w = weights.getOrDefault(s + ":" + t, 1);
 180             if (w != 1)
 181                 return w;
 182             if (s.startsWith("java.") && t.startsWith("java."))
 183                 return 10;
 184             return 1;
 185         }
 186 
 187         public void weight(String s, String t, int w) {
 188             weights.put(s + ":" + t, w);
 189         }
 190     }
 191 
 192     private static final ModuleGraphAttributes ATTRIBUTES =
 193         new ModuleGraphAttributes();
 194 
 195     private final Path dir;
 196     private final boolean spec;
 197     GenGraphs(Path dir, boolean spec) {
 198         this.dir = dir;
 199         this.spec = spec;
 200     }
 201 
 202     void genDotFiles(Map<String, Configuration> configurations) throws IOException {
 203         ModuleDotGraph dotGraph = new ModuleDotGraph(configurations, spec);
 204         dotGraph.genDotFiles(dir, ATTRIBUTES);
 205     }
 206 
 207     /**
 208      * Returns true for any name if generating graph for non-spec;
 209      * otherwise, returns true except "jdk" and name with "jdk.internal." prefix
 210      */
 211     boolean accept(String name, ModuleDescriptor descriptor) {
 212         if (!spec)
 213             return true;
 214 
 215         return !name.equals("jdk") && !name.startsWith("jdk.internal.");
 216     }
 217 }