jdk/make/modules/tools/src/com/sun/classanalyzer/ClassAnalyzer.java
changeset 8642 a6cccd458bef
parent 8641 fbf4a969ccba
parent 8608 8e043a4d3cf6
child 8643 def8e16dd237
equal deleted inserted replaced
8641:fbf4a969ccba 8642:a6cccd458bef
     1 /*
       
     2  * Copyright (c) 2009, 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.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 package com.sun.classanalyzer;
       
    24 
       
    25 import com.sun.classanalyzer.AnnotatedDependency.*;
       
    26 import com.sun.classanalyzer.Module.Dependency;
       
    27 import com.sun.classanalyzer.Module.PackageInfo;
       
    28 import java.io.IOException;
       
    29 import java.util.ArrayList;
       
    30 import java.util.List;
       
    31 import java.io.File;
       
    32 import java.io.PrintWriter;
       
    33 import java.util.Map;
       
    34 import java.util.Set;
       
    35 import java.util.TreeMap;
       
    36 import java.util.TreeSet;
       
    37 
       
    38 /**
       
    39  *
       
    40  * @author Mandy Chung
       
    41  */
       
    42 public class ClassAnalyzer {
       
    43 
       
    44     public static void main(String[] args) throws Exception {
       
    45         String jdkhome = null;
       
    46         String cpath = null;
       
    47         List<String> configs = new ArrayList<String>();
       
    48         List<String> depconfigs = new ArrayList<String>();
       
    49         String output = ".";
       
    50         boolean mergeModules = true;
       
    51         boolean showDynamic = false;
       
    52 
       
    53         // process arguments
       
    54         int i = 0;
       
    55         while (i < args.length) {
       
    56             String arg = args[i++];
       
    57             if (arg.equals("-jdkhome")) {
       
    58                 if (i < args.length) {
       
    59                     jdkhome = args[i++];
       
    60                 } else {
       
    61                     usage();
       
    62                 }
       
    63             } else if (arg.equals("-cpath")) {
       
    64                 if (i < args.length) {
       
    65                     cpath = args[i++];
       
    66                 } else {
       
    67                     usage();
       
    68                 }
       
    69             } else if (arg.equals("-config")) {
       
    70                 if (i < args.length) {
       
    71                     configs.add(args[i++]);
       
    72                 } else {
       
    73                     usage();
       
    74                 }
       
    75             } else if (arg.equals("-depconfig")) {
       
    76                 if (i < args.length) {
       
    77                     depconfigs.add(args[i++]);
       
    78                 } else {
       
    79                     usage();
       
    80                 }
       
    81             } else if (arg.equals("-output")) {
       
    82                 if (i < args.length) {
       
    83                     output = args[i++];
       
    84                 } else {
       
    85                     usage();
       
    86                 }
       
    87             } else if (arg.equals("-base")) {
       
    88                 ModuleConfig.setBaseModule(args[i++]);
       
    89             } else if (arg.equals("-nomerge")) {
       
    90                 // analyze the fine-grained module dependencies
       
    91                 mergeModules = false;
       
    92             } else if (arg.equals("-showdynamic")) {
       
    93                 showDynamic = true;
       
    94             } else {
       
    95                 System.err.println("Invalid option: " + arg);
       
    96                 usage();
       
    97             }
       
    98         }
       
    99 
       
   100         if ((jdkhome == null && cpath == null) || (jdkhome != null && cpath != null)) {
       
   101             usage();
       
   102         }
       
   103         if (configs.isEmpty()) {
       
   104             usage();
       
   105         }
       
   106 
       
   107         if (jdkhome != null) {
       
   108             ClassPath.setJDKHome(jdkhome);
       
   109         } else if (cpath != null) {
       
   110             ClassPath.setClassPath(cpath);
       
   111         }
       
   112 
       
   113         // create output directory if it doesn't exist
       
   114         File dir = new File(output);
       
   115         if (!dir.isDirectory()) {
       
   116             if (!dir.exists()) {
       
   117                 boolean created = dir.mkdir();
       
   118                 if (!created) {
       
   119                     throw new RuntimeException("Unable to create `" + dir + "'");
       
   120                 }
       
   121             }
       
   122         }
       
   123 
       
   124         buildModules(configs, depconfigs, mergeModules);
       
   125 
       
   126         // generate output files
       
   127         for (Module m : modules) {
       
   128             // only generate reports for top-level modules
       
   129             if (m.group() == m) {
       
   130                 m.printClassListTo(resolve(dir, m.name(), "classlist"));
       
   131                 m.printResourceListTo(resolve(dir, m.name(), "resources"));
       
   132                 m.printSummaryTo(resolve(dir, m.name(), "summary"));
       
   133                 m.printDependenciesTo(resolve(dir, m.name(), "dependencies"), showDynamic);
       
   134             }
       
   135         }
       
   136 
       
   137         // Generate other summary reports
       
   138         printModulesSummary(dir, showDynamic);
       
   139         printModulesDot(dir, showDynamic);
       
   140         printModulesList(dir);
       
   141         printPackagesSummary(dir);
       
   142     }
       
   143     private static List<Module> modules = new ArrayList<Module>();
       
   144 
       
   145     static void buildModules(List<String> configs,
       
   146             List<String> depconfigs,
       
   147             boolean mergeModules) throws IOException {
       
   148         // create modules based on the input config files
       
   149         for (String file : configs) {
       
   150             for (ModuleConfig mconfig : ModuleConfig.readConfigurationFile(file)) {
       
   151                 modules.add(Module.addModule(mconfig));
       
   152             }
       
   153         }
       
   154 
       
   155         // parse class files
       
   156         ClassPath.parseAllClassFiles();
       
   157 
       
   158         // Add additional dependencies if specified
       
   159         if (depconfigs != null && depconfigs.size() > 0) {
       
   160             DependencyConfig.parse(depconfigs);
       
   161         }
       
   162 
       
   163         // process the roots and dependencies to get the classes for each module
       
   164         for (Module m : modules) {
       
   165             m.processRootsAndReferences();
       
   166         }
       
   167 
       
   168         // update the dependencies for classes that were subsequently allocated
       
   169         // to modules
       
   170         for (Module m : modules) {
       
   171             m.fixupDependencies();
       
   172         }
       
   173 
       
   174         if (mergeModules) {
       
   175             Module.buildModuleMembers();
       
   176         }
       
   177     }
       
   178 
       
   179     private static void printModulesSummary(File dir, boolean showDynamic) throws IOException {
       
   180         // print summary of dependencies
       
   181         PrintWriter writer = new PrintWriter(new File(dir, "modules.summary"));
       
   182         try {
       
   183             for (Module m : modules) {
       
   184                 // only show top-level module dependencies
       
   185                 if (m.group() == m) {
       
   186                     for (Dependency dep : m.dependents()) {
       
   187                         if (!showDynamic && dep.dynamic && dep.optional) {
       
   188                             continue;
       
   189                         }
       
   190                         if (dep.module == null || !dep.module.isBase()) {
       
   191 
       
   192                             String prefix = "";
       
   193                             if (dep.optional) {
       
   194                                 if (dep.dynamic) {
       
   195                                     prefix = "[dynamic] ";
       
   196                                 } else {
       
   197                                     prefix = "[optional] ";
       
   198                                 }
       
   199                             }
       
   200 
       
   201                             Module other = dep != null ? dep.module : null;
       
   202                             writer.format("%s%s -> %s%n", prefix, m, other);
       
   203                         }
       
   204                     }
       
   205                 }
       
   206             }
       
   207         } finally {
       
   208             writer.close();
       
   209         }
       
   210     }
       
   211 
       
   212     private static void printModulesDot(File dir, boolean showDynamic) throws IOException {
       
   213         PrintWriter writer = new PrintWriter(new File(dir, "modules.dot"));
       
   214         try {
       
   215             writer.println("digraph jdk {");
       
   216             for (Module m : modules) {
       
   217                 if (m.group() == m) {
       
   218                     for (Dependency dep : m.dependents()) {
       
   219                         if (!showDynamic && dep.dynamic && dep.optional) {
       
   220                             continue;
       
   221                         }
       
   222                         if (dep.module == null || !dep.module.isBase()) {
       
   223                             String style = "";
       
   224                             String color = "";
       
   225                             String property = "";
       
   226                             if (dep.optional) {
       
   227                                 style = "style=dotted";
       
   228                             }
       
   229                             if (dep.dynamic) {
       
   230                                 color = "color=red";
       
   231                             }
       
   232                             if (style.length() > 0 || color.length() > 0) {
       
   233                                 String comma = "";
       
   234                                 if (style.length() > 0 && color.length() > 0) {
       
   235                                     comma = ", ";
       
   236                                 }
       
   237                                 property = String.format(" [%s%s%s]", style, comma, color);
       
   238                             }
       
   239                             Module other = dep != null ? dep.module : null;
       
   240                             writer.format("    \"%s\" -> \"%s\"%s;%n", m, other, property);
       
   241                         }
       
   242                     }
       
   243                 }
       
   244             }
       
   245             writer.println("}");
       
   246         } finally {
       
   247             writer.close();
       
   248         }
       
   249     }
       
   250 
       
   251     private static void printMembers(Module m, PrintWriter writer) {
       
   252         for (Module member : m.members()) {
       
   253             if (!member.isEmpty()) {
       
   254                 writer.format("%s ", member);
       
   255                 printMembers(member, writer);
       
   256             }
       
   257         }
       
   258     }
       
   259 
       
   260     private static void printModulesList(File dir) throws IOException {
       
   261         // print module group / members relationship
       
   262         PrintWriter writer = new PrintWriter(new File(dir, "modules.list"));
       
   263         try {
       
   264             for (Module m : modules) {
       
   265                 if (m.group() == m && !m.isEmpty()) {
       
   266                     writer.format("%s ", m);
       
   267                     printMembers(m, writer);
       
   268                     writer.println();
       
   269                 }
       
   270             }
       
   271         } finally {
       
   272             writer.close();
       
   273         }
       
   274     }
       
   275 
       
   276     private static void printPackagesSummary(File dir) throws IOException {
       
   277         // print package / module relationship
       
   278         PrintWriter writer = new PrintWriter(new File(dir, "modules.pkginfo"));
       
   279         try {
       
   280             Map<String, Set<Module>> packages = new TreeMap<String, Set<Module>>();
       
   281             Set<String> splitPackages = new TreeSet<String>();
       
   282 
       
   283             for (Module m : modules) {
       
   284                 if (m.group() == m) {
       
   285                     for (PackageInfo info : m.getPackageInfos()) {
       
   286                         Set<Module> value = packages.get(info.pkgName);
       
   287                         if (value == null) {
       
   288                             value = new TreeSet<Module>();
       
   289                             packages.put(info.pkgName, value);
       
   290                         } else {
       
   291                             // package in more than one module
       
   292                             splitPackages.add(info.pkgName);
       
   293                         }
       
   294                         value.add(m);
       
   295                     }
       
   296                 }
       
   297             }
       
   298 
       
   299             // packages that are splitted among multiple modules
       
   300             writer.println("Packages splitted across modules:-\n");
       
   301             writer.format("%-60s  %s\n", "Package", "Module");
       
   302 
       
   303             for (String pkgname : splitPackages) {
       
   304                 writer.format("%-60s", pkgname);
       
   305                 for (Module m : packages.get(pkgname)) {
       
   306                     writer.format("  %s", m);
       
   307                 }
       
   308                 writer.println();
       
   309             }
       
   310 
       
   311             writer.println("\nPackage-private dependencies:-");
       
   312             for (String pkgname : splitPackages) {
       
   313                 for (Klass k : Klass.getAllClasses()) {
       
   314                     if (k.getPackageName().equals(pkgname)) {
       
   315                         Module m = k.getModule();
       
   316                         // check if this klass references a package-private
       
   317                         // class that is in a different module
       
   318                         for (Klass other : k.getReferencedClasses()) {
       
   319                             if (other.getModule() != m &&
       
   320                                     !other.isPublic() &&
       
   321                                     other.getPackageName().equals(pkgname)) {
       
   322                                 String from = k.getClassName() + " (" + m + ")";
       
   323                                 writer.format("%-60s -> %s (%s)\n", from, other, other.getModule());
       
   324                             }
       
   325                         }
       
   326                     }
       
   327                 }
       
   328             }
       
   329         } finally {
       
   330             writer.close();
       
   331         }
       
   332 
       
   333     }
       
   334 
       
   335     private static String resolve(File dir, String mname, String suffix) {
       
   336         File f = new File(dir, mname + "." + suffix);
       
   337         return f.toString();
       
   338 
       
   339     }
       
   340 
       
   341     private static void usage() {
       
   342         System.out.println("Usage: ClassAnalyzer <options>");
       
   343         System.out.println("Options: ");
       
   344         System.out.println("\t-jdkhome <JDK home> where all jars will be parsed");
       
   345         System.out.println("\t-cpath   <classpath> where classes and jars will be parsed");
       
   346         System.out.println("\t         Either -jdkhome or -cpath option can be used.");
       
   347         System.out.println("\t-config  <module config file>");
       
   348         System.out.println("\t         This option can be repeated for multiple module config files");
       
   349         System.out.println("\t-output  <output dir>");
       
   350         System.out.println("\t-nomerge specify not to merge modules");
       
   351         System.out.println("\t-showdynamic show dynamic dependencies in the reports");
       
   352         System.exit(-1);
       
   353     }
       
   354 }