make/tools/classanalyzer/src/com/sun/classanalyzer/ClassAnalyzer.java

Print this page

        

*** 21,150 **** * questions. */ package com.sun.classanalyzer; import com.sun.classanalyzer.AnnotatedDependency.*; ! import com.sun.classanalyzer.Module.Dependency; ! import com.sun.classanalyzer.Module.PackageInfo; ! import com.sun.classanalyzer.Module.RequiresModule; ! import java.io.BufferedReader; import java.io.IOException; - import java.util.ArrayList; - import java.util.List; import java.io.File; - import java.io.FileReader; import java.io.PrintWriter; ! import java.util.LinkedHashSet; ! import java.util.Map; ! import java.util.Properties; ! import java.util.Set; ! import java.util.TreeMap; ! import java.util.TreeSet; /** * * @author Mandy Chung */ public class ClassAnalyzer { public static void main(String[] args) throws Exception { String jdkhome = null; ! String cpath = null; List<String> configs = new ArrayList<String>(); List<String> depconfigs = new ArrayList<String>(); ! String output = "."; ! String minfoPath = null; ! String nonCorePkgs = null; ! String properties = null; boolean mergeModules = true; boolean showDynamic = false; // process arguments int i = 0; while (i < args.length) { String arg = args[i++]; if (arg.equals("-jdkhome")) { jdkhome = getOption(args, i++); ! } else if (arg.equals("-cpath")) { ! cpath = getOption(args, i++); } else if (arg.equals("-config")) { configs.add(getOption(args, i++)); } else if (arg.equals("-depconfig")) { depconfigs.add(getOption(args, i++)); } else if (arg.equals("-output")) { ! output = getOption(args, i++); } else if (arg.equals("-moduleinfo")) { ! minfoPath = getOption(args, i++); ! } else if (arg.equals("-base")) { ! Module.setBaseModule(getOption(args, i++)); } else if (arg.equals("-version")) { ! Module.setVersion(getOption(args, i++)); ! } else if (arg.equals("-properties")) { ! Module.setModuleProperties(getOption(args, i++)); ! } else if (arg.equals("-noncorepkgs")) { ! nonCorePkgs = getOption(args, i++); } else if (arg.equals("-nomerge")) { // analyze the fine-grained module dependencies mergeModules = false; } else if (arg.equals("-showdynamic")) { showDynamic = true; } else { ! System.err.println("Invalid option: " + arg); ! usage(); } } ! if ((jdkhome == null && cpath == null) || (jdkhome != null && cpath != null)) { ! usage(); } if (configs.isEmpty()) { ! usage(); } if (jdkhome != null) { ! ClassPath.setJDKHome(jdkhome); ! } else if (cpath != null) { ! ClassPath.setClassPath(cpath); } ! if (nonCorePkgs != null) { ! Platform.addNonCorePkgs(nonCorePkgs); } ! // create output directory if it doesn't exist ! File dir = getDir(output); ! File moduleInfoSrc; ! if (minfoPath == null) { ! moduleInfoSrc = getDir(dir, "src"); } else { ! moduleInfoSrc = getDir(minfoPath); } ! buildModules(configs, depconfigs, mergeModules); ! // generate output files only for top-level modules ! for (Module m : Module.getTopLevelModules()) { ! String module = m.name(); ! m.printClassListTo(resolve(dir, module, "classlist")); ! m.printResourceListTo(resolve(dir, module, "resources")); ! m.printSummaryTo(resolve(dir, module, "summary")); ! m.printDependenciesTo(resolve(dir, module, "dependencies"), showDynamic); } ! // Generate other summary reports printModulesSummary(dir, showDynamic); printModulesDot(dir, showDynamic); printPackagesSummary(dir); - - // Generate module-info.java for all modules - printModuleInfos(moduleInfoSrc); - - // generate modules.list file that list the top-level modules - // and its sub-modules. The modules build reads this file - // to reconstruct the module content for each top-level module - printModulesList(dir); } private static String getOption(String[] args, int index) { if (index < args.length) { return args[index]; --- 21,273 ---- * questions. */ package com.sun.classanalyzer; import com.sun.classanalyzer.AnnotatedDependency.*; ! import com.sun.classanalyzer.Module.*; ! import com.sun.classanalyzer.ModuleInfo.*; import java.io.IOException; import java.io.File; import java.io.PrintWriter; ! import java.util.*; ! import java.util.jar.JarEntry; ! import java.util.jar.JarFile; /** + * Analyze the class dependencies of all classes in a given classpath + * and assign classes and resource files into modules as defined + * in the input configuration files. * + * The ClassAnalyzer tool will generate the following reports + * modules.list + * modules.summary + * modules.dot + * and for each module named <m>, + * <m>.classlist + * <m>.resources + * <m>.summary + * <m>.dependencies + * + * If -moduleinfo option is specified, <m>/module-info.java + * will be created under the given directory. + * + * The -update option can be specified to perform an incremental analysis + * rather than parsing all class files. + * * @author Mandy Chung */ public class ClassAnalyzer { public static void main(String[] args) throws Exception { String jdkhome = null; ! String cparg = null; List<String> configs = new ArrayList<String>(); List<String> depconfigs = new ArrayList<String>(); ! String version = null; ! String classlistDir = "."; ! String minfoDir = null; ! String nonCorePkgsFile = null; ! ClassPaths cpaths = null; boolean mergeModules = true; + boolean apiOnly = false; boolean showDynamic = false; + boolean update = false; // process arguments int i = 0; while (i < args.length) { String arg = args[i++]; if (arg.equals("-jdkhome")) { + if (cparg != null) { + error("Both -jdkhome and -classpath are set"); + } jdkhome = getOption(args, i++); ! cpaths = ClassPaths.newJDKClassPaths(jdkhome); ! } else if (arg.equals("-classpath")) { ! if (jdkhome != null) { ! error("Both -jdkhome and -classpath are set"); ! } ! cparg = getOption(args, i++); ! cpaths = ClassPaths.newInstance(cparg); } else if (arg.equals("-config")) { configs.add(getOption(args, i++)); } else if (arg.equals("-depconfig")) { depconfigs.add(getOption(args, i++)); + } else if (arg.equals("-properties")) { + Module.setModuleProperties(getOption(args, i++)); } else if (arg.equals("-output")) { ! classlistDir = getOption(args, i++); ! } else if (arg.equals("-update")) { ! update = true; } else if (arg.equals("-moduleinfo")) { ! minfoDir = getOption(args, i++); } else if (arg.equals("-version")) { ! version = getOption(args, i++); } else if (arg.equals("-nomerge")) { // analyze the fine-grained module dependencies mergeModules = false; + } else if (arg.equals("-api")) { + // analyze the fine-grained module dependencies + apiOnly = true; } else if (arg.equals("-showdynamic")) { showDynamic = true; + } else if (arg.equals("-noncorepkgs")) { + nonCorePkgsFile = getOption(args, i++); } else { ! error("Invalid option: " + arg); } } ! if (jdkhome == null && cparg == null) { ! error("-jdkhome and -classpath not set"); } + if (configs.isEmpty()) { ! error("-config not set"); } + if (version == null) { + error("-version not set"); + } + + ModuleBuilder builder; if (jdkhome != null) { ! PlatformModuleBuilder pmb = ! new PlatformModuleBuilder(configs, depconfigs, mergeModules, version); ! if (nonCorePkgsFile != null) { ! pmb.readNonCorePackagesFrom(nonCorePkgsFile); } + builder = pmb; + } else { + builder = new ModuleBuilder(configs, depconfigs, mergeModules, version); + } ! ClassAnalyzer analyzer = new ClassAnalyzer(cpaths, builder, classlistDir); ! // parse class and resource files ! analyzer.run(update, apiOnly); ! ! // print reports and module-info.java ! analyzer.generateReports(classlistDir, showDynamic); ! if (minfoDir != null) { ! analyzer.printModuleInfos(minfoDir); } + } + private final ClassPaths cpaths; + private final ModuleBuilder builder; + private final File classlistDir; + private final File moduleList; + private final Set<Module> updatedModules; // updated modules ! ClassAnalyzer(ClassPaths cpaths, ModuleBuilder builder, String clistDir) { ! this.cpaths = cpaths; ! this.builder = builder; ! this.classlistDir = new File(clistDir); ! this.moduleList = new File(clistDir, "modules.list"); ! this.updatedModules = new TreeSet<Module>(); ! } ! void run(boolean update, boolean apiOnly) throws IOException { ! if (update) { ! // incremental ! if (!moduleList.exists()) { ! // fall back to the default - analyze the entire jdk ! update = false; ! } ! } ! // parse class and resource files ! processClassPaths(update, apiOnly); ! ! // build modules & packages ! builder.run(); ! ! if (update) { ! updatedModules.addAll(cpaths.getModules()); } else { ! updatedModules.addAll(builder.getModules()); } + } ! public void generateReports(String output, boolean showDynamic) ! throws IOException { ! File outputDir = new File(output); ! if (!outputDir.exists()) ! Files.mkdirs(outputDir); ! if (updatedModules.size() > 0) { ! printModulesList(); } ! // only print classlist of the recompiled modules ! for (Module m : updatedModules) { ! // write classlist and resourcelist of a module ! ClassListWriter writer = new ClassListWriter(outputDir, m); ! writer.printClassList(); ! writer.printResourceList(); ! writer.printDependencies(); ! ! // write the summary and modules.list files ! printModuleSummary(outputDir, m); ! printModuleList(outputDir, m); ! } ! ! printSummary(outputDir, showDynamic); ! } ! ! void processClassPaths(boolean update, boolean apiOnly) throws IOException { ! // TODO: always parseDeps? ! boolean parseDeps = update == false; ! ClassPaths.Filter filter = null; ! ! long timestamp = update ? moduleList.lastModified() : -1L; ! if (timestamp > 0) { ! // for incremental build, only update the modules with ! // recompiled classes or resources files. ! final long ts = timestamp; ! filter = new ClassPaths.Filter() { ! ! @Override ! public boolean accept(File f) { ! return (f.isDirectory() ! ? true ! : f.lastModified() > ts); ! } ! ! @Override ! public boolean accept(JarFile jf, JarEntry e) throws IOException { ! long lastModified = e.getTime(); ! return lastModified <= 0 || lastModified > ts; ! } ! }; ! ! // load modules from the existing class list and resource list ! builder.loadModulesFrom(classlistDir); ! } ! ! // parse class and resource files ! cpaths.parse(filter, parseDeps, apiOnly); ! if (Trace.traceOn) { ! cpaths.printStats(); ! } ! } ! ! public void printModuleInfos(String minfoDir) throws IOException { ! for (Module m : updatedModules) { ! ModuleInfo minfo = m.getModuleInfo(); ! File mdir = new File(minfoDir, m.name()); ! PrintWriter writer = new PrintWriter(Files.resolve(mdir, "module-info", "java")); ! try { ! writer.println(minfo.toString()); ! } finally { ! writer.close(); ! } ! } ! } ! ! public void printSummary(File dir, boolean showDynamic) throws IOException { printModulesSummary(dir, showDynamic); printModulesDot(dir, showDynamic); printPackagesSummary(dir); } private static String getOption(String[] args, int index) { if (index < args.length) { return args[index];
*** 152,254 **** usage(); } return null; } ! static void buildModules(List<String> configs, ! List<String> depconfigs, ! boolean mergeModules) throws IOException { ! List<Module> modules = new ArrayList<Module>(); ! // create modules based on the input config files ! for (String file : configs) { ! for (ModuleConfig mconfig : ModuleConfig.readConfigurationFile(file)) { ! modules.add(Module.addModule(mconfig)); } } ! // parse class files ! ClassPath.parseAllClassFiles(); ! // Add additional dependencies if specified ! if (depconfigs != null && depconfigs.size() > 0) { ! DependencyConfig.parse(depconfigs); } ! // process the roots and dependencies to get the classes for each module ! for (Module m : modules) { ! m.processRootsAndReferences(); } ! // update the dependencies for classes that were subsequently allocated ! // to modules ! for (Module m : modules) { ! m.fixupDependencies(); } ! if (mergeModules) { ! Module.buildModuleMembers(); } ! Platform.fixupPlatformModules(); } ! private static void printModulesSummary(File dir, boolean showDynamic) throws IOException { // print summary of dependencies PrintWriter writer = new PrintWriter(new File(dir, "modules.summary")); try { ! for (Module m : Module.getTopLevelModules()) { ! for (Dependency dep : m.dependences()) { ! if (!showDynamic && dep.dynamic && !dep.optional) { ! continue; ! } ! if (dep.module == null || !dep.module.isBase()) { ! String prefix = ""; ! if (dep.optional) { prefix = "[optional] "; - } else if (dep.dynamic) { - prefix = "[dynamic] "; } ! Module other = dep != null ? dep.module : null; writer.format("%s%s -> %s%n", prefix, m, other); } } } } finally { writer.close(); } } ! private static void printModulesDot(File dir, boolean showDynamic) throws IOException { PrintWriter writer = new PrintWriter(new File(dir, "modules.dot")); try { writer.println("digraph jdk {"); ! for (Module m : Module.getTopLevelModules()) { ! for (Dependency dep : m.dependences()) { ! if (!showDynamic && dep.dynamic && !dep.optional) { ! continue; ! } ! if (dep.module == null || !dep.module.isBase()) { String style = ""; String color = ""; String property = ""; ! if (dep.optional) { style = "style=dotted"; } - if (dep.dynamic) { - color = "color=red"; - } if (style.length() > 0 || color.length() > 0) { String comma = ""; if (style.length() > 0 && color.length() > 0) { comma = ", "; } property = String.format(" [%s%s%s]", style, comma, color); } ! Module other = dep != null ? dep.module : null; writer.format(" \"%s\" -> \"%s\"%s;%n", m, other, property); } } } writer.println("}"); --- 275,418 ---- usage(); } return null; } ! private void printModuleSummary(File dir, Module m) throws IOException { ! PrintWriter summary = ! new PrintWriter(Files.resolve(dir, m.name(), "summary")); ! try { ! ModuleInfo mi = m.getModuleInfo(); ! long total = 0L; ! int count = 0; ! summary.format("%10s\t%10s\t%s%n", "Bytes", "Classes", "Package name"); ! for (PackageInfo info : mi.packages()) { ! if (info.count > 0) { ! summary.format("%10d\t%10d\t%s%n", ! info.filesize, info.count, info.pkgName); ! total += info.filesize; ! count += info.count; ! } ! } ! summary.format("%nTotal: %d bytes (uncompressed) %d classes%n", ! total, count); ! } finally { ! summary.close(); ! } ! } ! private void printModuleList(File dir, Module m) throws IOException { ! String s = Module.getModuleProperty(m.name() + ".modules.list"); ! if (s == null || Boolean.parseBoolean(s) == false) { ! return; } + + PrintWriter mlist = new PrintWriter(Files.resolve(dir, m.name(), "modules.list")); + try { + Set<Module> deps = m.getModuleInfo().dependences( + new Dependence.Filter() { + + @Override + public boolean accept(Dependence d) { + return !d.isOptional(); } + }); + for (Module dm : deps) { + mlist.format("%s\n", dm.name()); + } + } finally { + mlist.close(); + } + } ! private void printModuleGroup(Module group, PrintWriter writer) { ! ModuleVisitor<Set<Module>> visitor = new ModuleVisitor<Set<Module>>() { ! public void preVisit(Module p, Set<Module> leafnodes) { } ! public void visited(Module p, Module m, Set<Module> leafnodes) { ! if (m.members().isEmpty()) { ! leafnodes.add(m); } + } ! public void postVisit(Module p, Set<Module> leafnodes) { } + }; ! Set<Module> visited = new TreeSet<Module>(); ! Set<Module> members = new TreeSet<Module>(); ! group.visitMembers(visited, visitor, members); ! ! // prints leaf members that are the modules defined in ! // the modules.config files ! writer.format("%s ", group); ! for (Module m : members) { ! writer.format("%s ", m); } + writer.println(); + } ! public void printModulesList() throws IOException { ! // print module group / members relationship in ! // the dependences order so that its dependences are listed first ! PrintWriter writer = new PrintWriter(moduleList); ! try { ! for (Module m : builder.getModules()) { ! printModuleGroup(m, writer); } + } finally { + writer.close(); + } + } ! public void printModulesSummary(File dir, boolean showDynamic) throws IOException { // print summary of dependencies PrintWriter writer = new PrintWriter(new File(dir, "modules.summary")); try { ! for (Module m : builder.getModules()) { ! ModuleInfo mi = m.getModuleInfo(); ! for (Dependence dep : mi.requires()) { ! if (!dep.getModule().isBase()) { String prefix = ""; ! if (dep.isOptional()) { prefix = "[optional] "; } ! Module other = dep.getModule(); writer.format("%s%s -> %s%n", prefix, m, other); } } } } finally { writer.close(); } } ! private void printModulesDot(File dir, boolean showDynamic) throws IOException { PrintWriter writer = new PrintWriter(new File(dir, "modules.dot")); try { writer.println("digraph jdk {"); ! for (Module m : builder.getModules()) { ! ModuleInfo mi = m.getModuleInfo(); ! for (Dependence dep : mi.requires()) { ! if (!dep.getModule().isBase()) { String style = ""; String color = ""; String property = ""; ! if (dep.isOptional()) { style = "style=dotted"; } if (style.length() > 0 || color.length() > 0) { String comma = ""; if (style.length() > 0 && color.length() > 0) { comma = ", "; } property = String.format(" [%s%s%s]", style, comma, color); } ! Module other = dep.getModule(); writer.format(" \"%s\" -> \"%s\"%s;%n", m, other, property); } } } writer.println("}");
*** 255,379 **** } finally { writer.close(); } } ! private static boolean isJdkTool(Module m) { ! Set<RequiresModule> tools = Module.findModule(Platform.JDK_TOOLS).requires(); ! for (RequiresModule t : tools) { ! if (t.module() == m) { ! return true; ! } ! } ! return false; ! } ! ! private static void printMembers(Module m, PrintWriter writer) { ! for (Module member : m.members()) { ! if (!member.isEmpty() || member.allowEmpty() || m.allowEmpty()) { ! writer.format("%s ", member); ! printMembers(member, writer); ! } ! } ! if (m.members().isEmpty() && isJdkTool(m)) { ! String name = m.name().substring(4, m.name().length()); ! writer.format("%s ", name); ! } ! } ! ! private static void printModuleGroup(Module group, PrintWriter writer) { ! writer.format("%s ", group); ! printMembers(group, writer); ! writer.println(); ! } ! ! private static void printPlatformModulesList(File dir, Module m) throws IOException { ! m.printDepModuleListTo(resolve(dir, m.name(), "modules.list")); ! } ! ! private static void printModulesList(File dir) throws IOException { ! // Generate ordered list of dependences ! // for constructing the base/module images ! printPlatformModulesList(dir, Platform.jdkModule()); ! printPlatformModulesList(dir, Platform.jreModule()); ! printPlatformModulesList(dir, Platform.jdkBaseModule()); ! printPlatformModulesList(dir, Platform.jdkBaseToolModule()); ! ! // print module group / members relationship in ! // the dependences order so that its dependences are listed first ! PrintWriter writer = new PrintWriter(new File(dir, "modules.list")); ! try { ! Module jdk = Platform.jdkModule(); ! Set<Module> allModules = new LinkedHashSet<Module>(jdk.orderedDependencies()); ! // put the boot module first ! allModules.add(Platform.bootModule()); ! for (Module m : Module.getTopLevelModules()) { ! if (!allModules.contains(m)) { ! allModules.addAll(m.orderedDependencies()); ! } ! } ! ! for (Module m : allModules) { ! printModuleGroup(m, writer); ! } ! ! } finally { ! writer.close(); ! } ! } ! ! private static void printModuleInfos(File dir) throws IOException { ! for (Module m : Module.getTopLevelModules()) { ! File mdir = getDir(dir, m.name()); ! m.printModuleInfoTo(resolve(mdir, "module-info", "java")); ! } ! } ! ! private static void printPackagesSummary(File dir) throws IOException { // print package / module relationship PrintWriter writer = new PrintWriter(new File(dir, "modules.pkginfo")); try { - Map<String, Set<Module>> packages = new TreeMap<String, Set<Module>>(); - Set<String> splitPackages = new TreeSet<String>(); - - for (Module m : Module.getTopLevelModules()) { - for (PackageInfo info : m.getPackageInfos()) { - Set<Module> value = packages.get(info.pkgName); - if (value == null) { - value = new TreeSet<Module>(); - packages.put(info.pkgName, value); - } else { - // package in more than one module - splitPackages.add(info.pkgName); - } - value.add(m); - } - } - // packages that are splitted among multiple modules writer.println("Packages splitted across modules:-\n"); writer.format("%-60s %s\n", "Package", "Module"); ! for (String pkgname : splitPackages) { writer.format("%-60s", pkgname); ! for (Module m : packages.get(pkgname)) { writer.format(" %s", m); } writer.println(); } writer.println("\nPackage-private dependencies:-"); ! for (String pkgname : splitPackages) { for (Klass k : Klass.getAllClasses()) { if (k.getPackageName().equals(pkgname)) { Module m = k.getModule(); // check if this klass references a package-private // class that is in a different module for (Klass other : k.getReferencedClasses()) { ! if (other.getModule() != m && ! !other.isPublic() && ! other.getPackageName().equals(pkgname)) { String from = k.getClassName() + " (" + m + ")"; writer.format("%-60s -> %s (%s)\n", from, other, other.getModule()); } } } --- 419,457 ---- } finally { writer.close(); } } ! private void printPackagesSummary(File dir) throws IOException { // print package / module relationship PrintWriter writer = new PrintWriter(new File(dir, "modules.pkginfo")); try { // packages that are splitted among multiple modules writer.println("Packages splitted across modules:-\n"); writer.format("%-60s %s\n", "Package", "Module"); ! Map<String, Set<Module>> splitPackages = builder.getSplitPackages(); ! for (Map.Entry<String, Set<Module>> e : splitPackages.entrySet()) { ! String pkgname = e.getKey(); writer.format("%-60s", pkgname); ! for (Module m : e.getValue()) { writer.format(" %s", m); } writer.println(); } writer.println("\nPackage-private dependencies:-"); ! for (String pkgname : splitPackages.keySet()) { for (Klass k : Klass.getAllClasses()) { if (k.getPackageName().equals(pkgname)) { Module m = k.getModule(); // check if this klass references a package-private // class that is in a different module for (Klass other : k.getReferencedClasses()) { ! if (other.getModule() != m ! && !other.isPublic() ! && other.getPackageName().equals(pkgname)) { String from = k.getClassName() + " (" + m + ")"; writer.format("%-60s -> %s (%s)\n", from, other, other.getModule()); } } }
*** 383,436 **** writer.close(); } } ! private static String resolve(File dir, String mname, String suffix) { ! File f = new File(dir, mname + "." + suffix); ! return f.toString(); ! } ! private static File getDir(File path, String subdir) { ! File dir = new File(path, subdir); ! if (!dir.isDirectory()) { ! if (!dir.exists()) { ! boolean created = dir.mkdir(); ! if (!created) { ! throw new RuntimeException("Unable to create `" + dir + "'"); } - } - } - return dir; - } - - private static File getDir(String path) { - File dir = new File(path); - if (!dir.isDirectory()) { - if (!dir.exists()) { - boolean created = dir.mkdir(); - if (!created) { - throw new RuntimeException("Unable to create `" + dir + "'"); - } - } - } - return dir; - } - - private static void usage() { - System.out.println("Usage: ClassAnalyzer <options>"); - System.out.println("Options: "); - System.out.println("\t-jdkhome <JDK home> where all jars will be parsed"); - System.out.println("\t-cpath <classpath> where classes and jars will be parsed"); - System.out.println("\t Either -jdkhome or -cpath option can be used."); - System.out.println("\t-config <module config file>"); - System.out.println("\t This option can be repeated for multiple module config files"); - System.out.println("\t-output <output dir>"); - System.out.println("\t-moduleinfo <output dir of module-info.java>"); - System.out.println("\t-base <base module>"); - System.out.println("\t-version <module's version>"); - System.out.println("\t-showdynamic show dynamic dependencies in the reports"); - System.out.println("\t-nomerge specify not to merge modules"); - System.exit(-1); - } } --- 461,491 ---- writer.close(); } } ! private static void error(String msg) { ! System.err.println("ERROR: " + msg); ! System.out.println(usage()); ! System.exit(-1); } ! private static String usage() { ! StringBuilder sb = new StringBuilder(); ! sb.append("Usage: ClassAnalyzer <options>\n"); ! sb.append("Options: \n"); ! sb.append("\t-jdkhome <JDK home> where all jars will be parsed\n"); ! sb.append("\t-classpath <classpath> where classes and jars will be parsed\n"); ! sb.append("\t Either -jdkhome or -classpath option can be used.\n"); ! sb.append("\t-config <module config file>\n"); ! sb.append("\t This option can be repeated for multiple module config files\n"); ! sb.append("\t-output <output dir>\n"); ! sb.append("\t-update update modules with newer files\n"); ! sb.append("\t-moduleinfo <output dir of module-info.java>\n"); ! sb.append("\t-properties module's properties\n"); ! sb.append("\t-noncorepkgs NON_CORE_PKGS.gmk\n"); ! sb.append("\t-version <module's version>\n"); ! sb.append("\t-showdynamic show dynamic dependencies in the reports\n"); ! sb.append("\t-nomerge specify not to merge modules\n"); ! return sb.toString(); } }