1 /* 2 * Copyright (c) 2014, 2018, 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.Properties; 44 import java.util.Set; 45 import java.util.function.Function; 46 import java.util.stream.Collectors; 47 48 /** 49 * Generate the DOT file for a module graph for each module in the JDK 50 * after transitive reduction. 51 */ 52 public class GenGraphs { 53 54 public static void main(String[] args) throws Exception { 55 Path dir = null; 56 boolean spec = false; 57 Properties props = null; 58 for (int i=0; i < args.length; i++) { 59 String arg = args[i]; 60 if (arg.equals("--spec")) { 61 spec = true; 62 } else if (arg.equals("--dot-attributes")) { 63 if (i++ == args.length) { 64 throw new IllegalArgumentException("Missing argument: --dot-attributes option"); 65 } 66 props = new Properties(); 67 props.load(Files.newInputStream(Paths.get(args[i]))); 68 } else if (arg.equals("--output")) { 69 dir = ++i < args.length ? Paths.get(args[i]) : null; 70 } else if (arg.startsWith("-")) { 71 throw new IllegalArgumentException("Invalid option: " + arg); 72 } 73 } 74 75 if (dir == null) { 76 System.err.println("ERROR: must specify --output argument"); 77 System.exit(1); 78 } 79 80 Files.createDirectories(dir); 81 ModuleGraphAttributes attributes; 82 if (props != null) { 83 attributes = new ModuleGraphAttributes(props); 84 } else { 85 attributes = new ModuleGraphAttributes(); 86 } 87 GenGraphs genGraphs = new GenGraphs(dir, spec, attributes); 88 89 // print dot file for each module 90 Map<String, Configuration> configurations = new HashMap<>(); 91 Set<String> modules = new HashSet<>(); 92 ModuleFinder finder = ModuleFinder.ofSystem(); 93 for (ModuleReference mref : finder.findAll()) { 94 String name = (mref.descriptor().name()); 95 modules.add(name); 96 if (genGraphs.accept(name, mref.descriptor())) { 97 configurations.put(name, Configuration.empty() 98 .resolve(finder, 99 ModuleFinder.of(), 100 Set.of(name))); 101 } 102 } 103 104 if (genGraphs.accept("jdk", null)) { 105 // print a graph of all JDK modules 106 configurations.put("jdk", Configuration.empty() 107 .resolve(finder, 108 ModuleFinder.of(), 109 modules)); 110 } 111 112 genGraphs.genDotFiles(configurations); 113 } 114 115 /** 116 * Custom dot file attributes. 117 */ 118 static class ModuleGraphAttributes implements ModuleDotGraph.Attributes { 119 static Map<String, String> DEFAULT_ATTRIBUTES = Map.of( 120 "ranksep", "0.6", 121 "fontsize", "12", 122 "fontcolor", BLACK, 123 "fontname", "DejaVuSans", 124 "arrowsize", "1", 125 "arrowwidth", "2", 126 "arrowcolor", DARK_GRAY, 127 // custom 128 "requiresMandatedColor", LIGHT_GRAY, 129 "javaSubgraphColor", ORANGE, 130 "jdkSubgraphColor", BLUE 131 ); 132 133 final Map<String, Integer> weights = new HashMap<>(); 134 final List<Set<String>> ranks = new ArrayList<>(); 135 final Map<String, String> attrs; 136 ModuleGraphAttributes(Map<String, String> attrs) { 137 int h = 1000; 138 weight("java.se", "java.sql.rowset", h * 10); 139 weight("java.sql.rowset", "java.sql", h * 10); 140 weight("java.sql", "java.xml", h * 10); 141 weight("java.xml", "java.base", h * 10); 142 143 ranks.add(Set.of("java.logging", "java.scripting", "java.xml")); 144 ranks.add(Set.of("java.sql")); 145 ranks.add(Set.of("java.compiler", "java.instrument")); 146 ranks.add(Set.of("java.desktop", "java.management")); 147 148 this.attrs = attrs; 149 } 150 151 ModuleGraphAttributes() { 152 this(DEFAULT_ATTRIBUTES); 153 } 154 ModuleGraphAttributes(Properties props) { 155 this(toAttributes(props)); 156 } 157 158 @Override 159 public double rankSep() { 160 return Double.valueOf(attrs.get("ranksep")); 161 } 162 163 @Override 164 public int fontSize() { 165 return Integer.valueOf(attrs.get("fontsize")); 166 } 167 168 @Override 169 public String fontName() { 170 return attrs.get("fontname"); 171 } 172 173 @Override 174 public String fontColor() { 175 return attrs.get("fontcolor"); 176 } 177 178 @Override 179 public int arrowSize() { 180 return Integer.valueOf(attrs.get("arrowsize")); 181 } 182 183 @Override 184 public int arrowWidth() { 185 return Integer.valueOf(attrs.get("arrowwidth")); 186 } 187 188 @Override 189 public String arrowColor() { 190 return attrs.get("arrowcolor"); 191 } 192 193 @Override 194 public List<Set<String>> ranks() { 195 return ranks; 196 } 197 198 @Override 199 public String requiresMandatedColor() { 200 return attrs.get("requiresMandatedColor"); 201 } 202 203 @Override 204 public String javaSubgraphColor() { 205 return attrs.get("javaSubgraphColor"); 206 } 207 208 @Override 209 public String jdkSubgraphColor() { 210 return attrs.get("jdkSubgraphColor"); 211 } 212 213 @Override 214 public int weightOf(String s, String t) { 215 int w = weights.getOrDefault(s + ":" + t, 1); 216 if (w != 1) 217 return w; 218 if (s.startsWith("java.") && t.startsWith("java.")) 219 return 10; 220 return 1; 221 } 222 223 public void weight(String s, String t, int w) { 224 weights.put(s + ":" + t, w); 225 } 226 227 static Map<String, String> toAttributes(Properties props) { 228 return DEFAULT_ATTRIBUTES.keySet().stream() 229 .collect(Collectors.toMap(Function.identity(), 230 k -> props.getProperty(k, DEFAULT_ATTRIBUTES.get(k)))); 231 } 232 } 233 234 private final Path dir; 235 private final boolean spec; 236 private final ModuleGraphAttributes attributes; 237 GenGraphs(Path dir, boolean spec, ModuleGraphAttributes attributes) { 238 this.dir = dir; 239 this.spec = spec; 240 this.attributes = attributes; 241 } 242 243 void genDotFiles(Map<String, Configuration> configurations) throws IOException { 244 ModuleDotGraph dotGraph = new ModuleDotGraph(configurations, spec); 245 dotGraph.genDotFiles(dir, attributes); 246 } 247 248 /** 249 * Returns true for any name if generating graph for non-spec; 250 * otherwise, returns true except "jdk" and name with "jdk.internal." prefix 251 */ 252 boolean accept(String name, ModuleDescriptor descriptor) { 253 if (!spec) 254 return true; 255 256 return !name.equals("jdk") && !name.startsWith("jdk.internal."); 257 } 258 }