--- old/make/common/Defs-modules.gmk Wed Oct 20 09:30:36 2010 +++ new/make/common/Defs-modules.gmk Wed Oct 20 09:30:36 2010 @@ -29,8 +29,8 @@ # jdk modules generated from make/modules/Makefile # - this combines one or more submodules -MODULES_DIR = $(OUTPUTDIR)/modules -ABS_MODULES_DIR = $(ABS_OUTPUTDIR)/modules +MODULEPATH_DIR = $(OUTPUTDIR)/modules +ABS_MODULEPATH_DIR = $(ABS_OUTPUTDIR)/modules # Submodules # Files are copied to its own MODULE during the jdk builds @@ -50,6 +50,10 @@ MODULES_TEMPDIR = $(OUTPUTDIR)/tmp/modules ABS_MODULES_TEMPDIR = $(ABS_OUTPUTDIR)/tmp/modules +# incremental build marker file +MODULES_UPDATE_MARKER = $(SUBMODULES_DIR)/.modules.update + +# Information for building platform modules BASE_MODULE = jdk.base JIGSAW_MODULE_LIB = $(ABS_OUTPUTDIR)/lib/modules @@ -119,4 +123,8 @@ define GetBaseModuleDest $(shell $(ECHO) $1 | $(SED) $(MODULE_PATH_PATTERN) | \ $(NAWK) '{print "$(SUBMODULES_DIR)/base/"$$0}') +endef + +define TouchModule +$(ECHO) $(MODULE) >> $(MODULES_UPDATE_MARKER) endef --- old/make/common/Defs.gmk Wed Oct 20 09:30:38 2010 +++ new/make/common/Defs.gmk Wed Oct 20 09:30:37 2010 @@ -504,7 +504,8 @@ define install-module-file dest=$(call GetModuleDest, $(@D)/) ; \ $(MKDIR) -p $$dest; \ -$(CP) -f $@ $$dest +$(CP) -f $@ $$dest; \ +$(TouchModule) endef # Install all files from the directory to its module @@ -511,7 +512,8 @@ define install-module-dir dest=$(call GetModuleDest, $(@D)/) ; \ $(MKDIR) -p $$dest; \ -$(CP) -rf $(@D)/* $$dest +$(CP) -rf $(@D)/* $$dest; \ +$(TouchModule) endef # chmod the file in its module --- old/make/common/Library.gmk Wed Oct 20 09:30:39 2010 +++ new/make/common/Library.gmk Wed Oct 20 09:30:39 2010 @@ -53,6 +53,7 @@ ACTUAL_LIBRARY = $(ACTUAL_LIBRARY_DIR)/$(ACTUAL_LIBRARY_NAME) library:: $(ACTUAL_LIBRARY) + $(TouchModule) FILES_o = $(patsubst %.c, %.$(OBJECT_SUFFIX), $(addprefix $(OBJDIR)/, $(notdir $(FILES_c)))) FILES_o += $(patsubst %.s, %.$(OBJECT_SUFFIX), $(addprefix $(OBJDIR)/, $(notdir $(FILES_s)))) --- old/make/common/Modules.gmk Wed Oct 20 09:30:40 2010 +++ new/make/common/Modules.gmk Wed Oct 20 09:30:40 2010 @@ -207,7 +207,7 @@ javahome=$(MODULE_IMAGES_DIR)/$$image; \ for d in bin lib ; do \ if [ -d $$m/$$d ] ; then \ - $(CP) -rf $(ABS_MODULES_DIR)/$$m/$$d $$javahome; \ + $(CP) -rf $(ABS_MODULEPATH_DIR)/$$m/$$d $$javahome; \ fi ; \ done endef @@ -228,8 +228,8 @@ from=$(strip $1); dest=$(strip $2); \ ($(CD) $(MODULE_IMAGES_DIR)/$$from && $(FIND) . -depth -print \ | $(CPIO) -pdum $(MODULE_IMAGES_DIR)/$$dest) ; \ -if [ -d "$(ABS_MODULES_DIR)/jdk.boot/include" ] ; then \ - $(CP) -rf $(ABS_MODULES_DIR)/jdk.boot/include $(MODULE_IMAGES_DIR)/$$dest; \ +if [ -d "$(ABS_MODULEPATH_DIR)/jdk.boot/include" ] ; then \ + $(CP) -rf $(ABS_MODULEPATH_DIR)/jdk.boot/include $(MODULE_IMAGES_DIR)/$$dest; \ fi endef @@ -243,7 +243,7 @@ mid=`$(HOST_JMOD_CMD) ls -L $$mlib $$m | $(GREP) -v '#'` ; \ if [ "x$$mid" = "x" ] ; then \ $(ECHO) "Installing module $$m in $$image" ; \ - $(CD) $(ABS_MODULES_DIR) && \ + $(CD) $(ABS_MODULEPATH_DIR) && \ if [ -d $$m/resources ] ; then \ $(HOST_JMOD_CMD) install $$m/classes -r $$m/resources -L $$mlib $$m || exit 1; \ else \ @@ -257,7 +257,7 @@ # Rules for building packages -include $(BUILDDIR)/common/BuildPackages.gmk +include $(BUILDDIR)/common/BuildNativePackages.gmk PKG_TYPES = jmod-pkgs DEBIAN := $(shell \ --- old/make/common/Rules.gmk Wed Oct 20 09:30:42 2010 +++ new/make/common/Rules.gmk Wed Oct 20 09:30:41 2010 @@ -239,6 +239,7 @@ $(ECHO) "# Running javac:"; \ $(ECHO) $(JAVAC_CMD) -sourcepath "$(SOURCEPATH)" -d $(CLASSDESTDIR) @$<.filtered; \ $(JAVAC_CMD) -sourcepath "$(SOURCEPATH)" -d $(CLASSDESTDIR) @$<.filtered; \ + $(TouchModule); \ fi @$(java-vm-cleanup) --- old/make/modules/Makefile Wed Oct 20 09:30:43 2010 +++ new/make/modules/Makefile Wed Oct 20 09:30:43 2010 @@ -24,13 +24,23 @@ # BUILDDIR = .. +PRODUCT = modules include $(BUILDDIR)/common/Defs.gmk +CLASSANALYZER_JAR_FILE = $(BUILDTOOLJARDIR)/classanalyzer.jar +MODULES_CONFIG = modules.config +MODULES_GROUP = modules.group +MODULES_PROPERTIES = modules.properties +DEP_CONFIG = jdk7.depconfig +OPTIONAL_DEP_CONFIG = optional.depconfig + # # Modularizing the JDK # - Post jdk build process until the source tree is restructured # for modules build -# - /modules/ will be created for each module. +# - /modules/ will be created for each module +# - modules are installed in /lib/modules (jigsaw module +# library) # # Steps: # 0. During jdk build before this makefile is invoked, classes, @@ -37,7 +47,7 @@ # resource files, and other non-class files such as native libraries, # properties file, images, etc are created. # -# Non-class files are copied to /tmp/modules/ +# Non-class files are copied to /submodules/ # directory in this step to prepare for the post-build modularization. # # The MODULE variable defined in other makefiles specifies @@ -45,11 +55,7 @@ # The name might or might not be the same as the name of the modules # in the resulting /modules directory. # -# 1. Unpack all jars in the /lib directory to a temporary -# location (/tmp/modules/classes) to prepare for modules -# creation. -# -# 2. Run ClassAnalyzer tool to analyze all jdk classes and generate +# 1. Run ClassAnalyzer tool to analyze all jdk classes and generate # class list for all modules and also perform dependency analysis. # # Input configuration files :- @@ -62,52 +68,58 @@ # service provider. # optional.depconfig : lists the optional dependencies # -# 3. Create one directory for each module (/modules/) -# based on the output files from (2). +# For incremental build, ClassAnalyzer tool will only parse files +# that are updated since the last build (by comparing the timestamp +# of modules.list). +# +# 2. Compile module-info.java files generated by the ClassAnalyzer tool. +# (only updated module-info.java files for incremental build) +# +# 3. Modularize the JDK. It creates one directory for each module +# (/modules/) based on the output files +# from (1) and also creates a jigsaw module library. +# +# a. Run Modularizer tool to copy files listed in $m.classlist and +# $m.resources to /modules/$m/classes and +# /modules/$m/resources respectively. # -# modules.list lists the modules to be created for the modules -# build and its members. For each module (m) in modules.list, -# a. copy classes listed in $m.classlist and $m.resources to -# /modules/$m/classes and /modules/$m/resources -# respectively. -# b. copy all other files from its members (such as bin, lib, include) +# For incremental build, Modularizer tool will only copy files +# that are modified since the last run (by comparing the timestamp +# between the source and destination). +# +# b. copy all non-class files from its members (such as bin, lib, include) # to /modules/$m. -# c. move module-info.class from modules/$m directory to -# modules/$m/classes. Default module-info destination for javac -# multi-module path is /. +# +# modules.list lists the modules to be created for the modules +# build and its members. +# +# c. Install each module in the jigsaw module library # -CLASSANALYZER_JAR_FILE = $(BUILDTOOLJARDIR)/classanalyzer.jar -MODULES_CONFIG = modules.config -MODULES_GROUP = modules.group -MODULES_PROPERTIES = modules.properties -DEP_CONFIG = jdk7.depconfig -OPTIONAL_DEP_CONFIG = optional.depconfig -MODULE_CLASSES = $(ABS_MODULES_TEMPDIR)/classes +all: $(MODULES_LIST) module-info-classes modularize $(ORB_IDL) $(IR_IDL) -all:: $(MODULES_LIST) install-modules +# clean modules build +define clean-build +$(RM) -rf $(JIGSAW_MODULE_LIB) +$(RM) -rf $(MODULEINFO_DIR) +$(RM) -rf $(MODULEPATH_DIR) +$(ECHO) "" > $(MODULES_UPDATE_MARKER) +endef -JAR_LIST := $(shell $(FIND) $(ABS_OUTPUTDIR)/lib -name \*.jar -print) -unpack-jars: - @$(ECHO) ">>>Making "$@" @ `$(DATE)` ..." - $(RM) -rf $(MODULE_CLASSES) - $(MKDIR) -p $(MODULE_CLASSES) - @for jf in $(JAR_LIST) ; do \ - if [ -f $$jf ] ; then \ - $(CD) $(MODULE_CLASSES) && $(BOOT_JAR_CMD) xf $$jf $(BOOT_JAR_JFLAGS); \ - fi ; \ - done - $(CP) -rf $(CLASSBINDIR)/* $(MODULE_CLASSES) - @$(ECHO) ">>>Finished making "$@" @ `$(DATE)` ..." - -$(MODULES_LIST): $(MODULES_CONFIG) $(MODULES_GROUP) \ +# Do a clean modules build if any of the input modules.config +# files is modified. +$(MODULES_LIST):: $(MODULES_CONFIG) $(MODULES_GROUP) \ $(MODULES_PROPERTIES) \ $(DEP_CONFIG) $(OPTIONAL_DEP_CONFIG) \ $(CLASSANALYZER_JAR_FILE) + $(clean-build) + +# +# Run class analyzer to analyze the jdk. For incremental build, +# only recompiled classes are parsed. module-info.java source +# files for all or just the updated modules are generated. +$(MODULES_LIST):: $(MODULES_UPDATE_MARKER) @$(ECHO) ">>>Making "$@" @ `$(DATE)` ..." - @$(RM) -rf $(MODULE_CLASSLIST_DIR) - @$(RM) -rf $(MODULEINFO_SRC) - @$(MKDIR) -p $(MODULE_CLASSLIST_DIR) $(HOST_JAVA_CMD) \ -Dclassanalyzer.debug \ -jar $(CLASSANALYZER_JAR_FILE) \ @@ -117,36 +129,72 @@ -depconfig $(DEP_CONFIG) \ -depconfig $(OPTIONAL_DEP_CONFIG) \ -properties $(MODULES_PROPERTIES) \ - -base $(BASE_MODULE) \ -version $(MODULE_VERSION) \ + -update \ -output $(MODULE_CLASSLIST_DIR) \ -moduleinfo $(MODULEINFO_SRC) \ -noncorepkgs $(BUILDDIR)/docs/NON_CORE_PKGS.gmk + @$(java-vm-cleanup) @$(ECHO) ">>>Finished making "$@" @ `$(DATE)` ..." -# -# TODO: create only modules that belong to the specified -# BUILD_MODULES. Currently, all jdk modules are created -# but some modules may be in partial state since some -# classes are compiled due to the dependencies. - -# Modularize the jdk: - -# -# Compile module-info.java for all modules +# Rules for compiling module-info.java # -Xbootclasspath is needed to compile module-info.java # if JDK_HOST_PATH is a legacy jdk and the main class # for some modules are not in rt.jar (or $outputdir/classes) +BOOTCLASSPATH := $(shell bcp="$(OUTPUTDIR)/classes"; \ + $(CD) $(OUTPUTDIR)/lib && ( \ + jfiles=`$(FIND) . -name \*.jar -print`) ; \ + for jf in $$jfiles ; do \ + if [ -f $$jf ] ; then \ + bcp="$$bcp$(CLASSPATH_SEPARATOR)$(OUTPUTDIR)/lib/$$jf"; \ + fi \ + done; \ + $(ECHO) $$bcp) +JAVAC_CMD = $(HOST_JAVAC_CMD) \ + -Xbootclasspath:$(BOOTCLASSPATH) \ + -modulepath $(MODULEPATH_DIR) \ + -sourcepath $(MODULEINFO_SRC) +FILES_java := $(shell $(CD) $(MODULEINFO_SRC) && \ + $(FIND) . -name '*.java' -print) +CLASSDESTDIR = $(MODULEPATH_DIR) + +include $(BUILDDIR)/common/Classes.gmk + +# Compile module-info.java +# javac depends on the jigsaw module library to exist +module-info-classes: + if [ ! -d $(JIGSAW_MODULE_LIB) ] ; then \ + $(HOST_JMOD_CMD) create -N -L $(JIGSAW_MODULE_LIB) ; \ + fi + $(MAKE) classes + +$(MODULEPATH_DIR)/%/module-info.class: $(MODULEINFO_SRC)/%/module-info.java + @$(add-java-file) + +# copy module-info.class from modules/$m directory to +# modules/$m/classes. Default module-info destination for javac +# multi-module path is /. +$(MODULEPATH_DIR)/%/classes/module-info.class: $(MODULEPATH_DIR)/%/module-info.class + @$(install-non-module-file) + # -# Create modules listed in the modules.list output by the ClassAnalyzer -# 1. Merge all non-class and non-resource files from module members -# to modules/$m -# 2. Copy all class files into modules/$m/classes -# 3. Copy all resource files into modules/$m/resources -# 4. Move module-info.class from modules/$m directory to -# modules/$m/classes. Default module-info destination for -# javac multi-module path is /. -# jmod only takes a directory with classes only. -# What should be the modules directory hierachy for packaging? +# Modularize JDK and install the modules in the jigsaw library +modularize: $(MODULES_LIST) + @$(ECHO) ">>>Making "$@" @ `$(DATE)` ..." + $(HOST_JAVA_CMD) \ + -Dclassanalyzer.debug \ + -cp $(CLASSANALYZER_JAR_FILE) \ + com.sun.classanalyzer.Modularizer \ + -jdkhome $(OUTPUTDIR) \ + -update \ + -classlist $(MODULE_CLASSLIST_DIR) \ + -modulepath $(ABS_MODULEPATH_DIR) + @$(java-vm-cleanup) + $(MAKE) `$(NAWK) '{print "$(JIGSAW_MODULE_LIB)/" $$1 "/$(MODULE_VERSION)/info" }' $<` + @$(ECHO) ">>>Finished making "$@" @ `$(DATE)` ..." + +# copy-module-content +# $1 - module # # TODO: # How should the *.map (symbol -> address mappings) and @@ -154,51 +202,43 @@ # their .dll resides? We need to make them available for # troubleshooting and support purpose. # +define copy-module-content + m=$(strip $1); \ + mlib=$(ABS_MODULEPATH_DIR)/$$m ; \ + $(MKDIR) -p $$mlib ; \ + for s in `$(GREP) "^$$m " $(MODULES_LIST)` ; do \ + if [ -d $(ABS_SUBMODULES_DIR)/$$s ] ; then \ + for d in bin lib etc include ; do \ + if [ -d $(ABS_SUBMODULES_DIR)/$$s/$$d ] ; then \ + $(CP) -rf $(ABS_SUBMODULES_DIR)/$$s/$$d $$mlib; \ + fi ; \ + done \ + fi ; \ + done ; \ + $(FIND) $$mlib -name '*.map' -exec $(RM) \{} \; ; \ + $(FIND) $$mlib -name '*.pdb' -exec $(RM) \{} \; ; +endef -prep-module-lib: - $(RM) -rf $(JIGSAW_MODULE_LIB) - $(HOST_JMOD_CMD) create -N -L $(JIGSAW_MODULE_LIB) +# +# Install the module if module-info.class is updated +# or .summary is updated +# +$(JIGSAW_MODULE_LIB)/%/$(MODULE_VERSION)/info: $(MODULEPATH_DIR)/%/classes/module-info.class \ + $(MODULE_CLASSLIST_DIR)/%.summary + $(call copy-module-content,$*) + $(RM) -r $(JIGSAW_MODULE_LIB)/$* + m=$(strip $*); \ + $(ECHO) "Installing module $$m" ; \ + $(CD) $(MODULEPATH_DIR) && \ + if [ -f $(MODULE_CLASSLIST_DIR)/$$m.resources ] ; then \ + $(HOST_JMOD_CMD) install $$m/classes -r $$m/resources $$m \ + -L $(JIGSAW_MODULE_LIB) || exit 1 ; \ + else \ + $(HOST_JMOD_CMD) install $$m/classes $$m \ + -L $(JIGSAW_MODULE_LIB) || exit 1 ; \ + fi + @$(java-vm-cleanup) -modularize: unpack-jars prep-module-lib - @$(ECHO) ">>>Making "$@" @ `$(DATE)` ..." - $(RM) -rf $(MODULES_DIR) - @$(MKDIR) -p $(MODULES_DIR) - $(HOST_JAVAC_CMD) -d $(MODULES_DIR) \ - -Xbootclasspath:$(MODULE_CLASSES) \ - -modulepath $(MODULES_DIR) \ - -sourcepath $(MODULEINFO_SRC) \ - $(MODULEINFO_SRC)/*/module-info.java - $(CD) $(MODULE_CLASSES) && \ - for m in `$(NAWK) '{print $$1}' $(MODULES_LIST)` ; do \ - $(ECHO) "Creating module $$m" ; \ - mlib=$(ABS_MODULES_DIR)/$$m ; \ - $(MKDIR) -p $$mlib ; \ - for s in `$(GREP) "^$$m " $(MODULES_LIST)` ; do \ - if [ -d $(ABS_SUBMODULES_DIR)/$$s ] ; then \ - for d in bin lib etc include ; do \ - if [ -d $(ABS_SUBMODULES_DIR)/$$s/$$d ] ; then \ - $(CP) -rf $(ABS_SUBMODULES_DIR)/$$s/$$d $$mlib; \ - fi ; \ - done \ - fi ; \ - done ; \ - $(MKDIR) -p $$mlib/classes ; \ - $(MV) $$mlib/module-info.class $$mlib/classes ; \ - if [ -f $(MODULE_CLASSLIST_DIR)/$$m.classlist ] ; then \ - $(SED) -e 's%\\%\/%g' < $(MODULE_CLASSLIST_DIR)/$$m.classlist \ - | $(CPIO) -pdum $$mlib/classes ; \ - fi ; \ - if [ -f $(MODULE_CLASSLIST_DIR)/$$m.resources ] ; then \ - $(MKDIR) -p $$mlib/resources ; \ - $(SED) -e 's%\\%\/%g' < $(MODULE_CLASSLIST_DIR)/$$m.resources \ - | $(CPIO) -pdum $$mlib/resources ; \ - fi ; \ - $(FIND) $$mlib -name '*.map' -exec $(RM) \{} \; ; \ - $(FIND) $$mlib -name '*.pdb' -exec $(RM) \{} \; ; \ - done - @$(CD) $(MODULE_CLASSES) && $(java-vm-cleanup) - @$(ECHO) ">>>Finished making "$@" @ `$(DATE)` ..." - # # Temporary workaround: # Copy orb.idl and ir.idl to idlj module @@ -206,29 +246,18 @@ # # A better fix would be in make/common/internal/ImportComponents.gmk # to install imported files in a module -ORB_IDL=$(MODULES_DIR)/$(IDLJ_MODULE)/lib/orb.idl -IR_IDL=$(MODULES_DIR)/$(IDLJ_MODULE)/lib/ir.idl -$(MODULES_DIR)/$(IDLJ_MODULE)/lib/%.idl : $(LIBDIR)/%.idl +ORB_IDL=$(MODULEPATH_DIR)/$(IDLJ_MODULE)/lib/orb.idl +IR_IDL=$(MODULEPATH_DIR)/$(IDLJ_MODULE)/lib/ir.idl +$(MODULEPATH_DIR)/$(IDLJ_MODULE)/lib/%.idl : $(LIBDIR)/%.idl $(install-non-module-file) -install-modules: modularize $(ORB_IDL) $(IR_IDL) - @$(ECHO) ">>>Making "$@" @ `$(DATE)` ..." - $(CD) $(MODULES_DIR) && \ - for m in `$(NAWK) '{print $$1}' $(MODULES_LIST)` ; do \ - $(ECHO) "Installing module $$m" ; \ - if [ -f $(MODULE_CLASSLIST_DIR)/$$m.resources ] ; then \ - $(HOST_JMOD_CMD) install $$m/classes -r $$m/resources $$m \ - -L $(JIGSAW_MODULE_LIB) || exit 1 ; \ - else \ - $(HOST_JMOD_CMD) install $$m/classes $$m \ - -L $(JIGSAW_MODULE_LIB) || exit 1 ; \ - fi ; \ - done - @$(ECHO) ">>>Finished making "$@" @ `$(DATE)` ..." - clean clobber:: - $(RM) -rf $(MODULEINFO_DIR) - $(RM) -rf $(MODULES_DIR) - $(RM) -rf $(JIGSAW_MODULE_LIB) + $(clean-build) -.PHONY: unpack-jars gen-classlist prep-module-lib modularize install-modules +.PHONY: modularize module-info-classes + +# GNU make bug: make pattern rules delete intermediate files +# Workaround: marks the files as precious +.PRECIOUS: $(MODULEPATH_DIR)/%/classes/module-info.class \ + $(MODULEPATH_DIR)/%/module-info.class \ + $(MODULEPATH_DIR)/%/.modules.update --- old/make/modules/modules.properties Wed Oct 20 09:30:44 2010 +++ new/make/modules/modules.properties Wed Oct 20 09:30:44 2010 @@ -23,9 +23,31 @@ # questions. # +# The following modules must be defined in the input modules.config files +platform.boot.module = jdk.boot +platform.base.module = jdk.base + +# ClassAnalyzer will create the platform.jdk.module and platform.jre.module +# when analyzing the jdk. + +platform.jdk.module = jdk +platform.jre.module = jdk.jre +platform.jre.tools.module = jdk.tools.jre + +# For legacy support: +# platform.legacy.module = jdk.legacy + + +# +# supported properties of a module +# .allow.empty +# .alias +# .modules.list - print an ordered list of its required modules +# + jdwp.allow.empty = true -jdk.jdwp.allow.empty = true -sun.xml.alias = sun.xmlparser -sun.xerces.alias = sun.xmlparser -sun.xalan.alias = sun.xmltransform +jdk.modules.list = true +jdk.jre.modules.list = true +jdk.base.modules.list = true +jdk.tools.base.modules.list = true --- old/make/tools/classanalyzer/build.xml Wed Oct 20 09:30:46 2010 +++ new/make/tools/classanalyzer/build.xml Wed Oct 20 09:30:45 2010 @@ -27,4 +27,5 @@ - + + --- old/make/tools/classanalyzer/nbproject/project.properties Wed Oct 20 09:30:47 2010 +++ new/make/tools/classanalyzer/nbproject/project.properties Wed Oct 20 09:30:47 2010 @@ -57,7 +57,7 @@ excludes= file.reference.tools-src=src -file.reference.tools.jar=${jdk.home}/lib/tools.jar +file.reference.tools.jar=/export/mchung/jigsaw/bundles/solaris-i586/lib/tools.jar includes=** jar.compress=false javac.classpath=\ @@ -79,7 +79,7 @@ main.class=com.sun.classanalyzer.ClassAnalyzer manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF -platform.active=JDK7 +platform.active=JDK_1.7 run.classpath=\ ${javac.classpath}:\ ${build.classes.dir} @@ -86,7 +86,7 @@ # Space-separated list of JVM arguments used when running the project # (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value # or test-sys-prop.name=value to set system properties for unit tests): -run.jvmargs=-Xmx896m -Xms128m +run.jvmargs=-Xmx896m -Xms128m -Dclassanalyzer.debug run.test.classpath= source.encoding=UTF-8 src.dir=${file.reference.tools-src} --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/AnnotatedDependency.java Wed Oct 20 09:30:49 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/AnnotatedDependency.java Wed Oct 20 09:30:48 2010 @@ -36,6 +36,7 @@ import java.util.Map; import com.sun.classanalyzer.Module.Reference; +import com.sun.classanalyzer.ModuleInfo.Dependence; import java.util.LinkedList; import java.util.TreeMap; @@ -277,7 +278,7 @@ static void readServiceConfiguration(String config, List names) { BufferedReader br = null; try { - InputStream is = ClassPath.open(config); + InputStream is = ClassPaths.open(config); if (is != null) { // Properties doesn't perserve the order of the input file br = new BufferedReader(new InputStreamReader(is, "utf-8")); @@ -571,17 +572,17 @@ return result; } - static Set getDependencies(Module m) { + static Set getDependencies(Module m) { // ensure it's initialized initDependencies(); - Set deps = new TreeSet(); + Set deps = new TreeSet(); for (Reference ref : annotatedDepsMap.keySet()) { if (m.contains(ref.referrer())) { Module other = m.getModuleDependence(ref.referree()); if (other != null) { for (AnnotatedDependency ad : annotatedDepsMap.get(ref)) { - Module.Dependency d = new Module.Dependency(other, ad.isOptional(), ad.isDynamic()); + Dependence d = new Dependence(other, ad.isOptional()); deps.add(d); } } --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/AnnotationParser.java Wed Oct 20 09:30:50 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/AnnotationParser.java Wed Oct 20 09:30:50 2010 @@ -227,11 +227,10 @@ CodeAttributeParser.setParseCodeAttribute(true); AnnotationParser.setParseAnnotation(true); - ClassPath.setJDKHome(jdkhome); - ClassPath.parseAllClassFiles(); + ClassPaths cpaths = ClassPaths.newJDKClassPaths(jdkhome); + cpaths.parse(); PrintWriter writer = new PrintWriter(new File(output, "jdk7.depconfig")); - try { for (Klass k : Klass.getAllClasses()) { for (AnnotatedDependency dep : k.getAnnotatedDeps()) { --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/BootAnalyzer.java Wed Oct 20 09:30:51 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/BootAnalyzer.java Wed Oct 20 09:30:51 2010 @@ -23,6 +23,7 @@ */ package com.sun.classanalyzer; +import java.util.Collections; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; @@ -43,6 +44,8 @@ import static com.sun.tools.classfile.ConstantPool.*; import com.sun.tools.classfile.Instruction.TypeKind; import com.sun.tools.classfile.Type.*; +import com.sun.classanalyzer.ModuleInfo.PackageInfo; +import static com.sun.classanalyzer.Trace.*; /** * Generate the module config for the boot module with @@ -79,11 +82,12 @@ * @author Mandy Chung */ public class BootAnalyzer { - + private static ClassPaths cpaths; public static void main(String[] args) throws Exception { String jdkhome = null; String config = null; String output = "."; + String version = "7-ea"; boolean printClassList = false; // process arguments @@ -96,6 +100,8 @@ } else { usage(); } + } else if (arg.equals("-version")) { + version = args[i++]; } else if (arg.equals("-config")) { config = args[i++]; } else if (arg.equals("-output")) { @@ -107,8 +113,6 @@ } } - - if (jdkhome == null || config == null) { usage(); } @@ -115,11 +119,11 @@ File jre = new File(jdkhome, "jre"); if (jre.exists()) { - ClassPath.setJDKHome(jdkhome); + cpaths = ClassPaths.newJDKClassPaths(jdkhome); } else { File classes = new File(jdkhome, "classes"); if (classes.exists()) { - ClassPath.setClassPath(classes.getCanonicalPath()); + cpaths = ClassPaths.newInstance(classes.getCanonicalPath()); } else { throw new RuntimeException("Invalid jdkhome: " + jdkhome); } @@ -143,19 +147,50 @@ String bootconfig = resolve(dir, bootmodule, "config"); printBootConfig(bootconfig, bootmodule); - List list = ModuleConfig.readConfigurationFile(bootconfig); - Module module = Module.addModule(list.get(0)); + ModuleBuilder builder = + new ModuleBuilder(Collections.singletonList(bootconfig), version); + + assert Module.getAllModules().size() == 1; + Module module = null; + for (Module m : Module.getAllModules()) { + module = m; + break; + } for (Klass k : Klass.getAllClasses()) { module.addKlass(k); } - module.fixupDependencies(); + builder.run(); if (printClassList) { - module.printClassListTo(resolve(dir, bootmodule, "classlist")); - module.printSummaryTo(resolve(dir, bootmodule, "summary")); + ClassListWriter writer = new ClassListWriter(dir, module); + writer.printClassList(); + writer.printResourceList(); + printModuleSummary(dir, module); } } + private static void printModuleSummary(File dir, Module m) throws IOException { + PrintWriter summary = + new PrintWriter(Files.resolve(dir, m.name(), "summary")); + try { + long total = 0L; + int count = 0; + summary.format("%10s\t%10s\t%s%n", "Bytes", "Classes", "Package name"); + for (PackageInfo info : m.getModuleInfo().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(); + } + } + // print boot.config file as an input to the ClassAnalyzer private static void printBootConfig(String output, String bootmodule) throws IOException { @@ -338,7 +373,7 @@ synchronized ClassFileParser getClassFileParser() throws IOException { if (parser == null) { - parser = ClassPath.parserForClass(classname); + parser = cpaths.parserForClass(classname); if (parser != null) { parseClassFile(); List descriptors = parse(new MethodDescriptor(classname + ".", "()V", false)); @@ -804,14 +839,7 @@ } }; } - static boolean traceOn = System.getProperty("classanalyzer.debug") != null; - private static void trace(String format, Object... args) { - if (traceOn) { - System.out.format(format, args); - } - } - private static void usage() { System.out.println("Usage: BootAnalyzer "); System.out.println("Options: "); --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/ClassAnalyzer.java Wed Oct 20 09:30:53 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ClassAnalyzer.java Wed Oct 20 09:30:52 2010 @@ -23,25 +23,36 @@ 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 com.sun.classanalyzer.Module.*; +import com.sun.classanalyzer.ModuleInfo.*; 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; +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 , + * .classlist + * .resources + * .summary + * .dependencies + * + * If -moduleinfo option is specified, /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 { @@ -48,15 +59,18 @@ public static void main(String[] args) throws Exception { String jdkhome = null; - String cpath = null; + String cparg = null; List configs = new ArrayList(); List depconfigs = new ArrayList(); - String output = "."; - String minfoPath = null; - String nonCorePkgs = null; - String properties = null; + 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; @@ -63,154 +77,309 @@ 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++); - } else if (arg.equals("-cpath")) { - cpath = 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++)); + configs.add(getOption(args, i++)); } else if (arg.equals("-depconfig")) { - depconfigs.add(getOption(args, i++)); + depconfigs.add(getOption(args, i++)); + } else if (arg.equals("-properties")) { + Module.setModuleProperties(getOption(args, i++)); } else if (arg.equals("-output")) { - output = getOption(args, i++); + classlistDir = getOption(args, i++); + } else if (arg.equals("-update")) { + update = true; } else if (arg.equals("-moduleinfo")) { - minfoPath = getOption(args, i++); - } else if (arg.equals("-base")) { - Module.setBaseModule(getOption(args, i++)); + minfoDir = 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++); + 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 { - System.err.println("Invalid option: " + arg); - usage(); + error("Invalid option: " + arg); } } - if ((jdkhome == null && cpath == null) || (jdkhome != null && cpath != null)) { - usage(); + if (jdkhome == null && cparg == null) { + error("-jdkhome and -classpath not set"); } + if (configs.isEmpty()) { - usage(); + error("-config not set"); } + if (version == null) { + error("-version not set"); + } + + ModuleBuilder builder; if (jdkhome != null) { - ClassPath.setJDKHome(jdkhome); - } else if (cpath != null) { - ClassPath.setClassPath(cpath); + PlatformModuleBuilder pmb = + new PlatformModuleBuilder(configs, depconfigs, mergeModules, version); + if (nonCorePkgsFile != null) { + pmb.readNonCorePackagesFrom(nonCorePkgsFile); + } + builder = pmb; + } else { + builder = new ModuleBuilder(configs, depconfigs, mergeModules, version); } - - if (nonCorePkgs != null) { - Platform.addNonCorePkgs(nonCorePkgs); + + 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 updatedModules; // updated modules - // create output directory if it doesn't exist - File dir = getDir(output); + 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(); + } - File moduleInfoSrc; - if (minfoPath == null) { - moduleInfoSrc = getDir(dir, "src"); + 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 { - moduleInfoSrc = getDir(minfoPath); + updatedModules.addAll(builder.getModules()); } + } - buildModules(configs, depconfigs, mergeModules); + public void generateReports(String output, boolean showDynamic) + throws IOException { + File outputDir = new File(output); + if (!outputDir.exists()) + Files.mkdirs(outputDir); - // 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); + if (updatedModules.size() > 0) { + printModulesList(); } - // Generate other summary reports + // 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); - - // 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]; + return args[index]; } else { - usage(); + usage(); } return null; } - static void buildModules(List configs, - List depconfigs, - boolean mergeModules) throws IOException { - List modules = new ArrayList(); - - // create modules based on the input config files - for (String file : configs) { - for (ModuleConfig mconfig : ModuleConfig.readConfigurationFile(file)) { - modules.add(Module.addModule(mconfig)); + 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(); } + } - // parse class files - ClassPath.parseAllClassFiles(); - - // Add additional dependencies if specified - if (depconfigs != null && depconfigs.size() > 0) { - DependencyConfig.parse(depconfigs); + 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; } - // process the roots and dependencies to get the classes for each module - for (Module m : modules) { - m.processRootsAndReferences(); - } + PrintWriter mlist = new PrintWriter(Files.resolve(dir, m.name(), "modules.list")); + try { + Set deps = m.getModuleInfo().dependences( + new Dependence.Filter() { - // update the dependencies for classes that were subsequently allocated - // to modules - for (Module m : modules) { - m.fixupDependencies(); + @Override + public boolean accept(Dependence d) { + return !d.isOptional(); + } + }); + for (Module dm : deps) { + mlist.format("%s\n", dm.name()); + } + } finally { + mlist.close(); } + } - if (mergeModules) { - Module.buildModuleMembers(); + private void printModuleGroup(Module group, PrintWriter writer) { + ModuleVisitor> visitor = new ModuleVisitor>() { + + public void preVisit(Module p, Set leafnodes) { + } + + public void visited(Module p, Module m, Set leafnodes) { + if (m.members().isEmpty()) { + leafnodes.add(m); + } + } + + public void postVisit(Module p, Set leafnodes) { + } + }; + + Set visited = new TreeSet(); + Set members = new TreeSet(); + 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(); + } - Platform.fixupPlatformModules(); + 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(); + } } - private static void printModulesSummary(File dir, boolean showDynamic) throws IOException { + 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 : Module.getTopLevelModules()) { - for (Dependency dep : m.dependences()) { - if (!showDynamic && dep.dynamic && !dep.optional) { - continue; - } - if (dep.module == null || !dep.module.isBase()) { - + for (Module m : builder.getModules()) { + ModuleInfo mi = m.getModuleInfo(); + for (Dependence dep : mi.requires()) { + if (!dep.getModule().isBase()) { String prefix = ""; - if (dep.optional) { + if (dep.isOptional()) { prefix = "[optional] "; - } else if (dep.dynamic) { - prefix = "[dynamic] "; } - Module other = dep != null ? dep.module : null; + Module other = dep.getModule(); writer.format("%s%s -> %s%n", prefix, m, other); } } @@ -220,25 +389,20 @@ } } - private static void printModulesDot(File dir, boolean showDynamic) throws IOException { + 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 : Module.getTopLevelModules()) { - for (Dependency dep : m.dependences()) { - if (!showDynamic && dep.dynamic && !dep.optional) { - continue; - } - if (dep.module == null || !dep.module.isBase()) { + 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.optional) { + if (dep.isOptional()) { 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) { @@ -246,7 +410,7 @@ } property = String.format(" [%s%s%s]", style, comma, color); } - Module other = dep != null ? dep.module : null; + Module other = dep.getModule(); writer.format(" \"%s\" -> \"%s\"%s;%n", m, other, property); } } @@ -257,105 +421,19 @@ } } - private static boolean isJdkTool(Module m) { - Set 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 allModules = new LinkedHashSet(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 { + private void printPackagesSummary(File dir) throws IOException { // print package / module relationship PrintWriter writer = new PrintWriter(new File(dir, "modules.pkginfo")); try { - Map> packages = new TreeMap>(); - Set splitPackages = new TreeSet(); - - for (Module m : Module.getTopLevelModules()) { - for (PackageInfo info : m.getPackageInfos()) { - Set value = packages.get(info.pkgName); - if (value == null) { - value = new TreeSet(); - 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) { + Map> splitPackages = builder.getSplitPackages(); + for (Map.Entry> e : splitPackages.entrySet()) { + String pkgname = e.getKey(); writer.format("%-60s", pkgname); - for (Module m : packages.get(pkgname)) { + for (Module m : e.getValue()) { writer.format(" %s", m); } writer.println(); @@ -362,7 +440,7 @@ } writer.println("\nPackage-private dependencies:-"); - for (String pkgname : splitPackages) { + for (String pkgname : splitPackages.keySet()) { for (Klass k : Klass.getAllClasses()) { if (k.getPackageName().equals(pkgname)) { Module m = k.getModule(); @@ -369,9 +447,9 @@ // 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)) { + 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()); } @@ -385,52 +463,29 @@ } - private static String resolve(File dir, String mname, String suffix) { - File f = new File(dir, mname + "." + suffix); - return f.toString(); - + private static void error(String msg) { + System.err.println("ERROR: " + msg); + System.out.println(usage()); + System.exit(-1); } - 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 String usage() { + StringBuilder sb = new StringBuilder(); + sb.append("Usage: ClassAnalyzer \n"); + sb.append("Options: \n"); + sb.append("\t-jdkhome where all jars will be parsed\n"); + sb.append("\t-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 \n"); + sb.append("\t This option can be repeated for multiple module config files\n"); + sb.append("\t-output \n"); + sb.append("\t-update update modules with newer files\n"); + sb.append("\t-moduleinfo \n"); + sb.append("\t-properties module's properties\n"); + sb.append("\t-noncorepkgs NON_CORE_PKGS.gmk\n"); + sb.append("\t-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(); } - - 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 "); - System.out.println("Options: "); - System.out.println("\t-jdkhome where all jars will be parsed"); - System.out.println("\t-cpath where classes and jars will be parsed"); - System.out.println("\t Either -jdkhome or -cpath option can be used."); - System.out.println("\t-config "); - System.out.println("\t This option can be repeated for multiple module config files"); - System.out.println("\t-output "); - System.out.println("\t-moduleinfo "); - System.out.println("\t-base "); - System.out.println("\t-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); - } } --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/ConstantPoolAnalyzer.java Wed Oct 20 09:30:54 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ConstantPoolAnalyzer.java Wed Oct 20 09:30:54 2010 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,8 +46,8 @@ if (jdkhome == null) { usage(); } - ClassPath.setJDKHome(jdkhome); - ClassPath.parseAllClassFiles(); + ClassPaths cpaths = ClassPaths.newJDKClassPaths(jdkhome); + cpaths.parse(); } private static void usage() { @@ -54,7 +54,6 @@ System.out.println("Usage: ConstantPoolAnalyzer "); System.out.println("Options: "); System.out.println("\t-jdkhome where all jars will be parsed"); - System.out.println("\t-cpath where classes and jars will be parsed"); System.exit(-1); } } --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/Klass.java Wed Oct 20 09:30:56 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/Klass.java Wed Oct 20 09:30:55 2010 @@ -21,6 +21,7 @@ * questions. * */ + package com.sun.classanalyzer; import java.io.File; @@ -34,6 +35,7 @@ import java.util.TreeSet; import com.sun.tools.classfile.AccessFlags; +import java.util.Collections; /** * @@ -40,17 +42,15 @@ * @author Mandy Chung */ public class Klass implements Comparable { - private final String classname; private final String packagename; private Module module; + private boolean isJavaLangObject; private String[] paths; private Map> methods; private AccessFlags accessFlags; private long filesize; - private boolean isJavaLangObject; - private boolean isPlatformAPI; - private boolean isNonCoreAPI; + private SortedMap> deps; private SortedMap> referrers; private List annotatedDeps; @@ -60,13 +60,12 @@ this.classname = classname; this.paths = classname.replace('.', '/').split("/"); this.isJavaLangObject = classname.equals("java.lang.Object"); - this.isPlatformAPI = Platform.isPlatformAPI(classname); - this.isNonCoreAPI = Platform.isNonCoreAPI(classname); this.deps = new TreeMap>(); this.referrers = new TreeMap>(); this.methods = new HashMap>(); this.annotatedDeps = new ArrayList(); this.classForNameRefs = new TreeSet(); + int pos = classname.lastIndexOf('.'); this.packagename = (pos > 0) ? classname.substring(0, pos) : ""; } @@ -96,14 +95,6 @@ return accessFlags == null || accessFlags.is(AccessFlags.ACC_PUBLIC); } - boolean isPlatformAPI() { - return isPlatformAPI; - } - - boolean isNonCoreAPI() { - return isNonCoreAPI; - } - Module getModule() { return module; } @@ -152,12 +143,10 @@ if (skip(ref)) { return; } - Set resInfos; - if (!deps.containsKey(ref)) { + Set resInfos = deps.get(ref); + if (resInfos == null) { resInfos = new TreeSet(); deps.put(ref, resInfos); - } else { - resInfos = deps.get(ref); } resInfos.add(ri); } @@ -170,12 +159,10 @@ if (skip(k)) { return; } - Set resInfos; - if (!referrers.containsKey(k)) { + Set resInfos = referrers.get(k); + if (resInfos == null) { resInfos = new TreeSet(); referrers.put(k, resInfos); - } else { - resInfos = referrers.get(k); } resInfos.add(ri); } @@ -185,10 +172,8 @@ } Method getMethod(String name, String signature) { - Set set; - if (methods.containsKey(name)) { - set = methods.get(name); - } else { + Set set = methods.get(name); + if (set == null) { set = new TreeSet(); methods.put(name, set); } @@ -224,10 +209,17 @@ List getAnnotatedDeps() { return annotatedDeps; } - private static Map classes = new TreeMap(); - static Set getAllClasses() { - return new TreeSet(classes.values()); + private static Map classes = new TreeMap(); + + // cache the sorted list of classes for performance + // No more class can be added after this point + private static List sortedClassList = null; + static synchronized Iterable getAllClasses() { + if (sortedClassList == null) { + sortedClassList = new ArrayList(classes.values()); + } + return Collections.unmodifiableCollection(sortedClassList); } static Klass findKlassFromPathname(String filename) { @@ -251,14 +243,15 @@ } static Klass getKlass(String name) { - Klass k; String classname = name.replace('/', '.'); if (classname.charAt(classname.length() - 1) == ';') { classname = classname.substring(0, classname.length() - 1); } - if (classes.containsKey(classname)) { - k = classes.get(classname); - } else { + Klass k = classes.get(classname); + if (k == null) { + if (sortedClassList != null) + throw new RuntimeException("new class is not expected to be added at this point"); + k = new Klass(classname); classes.put(classname, k); } --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/Module.java Wed Oct 20 09:30:57 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/Module.java Wed Oct 20 09:30:57 2010 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2010 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,12 +23,11 @@ */ package com.sun.classanalyzer; -import com.sun.classanalyzer.AnnotatedDependency.OptionalDependency; +import com.sun.classanalyzer.ModuleInfo.Dependence; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.Collection; import java.util.Collections; @@ -35,45 +34,32 @@ import java.util.Deque; import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; -import java.util.TreeMap; import java.util.TreeSet; -import static com.sun.classanalyzer.Platform.*; -import static com.sun.classanalyzer.Trace.*; /** + * Module contains a list of classes and resources. * * @author Mandy Chung */ public class Module implements Comparable { - private static final Map modules = new LinkedHashMap(); + private static final Map modules = + new LinkedHashMap(); - /** - * Returns the top-level modules that are defined in - * the input module config files. - * - */ - static Collection getTopLevelModules() { - Set result = new LinkedHashSet(); - // always put the boot module first and then the base - if (Platform.bootModule() != null) { - result.add(Platform.bootModule()); - } - result.add(findModule(baseModuleName)); + public static Collection getAllModules() { + return Collections.unmodifiableCollection(modules.values()); + } - for (Module m : modules.values()) { - if (m.isTopLevel()) { - result.add(m); - } + public static void addModule(Module m) { + String name = m.name(); + if (modules.containsKey(name)) { + throw new RuntimeException("module \"" + name + "\" already exists"); } - return Collections.unmodifiableCollection(result); + modules.put(name, m); } public static Module addModule(ModuleConfig config) { @@ -81,13 +67,8 @@ if (modules.containsKey(name)) { throw new RuntimeException("module \"" + name + "\" already exists"); } - Module m; - if (Platform.isBootModule(config.module)) { - m = Platform.createBootModule(config); - } else { - m = new Module(config); - } - modules.put(name, m); + Module m = new Module(config); + addModule(m); return m; } @@ -94,17 +75,19 @@ public static Module findModule(String name) { return modules.get(name); } - private static String baseModuleName = "base"; - private static String version = "7-ea"; + private static String baseModuleName = "base"; static void setBaseModule(String name) { + if (name == null || name.isEmpty()) { + throw new RuntimeException("Null or empty base module"); + } baseModuleName = name; } - static void setVersion(String ver) { - version = ver; - } private static Properties moduleProps = new Properties(); + static String getModuleProperty(String key) { + return moduleProps.getProperty(key); + } static void setModuleProperties(String file) throws IOException { File f = new File(file); @@ -123,15 +106,11 @@ private final Set classes; private final Set resources; private final Set unresolved; - private final Map packages; - private final Set dependents; private final Set members; - private final Set requires; // update during the analysis - private Set permits; private Module group; + private ModuleInfo minfo; private boolean isBaseModule; - private int platformApiCount; protected Module(ModuleConfig config) { this.name = config.module; @@ -140,12 +119,8 @@ this.resources = new TreeSet(); this.config = config; this.unresolved = new HashSet(); - this.dependents = new TreeSet(); - this.packages = new TreeMap(); this.members = new TreeSet(); - this.requires = new TreeSet(config.requires()); this.group = this; // initialize to itself - this.platformApiCount = 0; } String name() { @@ -152,6 +127,10 @@ return name; } + ModuleConfig config() { + return config; + } + Module group() { return group; } @@ -160,42 +139,26 @@ return isBaseModule; } - // requires local for JRE modules that are strongly - // connected with the boot module - boolean isBootConnected() { - for (RequiresModule rm : requires) { - if (Platform.isBootModule(rm.modulename)) { - return true; - } - } - return false; + Set classes() { + return Collections.unmodifiableSet(classes); } - private Module moduleForRequires; - synchronized Module toRequiredModule() { - if (moduleForRequires == null) { - // create a module for external requires if needed - moduleForRequires = Platform.toRequiresModule(this); - } - return moduleForRequires; + Set resources() { + return Collections.unmodifiableSet(resources); } Set members() { - return members; + return Collections.unmodifiableSet(members); } - boolean hasPlatformAPIs() { - return platformApiCount > 0; - } - boolean contains(Klass k) { return k != null && classes.contains(k); } boolean isEmpty() { - return classes.isEmpty() && - resources.isEmpty() && - mainClass() == null; + return classes.isEmpty() + && resources.isEmpty() + && mainClass() == null; } boolean allowEmpty() { @@ -202,84 +165,17 @@ return moduleProps.getProperty(name + ".allow.empty") != null; } - Module alias() { - String mn = moduleProps.getProperty(name + ".alias"); - Module m = this; - if (mn != null) { - m = findModule(mn); - if (m == null) { - throw new RuntimeException(name + ".alias = " + mn + " not found"); - } - } - return m; + // returns itself. + public Module exporter(Module from) { + return this; } protected boolean isTopLevel() { // module with no class is not included except the base module - return this.group == this && - (isBase() || !isEmpty() || isAggregator() || allowEmpty()); + return this.group == this + && (isBase() || !isEmpty() || !config.requires().isEmpty() || allowEmpty()); } - boolean isAggregator() { - // a module is an aggregator if it has no class and resource and no main class - // but has a list of requires. - if (isEmpty() && requires.size() > 0) { - // return false if it requires only jdk.boot - if (requires.size() == 1) { - for (RequiresModule rm : requires) { - if (Platform.isBootModule(rm.modulename)) { - return false; - } - } - } - return true; - } - - return false; - } - - // fixup permits and requires set after modules are merged - void fixupModuleInfo() { - Set newPermits = new TreeSet(); - for (Module m : permits()) { - // in case multiple permits from the same group - newPermits.add(m.group()); - } - permits.clear(); - permits.addAll(newPermits); - - // fixup requires set - Set newRequires = new TreeSet(); - for (RequiresModule rm : requires) { - Module req = rm.module(); - if (req.isEmpty() && !req.isAggregator()) { - // remove from requires set if empty and not a module aggregator - continue; - } - - newRequires.add(rm); - if (req.requirePermits()) { - req.permits().add(this.group()); - } - } - requires.clear(); - requires.addAll(newRequires); - - // add this to the permits set of its dependences if needed - for (Dependency d : dependences()) { - if (d.dynamic && !d.optional) { - // ignore dynamic dependencies for now - continue; - } - - // add permits for all local dependencies - Module dm = d.module(); - if (dm.requirePermits()) { - dm.permits().add(this.group()); - } - } - } - Klass mainClass() { String cls = config.mainClass(); if (cls == null) { @@ -290,73 +186,6 @@ return k; } - synchronized Set permits() { - if (permits == null) { - this.permits = new TreeSet(); - // initialize the permits set - for (String s : config.permits()) { - Module m = findModule(s); - if (m != null) { - permits.add(m.group()); - } else { - throw new RuntimeException("module " + s + - " specified in the permits rule for " + name + " doesn't exist"); - } - } - } - return permits; - } - - Set requires() { - return requires; - } - - Collection dependents() { - Map deps = new LinkedHashMap(); - for (Dependency dep : dependents) { - Dependency d = deps.get(dep.module()); - if (d == null || dep.compareTo(d) > 0) { - deps.put(dep.module(), dep); - } - } - return deps.values(); - } - - boolean requires(Module m) { - for (RequiresModule rm : requires()) { - if (rm.module() == m) - return true; - } - return false; - } - /** - * Returns a Collection of Dependency, only one for each dependent - * module of the strongest dependency (i.e. - * hard static > hard dynamic > optional static > optional dynamic - */ - Collection dependences() { - Set result = new TreeSet(); - for (Dependency d : dependents()) { - Module dm = d.module(); - Module rm = dm; - if (!dm.alias().requires(this)) { - // use alias as the dependence except this module - // is required by the alias that will result in - // a recursive dependence. - rm = dm.alias(); - } - if (!isBootConnected()) { - // If it's a local module requiring jdk.boot, retain - // the original requires; otherwise, use its external - // module - rm = rm.toRequiredModule(); - } - - result.add(new Dependency(rm, d.optional, d.dynamic)); - } - return result; - } - @Override public int compareTo(Module o) { if (o == null) { @@ -373,22 +202,6 @@ void addKlass(Klass k) { classes.add(k); k.setModule(this); - if (k.isPlatformAPI()) { - platformApiCount++; - } - - // update package statistics - String pkg = k.getPackageName(); - PackageInfo pkginfo = packages.get(pkg); - if (pkginfo == null) { - pkginfo = new PackageInfo(pkg); - packages.put(pkg, pkginfo); - } - - if (k.exists()) { - // only count the class that is parsed - pkginfo.add(k.getFileSize()); - } } void addResource(ResourceFile res) { @@ -422,7 +235,6 @@ Module otherModule = other.getModule(); if (otherModule != null && otherModule != this) { // this module is dependent on otherModule - addDependency(k, other); continue; } @@ -448,11 +260,6 @@ Module otherModule = other.getModule(); if (otherModule == null) { unresolved.add(new Reference(c, other)); - } else { - if (otherModule != this) { - // this module is dependent on otherModule - addDependency(c, other); - } } } } @@ -470,50 +277,6 @@ } } - void addDependency(Klass from, Klass to) { - Dependency dep = new Dependency(from, to); - dependents.add(dep); - } - - void addRequiresModule(Module m) { - addRequiresModule(m, false); - } - - void addRequiresModule(Module m, boolean optional) { - requires.add(new RequiresModule(m, optional)); - if (m.requirePermits()) { - m.permits().add(this); - } - } - - boolean requirePermits() { - return (name().startsWith("sun.") || - permits().size() > 0); - } - - void fixupDependencies() { - // update dependencies for classes that were allocated to modules after - // this module was processed. - for (Reference ref : unresolved) { - Module m = ref.referree().getModule(); - if (m == null || m != this) { - addDependency(ref.referrer, ref.referree); - } - } - - // add dependency due to the main class - Klass k = mainClass(); - if (k != null) { - dependents.add(new Dependency(k.getModule(), false, false)); - } - fixupAnnotatedDependencies(); - } - - private void fixupAnnotatedDependencies() { - // add dependencies that this klass may depend on due to the AnnotatedDependency - dependents.addAll(AnnotatedDependency.getDependencies(this)); - } - boolean isModuleDependence(Klass k) { Module m = k.getModule(); return m == null || (!classes.contains(k) && !m.isBase()); @@ -533,12 +296,12 @@ return null; } -

void visitMember(Set visited, Visitor

visitor, P p) { +

void visitMembers(Set visited, ModuleVisitor

visitor, P p) { if (!visited.contains(this)) { visited.add(this); visitor.preVisit(this, p); for (Module m : members) { - m.visitMember(visited, visitor, p); + m.visitMembers(visited, visitor, p); visitor.visited(this, m, p); } visitor.postVisit(this, p); @@ -547,38 +310,6 @@ } } - private Set getDepModules() { - Set deps = new TreeSet(); - for (Dependency d : dependences()) { - if (d.dynamic || d.optional) { - // ignore dynamic or optional dependencies for now - continue; - } - deps.add(d.module()); - } - for (RequiresModule req : requires) { - if (req.optional) { - // ignore optional dependencies for now - continue; - } - deps.add(req.module()); - } - return deps; - } - -

void visitDependence(Set visited, Visitor

visitor, P p) { - if (!visited.contains(this)) { - visited.add(this); - - visitor.preVisit(this, p); - for (Module m : getDepModules()) { - m.visitDependence(visited, visitor, p); - visitor.visited(this, m, p); - } - visitor.postVisit(this, p); - } - } - void addMember(Module m) { // merge class list for (Klass k : m.classes) { @@ -589,24 +320,6 @@ for (ResourceFile res : m.resources) { resources.add(res); } - - platformApiCount += m.platformApiCount; - - // merge the package statistics - for (PackageInfo pinfo : m.getPackageInfos()) { - String packageName = pinfo.pkgName; - PackageInfo pkginfo = packages.get(packageName); - if (pkginfo == null) { - pkginfo = new PackageInfo(packageName); - packages.put(packageName, pkginfo); - } - - pkginfo.add(pinfo); - } - - // merge all permits and requires set - permits().addAll(m.permits()); - requires().addAll(m.requires()); } static void buildModuleMembers() { @@ -623,7 +336,7 @@ } // set up the top-level module - Visitor groupSetter = new Visitor() { + ModuleVisitor groupSetter = new ModuleVisitor() { public void preVisit(Module m, Module p) { m.group = p; @@ -646,12 +359,12 @@ for (Module p : modules.values()) { for (Module m : p.members) { if (m.group == m) { - m.visitMember(new TreeSet(), groupSetter, p); + m.visitMembers(new TreeSet(), groupSetter, p); } } } - Visitor mergeClassList = new Visitor() { + ModuleVisitor mergeClassList = new ModuleVisitor() { public void preVisit(Module m, Module p) { // nop - depth-first search @@ -672,518 +385,42 @@ groups.add(m); if (m.members().size() > 0) { // merge class list from all its members - m.visitMember(visited, mergeClassList, m); + m.visitMembers(visited, mergeClassList, m); } - - // clear the dependencies before fixup - m.dependents.clear(); - - // fixup dependencies - for (Klass k : m.classes) { - for (Klass other : k.getReferencedClasses()) { - if (m.isModuleDependence(other)) { - // this module is dependent on otherModule - m.addDependency(k, other); - } - } - } - - // add dependency due to the main class - Klass k = m.mainClass(); - if (k != null && m.isModuleDependence(k)) { - m.dependents.add(new Dependency(k.getModule().group(), false, false)); - } - - // add dependencies that this klass may depend on due to the AnnotatedDependency - m.fixupAnnotatedDependencies(); } } } - Set orderedDependencies() { - Visitor> walker = new Visitor>() { - public void preVisit(Module m, Set result) { - // nop - depth-first search - } - - public void visited(Module m, Module child, Set result) { - } - - public void postVisit(Module m, Set result) { - result.add(m); - } - }; - - Set visited = new TreeSet(); - Set result = new LinkedHashSet(); - - visitDependence(visited, walker, result); - return result; + ModuleInfo getModuleInfo() { + return minfo; } - class PackageInfo implements Comparable { - - final String pkgName; - int count; - long filesize; - - PackageInfo(String name) { - this.pkgName = name; - this.count = 0; - this.filesize = 0; - } - - void add(PackageInfo pkg) { - this.count += pkg.count; - this.filesize += pkg.filesize; - } - - void add(long size) { - count++; - filesize += size; - - } - - @Override - public int compareTo(Object o) { - return pkgName.compareTo(((PackageInfo) o).pkgName); - } + void setModuleInfo(ModuleInfo mi) { + if (minfo != null) + throw new AssertionError("ModuleInfo already created for " + name); + minfo = mi; } - Set getPackageInfos() { - return new TreeSet(packages.values()); - } + public interface Visitor { - void printSummaryTo(String output) throws IOException { - PrintWriter writer = new PrintWriter(output); - try { - long total = 0L; - int count = 0; - int nonCoreAPIs = 0; - writer.format("%10s\t%10s\t%s%n", "Bytes", "Classes", "Package name"); - for (String pkg : packages.keySet()) { - PackageInfo info = packages.get(pkg); - if (info.count > 0) { - if (Platform.isNonCoreAPI(pkg)) { - nonCoreAPIs += info.count; - writer.format("%10d\t%10d\t%s (*)%n", - info.filesize, info.count, pkg); - } else { - writer.format("%10d\t%10d\t%s%n", - info.filesize, info.count, pkg); - } - total += info.filesize; - count += info.count; - } - } + R visitClass(Klass k, P p); - - writer.format("%nTotal: %d bytes (uncompressed) %d classes%n", - total, count); - writer.format("APIs: %d core %d non-core (*)%n", - platformApiCount, nonCoreAPIs); - } finally { - writer.close(); - } - + R visitResource(ResourceFile r, P p); } - void printClassListTo(String output) throws IOException { - if (classes.isEmpty()) { - return; + public void visit(Visitor visitor, P p) { + for (Klass c : classes) { + visitor.visitClass(c, p); } - - PrintWriter writer = new PrintWriter(output); - try { - for (Klass c : classes) { - if (c.exists()) { - writer.format("%s\n", c.getClassFilePathname()); - } else { - trace("%s in module %s missing\n", c, this); - } - } - - } finally { - writer.close(); + for (ResourceFile res : resources) { + visitor.visitResource(res, p); } - } - void printResourceListTo(String output) throws IOException { - // no file created if the module doesn't have any resource file - if (resources.isEmpty()) { - return; - } - - PrintWriter writer = new PrintWriter(output); - try { - for (ResourceFile res : resources) { - writer.format("%s\n", res.getPathname()); - } - - } finally { - writer.close(); - } - - } - - void printDependenciesTo(String output, boolean showDynamic) throws IOException { - PrintWriter writer = new PrintWriter(output); - try { - // classes that this klass may depend on due to the AnnotatedDependency - Map> annotatedDeps = AnnotatedDependency.getReferences(this); - - for (Klass klass : classes) { - Set references = klass.getReferencedClasses(); - for (Klass other : references) { - String classname = klass.getClassName(); - boolean optional = OptionalDependency.isOptional(klass, other); - if (optional) { - classname = "[optional] " + classname; - } - - Module m = getModuleDependence(other); - if (m != null || other.getModule() == null) { - writer.format("%-40s -> %s (%s)", classname, other, m); - Reference ref = new Reference(klass, other); - if (annotatedDeps.containsKey(ref)) { - for (AnnotatedDependency ad : annotatedDeps.get(ref)) { - writer.format(" %s", ad.getTag()); - } - // printed; so remove the dependency from the annotated deps list - annotatedDeps.remove(ref); - } - writer.format("\n"); - } - } - } - - // print remaining dependencies specified in AnnotatedDependency list - if (annotatedDeps.size() > 0) { - for (Map.Entry> entry : annotatedDeps.entrySet()) { - Reference ref = entry.getKey(); - Module m = getModuleDependence(ref.referree); - if (m != null || ref.referree.getModule() == null) { - String classname = ref.referrer.getClassName(); - boolean optional = true; - boolean dynamic = true; - String tag = ""; - for (AnnotatedDependency ad : entry.getValue()) { - if (optional && !ad.isOptional()) { - optional = false; - tag = ad.getTag(); - } - - if (!ad.isDynamic()) { - dynamic = false; - } - } - if (!showDynamic && optional && dynamic) { - continue; - } - - if (optional) { - classname = "[optional] " + classname; - } else if (dynamic) { - classname = "[dynamic] " + classname; - } - writer.format("%-40s -> %s (%s) %s%n", classname, ref.referree, m, tag); - } - } - } - } finally { - writer.close(); - } - - } - - // print module dependency list - void printDepModuleListTo(String output) throws IOException { - PrintWriter writer = new PrintWriter(output); - try { - for (Module m : orderedDependencies()) { - writer.format("%s\n", m.name()); - } - if (Platform.legacyModule() != null && - (this == Platform.jdkBaseModule() || - this == Platform.jdkModule() || - this == Platform.jreModule())) { - // add legacy module in the modules.list - // so that it will install legacy module as well. - writer.format("%s\n", Platform.legacyModule()); - } - } finally { - writer.close(); - } - } - - void printModuleInfoTo(String output) throws IOException { - PrintWriter writer = new PrintWriter(output); - try { - writer.format("module %s @ %s {%n", name, version); - String formatSep = " requires"; - Map reqs = new TreeMap(); - for (RequiresModule rm : requires()) { - reqs.put(rm.module().name(), rm); - } - - for (Dependency dep : dependences()) { - Module dm = dep.module(); - if (!isBootConnected()) { - // If it's a local module requiring jdk.boot, retain - // the original requires - dm = dm.toRequiredModule(); - } - - if (dm == null) { - System.err.format("WARNING: module %s has a dependency on null module%n", name); - } - - StringBuilder attributes = new StringBuilder(); - RequiresModule rm = reqs.get(dm.name()); - - if (rm != null && rm.reexport) { - attributes.append(" public"); - } - - if (isBootConnected() || (rm != null && rm.local)) { - attributes.append(" local"); - } - - if (dep.optional || (rm != null && rm.optional)) { - attributes.append(" optional"); - } - - // FIXME: ignore dynamic dependencies - // Filter out optional dependencies for the boot module - // which are addded in the jdk.base module instead - if (!dep.dynamic || dep.optional) { - reqs.remove(dm.name()); - writer.format("%s%s %s @ %s;%n", - formatSep, - attributes.toString(), - dep != null ? dm : "null", version); - } - - } - // additional requires - if (reqs.size() > 0) { - for (RequiresModule rm : reqs.values()) { - StringBuilder attributes = new StringBuilder(); - if (rm.reexport) { - attributes.append(" public"); - } - if (rm.optional) { - attributes.append(" optional"); - } - if (isBootConnected() || rm.local) { - attributes.append(" local"); - } - - writer.format("%s%s %s @ %s;%n", formatSep, attributes.toString(), rm.module(), version); - } - } - - // permits - if (permits().size() > 0) { - formatSep = " permits"; - for (Module p : permits()) { - writer.format("%s %s", formatSep, p); - formatSep = ","; - } - writer.format(";%n"); - } - if (mainClass() != null) { - writer.format(" class %s;%n", mainClass().getClassName()); - } - writer.format("}%n"); - } finally { - writer.close(); - } - } - - static class Dependency implements Comparable { - - protected Module module; - final boolean optional; - final boolean dynamic; - - Dependency(Klass from, Klass to) { - // static dependency - this.module = to.getModule() != null ? to.getModule().group() : null; - this.optional = OptionalDependency.isOptional(from, to); - this.dynamic = false; - } - - Dependency(Module m, boolean optional, boolean dynamic) { - this.module = m != null ? m.group() : null; - this.optional = optional; - this.dynamic = dynamic; - } - - Module module() { - return module; - } - - public boolean isLocal(Module from) { - if (module().isBootConnected()) { - // local requires if the requesting module is the boot module - // or it's an aggregate platform module - return true; - } - - for (PackageInfo pkg : from.getPackageInfos()) { - // local dependence if any package this module owns is splitted - // across its dependence - for (PackageInfo p : module().getPackageInfos()) { - if (pkg.pkgName.equals(p.pkgName)) { - return true; - } - } - } - return false; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Dependency)) { - return false; - } - if (this == obj) { - return true; - } - - Dependency d = (Dependency) obj; - if (this.module() != d.module()) { - return false; - } else { - return this.optional == d.optional && this.dynamic == d.dynamic; - } - } - - @Override - public int hashCode() { - int hash = 3; - hash = 19 * hash + (this.module() != null ? this.module().hashCode() : 0); - hash = 19 * hash + (this.optional ? 1 : 0); - hash = 19 * hash + (this.dynamic ? 1 : 0); - return hash; - } - - @Override - public int compareTo(Dependency d) { - if (this.equals(d)) { - return 0; - } - - // Hard static > hard dynamic > optional static > optional dynamic - if (this.module() == d.module()) { - if (this.optional == d.optional) { - return this.dynamic ? -1 : 1; - } else { - return this.optional ? -1 : 1; - } - } else if (this.module() != null && d.module() != null) { - return (this.module().compareTo(d.module())); - } else { - return (this.module() == null) ? -1 : 1; - } - } - - @Override - public String toString() { - String s = module().name(); - if (optional) { - s += " (optional)"; - } else if (dynamic) { - s += " (dynamic)"; - } - return s; - } - } - - static class RequiresModule extends Dependency { - - final String modulename; - final boolean reexport; - final boolean local; - - public RequiresModule(String name, boolean optional, boolean reexport, boolean local) { - super(null, optional, false /* dynamic */); - this.modulename = name; - this.reexport = reexport; - this.local = local; - } - - public RequiresModule(Module m, boolean optional) { - super(m, optional, false); - this.modulename = m.name(); - this.reexport = true; - this.local = false; - } - - // deferred initialization until it's called. - // must call after all modules are merged. - synchronized Module fixupModule() { - if (module == null) { - Module m = findModule(modulename); - if (m == null) { - throw new RuntimeException("Required module \"" + modulename + "\" doesn't exist"); - } - module = m.group(); - } - return module; - } - - @Override - Module module() { - return fixupModule(); - } - - @Override - public int compareTo(Dependency d) { - RequiresModule rm = (RequiresModule) d; - if (this.equals(rm)) { - return 0; - } - return modulename.compareTo(rm.modulename); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof RequiresModule)) { - return false; - } - if (this == obj) { - return true; - } - - RequiresModule d = (RequiresModule) obj; - return this.modulename.equals(d.modulename); - } - - @Override - public int hashCode() { - int hash = 3; - hash = 19 * hash + this.modulename.hashCode(); - return hash; - } - - @Override - public String toString() { - String s = reexport ? "public " : ""; - if (optional) { - s += "optional "; - } - s += modulename; - return s; - } - } - static class Reference implements Comparable { - private final Klass referrer, referree; + final Klass referrer, referree; Reference(Klass referrer, Klass referree) { this.referrer = referrer; @@ -1226,7 +463,7 @@ } } - interface Visitor

{ + interface ModuleVisitor

{ public void preVisit(Module m, P param); --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/ModuleConfig.java Wed Oct 20 09:30:58 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ModuleConfig.java Wed Oct 20 09:30:58 2010 @@ -33,10 +33,10 @@ import java.util.Set; import java.util.TreeSet; import java.util.regex.Pattern; - -import com.sun.classanalyzer.Module.RequiresModule; +import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.Map; +import com.sun.classanalyzer.ModuleInfo.Dependence; /** * @@ -45,9 +45,9 @@ public class ModuleConfig { private final Set roots; - private final Set includes; - private final Set permits; - private final Map requires; + protected final Set includes; + protected final Set permits; + protected final Map requires; private final Filter filter; private List members; private String mainClass; @@ -62,7 +62,7 @@ this.roots = new TreeSet(); this.includes = new TreeSet(); this.permits = new TreeSet(); - this.requires = new LinkedHashMap(); + this.requires = new LinkedHashMap(); this.filter = new Filter(this); this.mainClass = mainClass; } @@ -85,10 +85,24 @@ return permits; } - Collection requires() { + Collection requires() { return requires.values(); } + void export(Module m) { + Dependence d = requires.get(m.name()); + if (d == null) { + d = new Dependence(m, EnumSet.of(Dependence.Modifier.PUBLIC)); + } else if (!d.isPublic()){ + throw new RuntimeException(module + " should require public " + m.name()); + } + requires.put(m.name(), d); + } + + void addPermit(Module m) { + permits.add(m.name()); + } + String mainClass() { return mainClass; } @@ -564,8 +578,8 @@ throw new RuntimeException(file + ", line " + lineNumber + " requires: \"" + s + "\" must be local"); } - RequiresModule rm = new RequiresModule(s, optional, reexport, local); - config.requires.put(s, rm); + Dependence d = new Dependence(s, optional, reexport, local); + config.requires.put(s, d); } } } @@ -628,7 +642,7 @@ sb.append(format("allow", filter.allow)); sb.append(format("exclude", filter.exclude)); Set reqs = new TreeSet(); - for (RequiresModule rm : requires.values()) { + for (Dependence rm : requires.values()) { reqs.add(rm.toString()); } sb.append(format("requires", reqs)); --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/ResourceFile.java Wed Oct 20 09:31:00 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ResourceFile.java Wed Oct 20 09:31:00 2010 @@ -39,12 +39,13 @@ * @author Mandy Chung */ public class ResourceFile implements Comparable { - private final String pathname; + protected final String name; private Module module; - ResourceFile(String pathname) { - this.pathname = pathname.replace(File.separatorChar, '/'); + ResourceFile(String fname) { + this.pathname = fname.replace('/', File.separatorChar); + this.name = fname.replace(File.separatorChar, '/'); } Module getModule() { @@ -59,7 +60,7 @@ } String getName() { - return pathname; + return name; } String getPathname() { @@ -68,18 +69,18 @@ @Override public String toString() { - return pathname; + return name; } @Override public int compareTo(ResourceFile o) { - return pathname.compareTo(o.pathname); + return name.compareTo(o.name); } + static Set resources = new TreeSet(); static boolean isResource(String pathname) { String name = pathname.replace(File.separatorChar, '/'); - if (name.endsWith("META-INF/MANIFEST.MF")) { return false; } @@ -90,15 +91,16 @@ return true; } - static void addResource(String name, InputStream in) { + static ResourceFile addResource(String fname, InputStream in) { ResourceFile res; - name = name.replace(File.separatorChar, '/'); - if (name.startsWith("META-INF/services")) { - res = new ServiceProviderConfigFile(name, in); + fname = fname.replace(File.separatorChar, '/'); + if (fname.startsWith("META-INF/services")) { + res = new ServiceProviderConfigFile(fname, in); } else { - res = new ResourceFile(name); + res = new ResourceFile(fname); } resources.add(res); + return res; } static Set getAllResources() { @@ -106,13 +108,12 @@ } static class ServiceProviderConfigFile extends ResourceFile { - - private final List providers = new ArrayList(); - private final String service; - ServiceProviderConfigFile(String pathname, InputStream in) { - super(pathname); + final List providers = new ArrayList(); + final String service; + ServiceProviderConfigFile(String fname, InputStream in) { + super(fname); readServiceConfiguration(in, providers); - this.service = pathname.substring("META-INF/services".length() + 1, pathname.length()); + this.service = name.substring("META-INF/services".length() + 1, name.length()); } @Override @@ -124,6 +125,40 @@ return providers.get(0); } } + + @Override + public boolean equals(Object o) { + if (o instanceof ServiceProviderConfigFile) { + ServiceProviderConfigFile sp = (ServiceProviderConfigFile) o; + if (service.equals(sp.service) && providers.size() == sp.providers.size()) { + List tmp = new ArrayList(providers); + if (tmp.removeAll(sp.providers)) { + return tmp.size() == 0; + } + } + } + return false; + } + + public int hashCode() { + int hash = 7; + hash = 73 * hash + (this.providers != null ? this.providers.hashCode() : 0); + hash = 73 * hash + (this.service != null ? this.service.hashCode() : 0); + return hash; + } + + @Override + public int compareTo(ResourceFile o) { + if (this.equals(o)) { + return 0; + } else { + if (getName().compareTo(o.getName()) < 0) { + return -1; + } else { + return 1; + } + } + } @SuppressWarnings("empty-statement") void readServiceConfiguration(InputStream in, List names) { --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/ShowDeps.java Wed Oct 20 09:31:01 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ShowDeps.java Wed Oct 20 09:31:01 2010 @@ -22,8 +22,7 @@ */ package com.sun.classanalyzer; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.util.*; /** @@ -32,9 +31,39 @@ * ignore references to classes listed in the file (including .classlists * created by the ClassAnalyzer tool). */ - public class ShowDeps { + private final ClassPaths cpaths; + private final Set classes = new TreeSet(); + + public ShowDeps(ClassPaths cpaths) { + this.cpaths = cpaths; + } + + public void run() throws IOException { + cpaths.parse(); + + // find the classes that don't exist + Set unresolved = new TreeSet(); + for (Klass k : Klass.getAllClasses()) { + if (k.getFileSize() == 0) { + unresolved.add(k); + } + } + + // print references to classes that don't exist + for (Klass k : Klass.getAllClasses()) { + for (Klass other : k.getReferencedClasses()) { + if (unresolved.contains(other)) { + String name = other.toString(); + if (!ignore.contains(name)) { + System.out.format("%s -> %s\n", k, other); + } + } + } + } + } + static void usage() { System.out.println("java ShowDeps [-ignore ] file..."); System.out.println(" where is a class or JAR file, or a directory"); @@ -43,14 +72,14 @@ System.out.println(" java ShowDeps Foo.jar"); System.out.println(" java ShowDeps -ignore base.classlist Foo.jar"); System.out.println(" java ShowDeps -ignore base.classlist -ignore " + - "jaxp-parsers.classlist

"); + "jaxp-parsers.classlist "); System.exit(-1); } + private static Set ignore = new HashSet(); public static void main(String[] args) throws IOException { // process -ignore options int argi = 0; - Set ignore = new HashSet(); while (argi < args.length && args[argi].equals("-ignore")) { argi++; Scanner s = new Scanner(new File(args[argi++])); @@ -57,12 +86,13 @@ try { while (s.hasNextLine()) { String line = s.nextLine(); - if (!line.endsWith(".class")) + if (!line.endsWith(".class")) { continue; + } int len = line.length(); // convert to class names String clazz = line.replace('\\', '.').replace('/', '.') - .substring(0, len-6); + .substring(0, len - 6); ignore.add(clazz); } } finally { @@ -70,31 +100,13 @@ } } - if (argi >= args.length) + if (argi >= args.length) { usage(); - - // parse all classes - while (argi < args.length) - ClassPath.setClassPath(args[argi++]); - ClassPath.parseAllClassFiles(); - - // find the classes that don't exist - Set unresolved = new TreeSet(); - for (Klass k : Klass.getAllClasses()) { - if (k.getFileSize() == 0) - unresolved.add(k); } - // print references to classes that don't exist - for (Klass k: Klass.getAllClasses()) { - for (Klass other : k.getReferencedClasses()) { - if (unresolved.contains(other)) { - String name = other.toString(); - if (!ignore.contains(name)) { - System.out.format("%s -> %s\n", k, other); - } - } - } - } + // parse all classes + ClassPaths cpaths = new ClassPaths(Arrays.copyOfRange(args, argi, args.length)); + ShowDeps instance = new ShowDeps(cpaths); + instance.run(); } } --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/Trace.java Wed Oct 20 09:31:03 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/Trace.java Wed Oct 20 09:31:02 2010 @@ -28,7 +28,11 @@ * @author Mandy Chung */ public class Trace { - public static boolean traceOn = System.getProperty("classanalyzer.debug") != null; + // Turn on tracing by setting a system property "classanalyzer.debug" + // or an environment variable "CLASS_ANALYZER_DEBUG" + public static boolean traceOn = + System.getProperty("classanalyzer.debug") != null || + System.getenv("CLASS_ANALYZER_DEBUG") != null; public static void trace(String format, Object... params) { if (traceOn) { --- old/make/common/BuildPackages.gmk Wed Oct 20 09:31:04 2010 +++ /dev/null Wed Oct 20 09:31:04 2010 @@ -1,102 +0,0 @@ -# -# Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Sun designates this -# particular file as subject to the "Classpath" exception as provided -# by Sun in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, -# CA 95054 USA or visit www.sun.com if you need additional information or -# have any questions. -# - -# -# Generate jmod and debian packages for all jdk modules -# -PKGS = $(ABS_OUTPUTDIR)/jigsaw-pkgs -JMOD_PKGS = $(PKGS)/jmod -DEB_PKGS = $(PKGS)/deb - -## TODO: generate packages for modules that are modified -## instead of all modules -$(PKGS)/%: - $(RM) -r $@ - $(MKDIR) -p $@ - -# Prepare to sign modules -ifdef BUILD_SIGNED_MODULES - -include $(BUILDDIR)/javax/crypto/Defs-jce.gmk -SIGNER_ARGS = --sign --keystore $(SIGNING_KEYSTORE) --signer $(SIGNING_ALIAS) -SIGNER_PW = "< $(SIGNING_PASSPHRASE)" - -else #BUILD_SIGNED_MODULES - -SIGNER_ARGS = -SIGNER_PW = - -endif #BUILD_SIGNED_MODULES - -# Rules to generate jmod packages -# -jmod-pkgs: $(JMOD_PKGS) - @$(ECHO) ">>>Making "$@" @ `$(DATE)` ..." - for m in `$(NAWK) '{print $$1}' $(MODULES_LIST)` ; do \ - $(CD) $(ABS_MODULES_DIR)/$$m && \ - (if [ -d resources ] ; then \ - RES="-r resources" ; \ - fi ; \ - if [ -d bin ] ; then \ - NATCMD="-natcmd bin" ; \ - fi ; \ - if [ -d lib ] ; then \ - NATLIB="-natlib lib" ; \ - fi ; \ - if [ -d etc ] ; then \ - CONFIG="-config etc" ; \ - fi ; \ - $(HOST_JPKG_CMD) -v -m classes \ - $$RES $$NATCMD $$NATLIB $$CONFIG $(SIGNER_ARGS) \ - -d $(JMOD_PKGS) jmod $$m $(SIGNER_PW)) || exit 1 ; \ - done - @$(ECHO) ">>>Finished making "$@" @ `$(DATE)` ..." - -# Rules to generate debian packages -# -JIGSAW_IMAGE = $(ABS_OUTPUTDIR)/tmp/jigsaw-image -DEB_MROOT=/usr/local/jigsaw -deb-pkgs: $(DEB_PKGS) - @$(ECHO) ">>>Making "$@" @ `$(DATE)` ..." - $(RM) -r $(JIGSAW_IMAGE) - for m in `$(NAWK) '{print $$1}' $(MODULES_LIST)` ; do \ - $(MKDIR) -p $(JIGSAW_IMAGE)/$$m/$(DEB_MROOT); \ - for d in bin lib include ; do \ - if [ -d $(MODULES_DIR)/$$m/$$d ] ; then \ - $(CP) -rf $(MODULES_DIR)/$$m/$$d $(JIGSAW_IMAGE)/$$m/$(DEB_MROOT) ; \ - fi ; \ - done ; \ - (ISZ=$$(expr $$(du -s $(MODULES_DIR)/$$m | cut -f1)); \ - RES="$$(if [ -f $(MODULE_CLASSLIST_DIR)/$$m.resources ] ; then $(ECHO) "-r $(MODULES_DIR)/$$m/resources" ; fi)"; \ - $(HOST_JPKG_CMD) -v -m $(MODULES_DIR)/$$m/classes \ - -i $(JIGSAW_IMAGE)/$$m $$RES \ - --java-home /usr/local/jigsaw \ - -L /usr/local/jigsaw/lib/modules \ - -n 'Jigsaw Team' -e 'jigsaw-dev@openjdk.java.net' \ - -s "Jigsaw JDK 7 $$m module" --installed-size $$ISZ \ - -d $(DEB_PKGS) deb $$m) ; \ - done - @$(ECHO) ">>>Finished making "$@" @ `$(DATE)` ..." - --- /dev/null Wed Oct 20 09:31:04 2010 +++ new/make/common/BuildNativePackages.gmk Wed Oct 20 09:31:04 2010 @@ -0,0 +1,102 @@ +# +# Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# +# Generate jmod and debian packages for all jdk modules +# +PKGS = $(ABS_OUTPUTDIR)/jigsaw-pkgs +JMOD_PKGS = $(PKGS)/jmod +DEB_PKGS = $(PKGS)/deb + +## TODO: generate packages for modules that are modified +## instead of all modules +$(PKGS)/%: + $(RM) -r $@ + $(MKDIR) -p $@ + +# Prepare to sign modules +ifdef BUILD_SIGNED_MODULES + +include $(BUILDDIR)/javax/crypto/Defs-jce.gmk +SIGNER_ARGS = --sign --keystore $(SIGNING_KEYSTORE) --signer $(SIGNING_ALIAS) +SIGNER_PW = "< $(SIGNING_PASSPHRASE)" + +else #BUILD_SIGNED_MODULES + +SIGNER_ARGS = +SIGNER_PW = + +endif #BUILD_SIGNED_MODULES + +# Rules to generate jmod packages +# +jmod-pkgs: $(JMOD_PKGS) + @$(ECHO) ">>>Making "$@" @ `$(DATE)` ..." + for m in `$(NAWK) '{print $$1}' $(MODULES_LIST)` ; do \ + $(CD) $(ABS_MODULEPATH_DIR)/$$m && \ + (if [ -d resources ] ; then \ + RES="-r resources" ; \ + fi ; \ + if [ -d bin ] ; then \ + NATCMD="-natcmd bin" ; \ + fi ; \ + if [ -d lib ] ; then \ + NATLIB="-natlib lib" ; \ + fi ; \ + if [ -d etc ] ; then \ + CONFIG="-config etc" ; \ + fi ; \ + $(HOST_JPKG_CMD) -v -m classes \ + $$RES $$NATCMD $$NATLIB $$CONFIG $(SIGNER_ARGS) \ + -d $(JMOD_PKGS) jmod $$m $(SIGNER_PW)) || exit 1 ; \ + done + @$(ECHO) ">>>Finished making "$@" @ `$(DATE)` ..." + +# Rules to generate debian packages +# +JIGSAW_IMAGE = $(ABS_OUTPUTDIR)/tmp/jigsaw-image +DEB_MROOT=/usr/local/jigsaw +deb-pkgs: $(DEB_PKGS) + @$(ECHO) ">>>Making "$@" @ `$(DATE)` ..." + $(RM) -r $(JIGSAW_IMAGE) + for m in `$(NAWK) '{print $$1}' $(MODULES_LIST)` ; do \ + $(MKDIR) -p $(JIGSAW_IMAGE)/$$m/$(DEB_MROOT); \ + for d in bin lib include ; do \ + if [ -d $(MODULEPATH_DIR)/$$m/$$d ] ; then \ + $(CP) -rf $(MODULEPATH_DIR)/$$m/$$d $(JIGSAW_IMAGE)/$$m/$(DEB_MROOT) ; \ + fi ; \ + done ; \ + (ISZ=$$(expr $$(du -s $(MODULEPATH_DIR)/$$m | cut -f1)); \ + RES="$$(if [ -f $(MODULE_CLASSLIST_DIR)/$$m.resources ] ; then $(ECHO) "-r $(MODULEPATH_DIR)/$$m/resources" ; fi)"; \ + $(HOST_JPKG_CMD) -v -m $(MODULEPATH_DIR)/$$m/classes \ + -i $(JIGSAW_IMAGE)/$$m $$RES \ + --java-home /usr/local/jigsaw \ + -L /usr/local/jigsaw/lib/modules \ + -n 'Jigsaw Team' -e 'jigsaw-dev@openjdk.java.net' \ + -s "Jigsaw JDK 7 $$m module" --installed-size $$ISZ \ + -d $(DEB_PKGS) deb $$m) ; \ + done + @$(ECHO) ">>>Finished making "$@" @ `$(DATE)` ..." + --- /dev/null Wed Oct 20 09:31:05 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ClassListReader.java Wed Oct 20 09:31:05 2010 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package com.sun.classanalyzer; + +import java.io.*; +import java.util.*; + +/** + * ClassListReader constructs modules from the .classlist and + * .resources files + * + * @author Mandy Chung + * + * @see ClassListWriter + */ +public class ClassListReader { + private final ModuleBuilder builder; + ClassListReader() { + ModuleBuilder mb = null; + try { + mb = new ModuleBuilder(null, ""); // use default module builder + } catch (IOException e) { + // should not reach here + } + this.builder = mb; + } + ClassListReader(ModuleBuilder builder) { + this.builder = builder; + } + + Module loadModule(String name, Set classes, Set resources) + throws IOException { + Module module = Module.findModule(name); + if (module == null) { + module = builder.newModule(name); + } + + for (String pathname : classes) { + String cn = pathname.substring(0, pathname.length() - ".class".length()) + .replace(File.separatorChar, '.'); + Klass k = Klass.getKlass(cn); + module.addKlass(k); + } + for (String pathname : resources) { + if (!ResourceFile.isResource(pathname)) { + throw new RuntimeException("\"" + pathname + "\" not a resource file"); + } + ResourceFile res = new ResourceFile(pathname); + module.addResource(res); + } + return module; + } + + /** + * Returns the list of modules constructed from the classlist + * and resources list in the given directory. + */ + public Set loadModulesFrom(File dir) throws IOException { + String[] summaryFiles = dir.list(new FilenameFilter() { + public boolean accept(File f, String fname) { + if (fname.endsWith(".summary")) { + return true; + } + return false; + } + }); + + Set modules = new LinkedHashSet(); + for (String fn : summaryFiles) { + String name = fn.substring(0, fn.length() - ".summary".length()); + Module m = loadModuleFrom(dir, name); + if (m != null) { + modules.add(m); + } + } + + return modules; + } + + private Module loadModuleFrom(File dir, String name) throws IOException { + File clist = new File(dir, name + ".classlist"); + File rlist = new File(dir, name + ".resources"); + assert clist.exists() || rlist.exists(); + + Module module = loadModule(name, readFile(clist), readFile(rlist)); + + // read dependencies + addDependencies(new File(dir, name + ".dependencies")); + + return module; + } + + private static Set readFile(File f) throws IOException { + if (!f.exists()) { + return Collections.emptySet(); + } + + // parse configuration file + FileInputStream in = new FileInputStream(f); + Set fnames = new TreeSet(); + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) { + continue; + } + fnames.add(line); + } + return fnames; + } finally { + in.close(); + } + } + + private static void addDependencies(File f) throws IOException { + if (!f.exists()) { + return; + } + + // parse configuration file + FileInputStream in = new FileInputStream(f); + Set fnames = new TreeSet(); + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) { + continue; + } + + String[] ss = line.split("\\s+"); + // skip [optional] if exists + int i = ss[0].startsWith("[") ? 1 : 0; + if (ss.length < (i+2) || !ss[i+1].equals("->")) { + throw new RuntimeException("Invalid dependency: " + line); + } + + Klass from = Klass.getKlass(ss[i]); + Klass to = Klass.getKlass(ss[i+2]); + ResolutionInfo ri = ResolutionInfo.resolved(from, to); + from.addDep(to, ri); + to.addReferrer(from, ri); + } + } finally { + in.close(); + } + } +} --- /dev/null Wed Oct 20 09:31:07 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ClassListWriter.java Wed Oct 20 09:31:06 2010 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.classanalyzer; + +import java.io.File; +import java.io.PrintWriter; +import java.io.IOException; +import java.util.*; + +import com.sun.classanalyzer.AnnotatedDependency.*; +import com.sun.classanalyzer.Module.*; + +/** + * ClassListWriter outputs the following files of a given module: + * <module>.classlist, + * <module>.resources, + * <module>.summary, + * <module>.dependencies. + * + * @author Mandy Chung + */ +public class ClassListWriter { + private final Module module; + private final File dir; + ClassListWriter(File dir, Module m) { + this.module = m; + this.dir = dir; + } + + void printClassList() throws IOException { + if (module.classes().isEmpty()) { + return; + } + + PrintWriter writer = new PrintWriter(Files.resolve(dir, module.name(), "classlist")); + try { + for (Klass c : module.classes()) { + writer.format("%s\n", c.getClassFilePathname()); + } + } finally { + writer.close(); + } + + } + + void printResourceList() throws IOException { + // no file created if the module doesn't have any resource file + if (module.resources().isEmpty()) { + return; + } + + PrintWriter writer = new PrintWriter(Files.resolve(dir, module.name(), "resources")); + try { + for (ResourceFile res : module.resources()) { + writer.format("%s\n", res.getPathname()); + } + + } finally { + writer.close(); + } + + } + + void printDependencies() throws IOException { + printDependencies(false); + } + + void printDependencies(boolean showDynamic) throws IOException { + PrintWriter writer = new PrintWriter(Files.resolve(dir, module.name(), "dependencies")); + try { + // classes that this klass may depend on due to the AnnotatedDependency + Map> annotatedDeps = + AnnotatedDependency.getReferences(module); + + for (Klass klass : module.classes()) { + Set references = klass.getReferencedClasses(); + for (Klass other : references) { + String classname = klass.getClassName(); + boolean optional = OptionalDependency.isOptional(klass, other); + if (optional) { + classname = "[optional] " + classname; + } + + Module m = module.getModuleDependence(other); + if (m != null || other.getModule() == null) { + writer.format("%-40s -> %s (%s)", classname, other, m); + Reference ref = new Reference(klass, other); + if (annotatedDeps.containsKey(ref)) { + for (AnnotatedDependency ad : annotatedDeps.get(ref)) { + writer.format(" %s", ad.getTag()); + } + // printed; so remove the dependency from the annotated deps list + annotatedDeps.remove(ref); + } + writer.format("\n"); + } + } + } + + // print remaining dependencies specified in AnnotatedDependency list + if (annotatedDeps.size() > 0) { + for (Map.Entry> entry : annotatedDeps.entrySet()) { + Reference ref = entry.getKey(); + Module m = module.getModuleDependence(ref.referree); + if (m != null || ref.referree.getModule() == null) { + String classname = ref.referrer.getClassName(); + boolean optional = true; + boolean dynamic = true; + String tag = ""; + for (AnnotatedDependency ad : entry.getValue()) { + if (optional && !ad.isOptional()) { + optional = false; + tag = ad.getTag(); + } + + if (!ad.isDynamic()) { + dynamic = false; + } + } + if (!showDynamic && optional && dynamic) { + continue; + } + + if (optional) { + classname = "[optional] " + classname; + } else if (dynamic) { + classname = "[dynamic] " + classname; + } + writer.format("%-40s -> %s (%s) %s%n", classname, ref.referree, m, tag); + } + } + } + } finally { + writer.close(); + } + } +} --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/ClassPath.java Wed Oct 20 09:31:08 2010 +++ /dev/null Wed Oct 20 09:31:08 2010 @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ -package com.sun.classanalyzer; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -/** - * - * @author mchung - */ -public class ClassPath { - - public class FileInfo { - - File file; - JarFile jarfile; - int classCount; - long filesize; - - FileInfo(File f) throws IOException { - this.file = f; - this.classCount = 0; - if (file.getName().endsWith(".jar")) { - this.filesize = file.length(); - jarfile = new JarFile(f); - } - } - - File getFile() { - return file; - } - - JarFile getJarFile() { - return jarfile; - } - - String getName() throws IOException { - return file.getCanonicalPath(); - } - } - private List fileList = new ArrayList(); - private static ClassPath instance = new ClassPath(); - - static List getFileInfos() { - return instance.fileList; - } - - static ClassPath setJDKHome(String jdkhome) throws IOException { - List files = new ArrayList(); - File jre = new File(jdkhome, "jre"); - File lib = new File(jdkhome, "lib"); - if (jre.exists() && jre.isDirectory()) { - listFiles(new File(jre, "lib"), ".jar", files); - listFiles(lib, ".jar", files); - } else if (lib.exists() && lib.isDirectory()) { - // either a JRE or a jdk build image - listFiles(lib, ".jar", files); - - File classes = new File(jdkhome, "classes"); - if (classes.exists() && classes.isDirectory()) { - // jdk build outputdir - instance.add(classes); - } - } else { - throw new RuntimeException("\"" + jdkhome + "\" not a JDK home"); - } - - for (File f : files) { - instance.add(f); - } - return instance; - } - - static ClassPath setClassPath(String path) throws IOException { - if (path.endsWith(".class")) { - // one class file - File f = new File(path); - if (!f.exists()) { - throw new RuntimeException("Classfile \"" + f + "\" doesn't exist"); - } - - instance.add(f); - } else { - List jarFiles = new ArrayList(); - String[] locs = path.split(File.pathSeparator); - for (String p : locs) { - File f = new File(p); - if (!f.exists()) { - throw new RuntimeException("\"" + f + "\" doesn't exist"); - } - - if (f.isDirectory()) { - instance.add(f); // add the directory to look up .class files - listFiles(f, ".jar", jarFiles); - } else if (p.endsWith(".jar")) { - // jar files - jarFiles.add(f); - } else { - throw new RuntimeException("Invalid file \"" + f); - } - } - // add jarFiles if any - for (File f : jarFiles) { - instance.add(f); - } - } - - return instance; - } - - private void add(File f) throws IOException { - fileList.add(new FileInfo(f)); - } - - public static InputStream open(String pathname) throws IOException { - for (FileInfo fi : instance.fileList) { - if (fi.getName().endsWith(".jar")) { - String path = pathname.replace(File.separatorChar, '/'); - JarEntry e = fi.jarfile.getJarEntry(path); - if (e != null) { - return fi.jarfile.getInputStream(e); - } - } else if (fi.getFile().isDirectory()) { - File f = new File(fi.getFile(), pathname); - if (f.exists()) { - return new FileInputStream(f); - } - } else if (fi.file.isFile()) { - if (fi.getName().endsWith(File.separator + pathname)) { - return new FileInputStream(fi.file); - } - } - } - return null; - } - - static ClassFileParser parserForClass(String classname) throws IOException { - String pathname = classname.replace('.', File.separatorChar) + ".class"; - - ClassFileParser cfparser = null; - for (FileInfo fi : instance.fileList) { - if (fi.getName().endsWith(".class")) { - if (fi.getName().endsWith(File.separator + pathname)) { - cfparser = ClassFileParser.newParser(fi.getFile(), true); - break; - } - } else if (fi.getName().endsWith(".jar")) { - JarEntry e = fi.jarfile.getJarEntry(classname.replace('.', '/') + ".class"); - if (e != null) { - cfparser = ClassFileParser.newParser(fi.jarfile.getInputStream(e), e.getSize(), true); - break; - } - } else if (fi.getFile().isDirectory()) { - File f = new File(fi.getFile(), pathname); - if (f.exists()) { - cfparser = ClassFileParser.newParser(f, true); - break; - } - } - } - return cfparser; - } - - public static void parseAllClassFiles() throws IOException { - instance.parseFiles(); - } - - private void parseFiles() throws IOException { - Set classes = new HashSet(); - - int count = 0; - for (FileInfo fi : fileList) { - // filter out public generated classes (i.e. not public API) - // javax.management.remote.rmi._RMIConnectionImpl_Tie - // javax.management.remote.rmi._RMIServerImpl_Tie - if (fi.getName().endsWith(".class")) { - parseClass(fi); - } else if (fi.getName().endsWith(".jar")) { - Enumeration entries = fi.jarfile.entries(); - while (entries.hasMoreElements()) { - JarEntry e = entries.nextElement(); - if (e.getName().endsWith(".class")) { - ClassFileParser cfparser = ClassFileParser.newParser(fi.jarfile.getInputStream(e), e.getSize(), true); - cfparser.parseDependency(false); - fi.classCount++; - } else if (!e.isDirectory() && ResourceFile.isResource(e.getName())) { - ResourceFile.addResource(e.getName(), fi.jarfile.getInputStream(e)); - } - } - } else if (fi.getFile().isDirectory()) { - List files = new ArrayList(); - listFiles(fi.getFile(), "", files); - for (File f : files) { - if (f.getName().endsWith(".class")) { - parseClass(fi, f); - } else if (!f.isDirectory() && ResourceFile.isResource(f.getCanonicalPath())) { - String pathname = f.getCanonicalPath(); - String dir = fi.getName(); - if (!pathname.startsWith(dir)) { - throw new RuntimeException("Incorrect pathname " + pathname); - } - String name = pathname.substring(dir.length() + 1, pathname.length()); - BufferedInputStream in = new BufferedInputStream(new FileInputStream(f)); - try { - ResourceFile.addResource(name, in); - } finally { - in.close(); - } - } - } - } else { - // should not reach here - throw new RuntimeException("Unexpected class path: " + fi.getFile()); - } - } - } - - private void parseClass(FileInfo fi) throws IOException { - parseClass(fi, fi.getFile()); - } - - private void parseClass(FileInfo fi, File f) throws IOException { - ClassFileParser cfparser = ClassFileParser.newParser(f, true); - cfparser.parseDependency(false); - fi.classCount++; - // need to update the filesize for this directory - fi.filesize += fi.getFile().length(); - - } - - public static void listFiles(File path, String suffix, List result) { - if (path.isDirectory()) { - File[] children = path.listFiles(); - for (File c : children) { - listFiles(c, suffix, result); - } - - } else { - if (suffix.isEmpty() || path.getName().endsWith(suffix)) { - result.add(path); - } - } - } -} --- /dev/null Wed Oct 20 09:31:08 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ClassPaths.java Wed Oct 20 09:31:08 2010 @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.classanalyzer; + +import com.sun.classanalyzer.ClassPaths.*; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * Legacy class path. Each entry can be a directory containing + * classes and resources, or a jar file. It supports classpath + * wildcard "*" that lists all jar files in the given directory + * and also recursive wildcard "**" that lists all jar files + * recursively in the given directory and its subdirectories. + * + * @author Mandy Chung + */ +public class ClassPaths { + + protected final List entries = new LinkedList(); + private final Set classes = new LinkedHashSet(); + private final Set resources = new LinkedHashSet(); + + private ClassPaths() { + } + + public ClassPaths(String... paths) throws IOException { + for (String p : paths) { + String cp = p; + int index = p.indexOf("*"); + String wildcard = ""; + if (index >= 0) { + cp = p.substring(0, index); + wildcard = p.substring(index, p.length()); + } + + File f = new File(cp); + if (!f.exists()) { + throw new RuntimeException("\"" + f + "\" doesn't exist"); + } + if (wildcard.isEmpty()) { + if (f.isDirectory()) { + entries.add(new DirClassPath(f)); + } else if (cp.endsWith(".jar")) { + entries.add(new JarFileClassPath(f)); + } else { + entries.add(new ClassPath(f)); + } + } else { + if (wildcard.equals("*")) { + // add jar files in the specified directory + String[] ls = Files.list(f); + for (String s : ls) { + File sf = new File(f, s); + if (s.endsWith(".jar")) { + entries.add(new JarFileClassPath(f)); + } + } + } else if (wildcard.equals("**")) { + // add all jar files in all directories under f + addJarFileEntries(f); + } + } + } + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + /** + * Returns the modules containing the classes and resources being + * processed. + */ + public Set getModules() { + Set modules = new LinkedHashSet(); + for (Klass k : classes) { + modules.add(k.getModule().group()); + } + for (ResourceFile r : resources) { + modules.add(r.getModule().group()); + } + return modules; + } + + public void parse() throws IOException { + parse(null, true, false); + } + + public void parse(boolean deps, boolean apiOnly) throws IOException { + parse(null, deps, apiOnly); + } + + public void parse(Filter filter, boolean deps, boolean apiOnly) throws IOException { + ClassResourceVisitor crv = new ClassResourceVisitor(classes, resources, deps, apiOnly); + ClassPathVisitor cpvisitor = new ClassPathVisitor(crv, filter); + visit(cpvisitor, filter, null); + } + + public void printStats() { + System.out.format("%d classes %d resource files processed%n", + classes.size(), resources.size()); + } + + protected void addJarFileEntries(File f) throws IOException { + List ls; + if (f.isDirectory()) { + ls = Files.walkTree(f, new Files.Filter() { + @Override + public boolean accept(File f) throws IOException { + return f.isDirectory() || f.getName().endsWith(".jar"); + } + }); + } else { + ls = Collections.singletonList(f); + } + for (File jf : ls) { + entries.add(new JarFileClassPath(jf)); + } + } + // FIXME - used by open() method + static ClassPaths instance = null; + + static ClassPaths newInstance(String cpaths) throws IOException { + String[] paths = cpaths.split(File.pathSeparator); + instance = new ClassPaths(paths); + return instance; + } + + static ClassPaths newJDKClassPaths(String jdkhome) throws IOException { + instance = new JDKClassPaths(jdkhome); + return instance; + } + + static class JDKClassPaths extends ClassPaths { + JDKClassPaths(String jdkhome) throws IOException { + super(); + List files = new ArrayList(); + File jre = new File(jdkhome, "jre"); + File lib = new File(jdkhome, "lib"); + + if (jre.exists() && jre.isDirectory()) { + addJarFiles(new File(jre, "lib")); + addJarFiles(lib); + } else if (lib.exists() && lib.isDirectory()) { + // either a JRE or a jdk build image + File classes = new File(jdkhome, "classes"); + if (classes.exists() && classes.isDirectory()) { + // jdk build outputdir + this.entries.add(new DirClassPath(classes)); + } + addJarFiles(lib); + } else { + throw new RuntimeException("\"" + jdkhome + "\" not a JDK home"); + } + } + + // Filter the jigsaw module library, if any + final void addJarFiles(File dir) throws IOException { + String[] ls = dir.list(); + for (String fn : ls) { + File f = new File(dir, fn); + if (f.isDirectory() && !fn.equals("modules")) { + addJarFileEntries(f); + } else if (f.isFile() && fn.endsWith(".jar")) { + addJarFileEntries(f); + } + } + } + } + + /** + * Visits all entries in the class path. + */ + public List visit(final ClassPath.Visitor visitor, + final Filter filter, P p) + throws IOException { + List result = new ArrayList(); + for (ClassPath cp : entries) { + if (filter != null && !filter.accept(cp.file)) { + continue; + } + R r = cp.accept(visitor, p); + result.add(r); + } + return result; + } + + public static interface FileVisitor { + public void visitClass(File f, String cn) throws IOException; + public void visitClass(JarFile jf, JarEntry e) throws IOException; + public void visitResource(File f, String rn) throws IOException; + public void visitResource(JarFile jf, JarEntry e) throws IOException; + } + + public static interface Filter { + // any file, jar file, directory + public boolean accept(File f) throws IOException; + public boolean accept(JarFile jf, JarEntry e) throws IOException; + } + + public static class ClassPath { + + private final File file; + private final String name; + + ClassPath(File f) throws IOException { + this.file = f; + this.name = file.getCanonicalPath(); + } + + File getFile() { + return file; + } + + String getName() { + return name; + } + + R accept(Visitor visitor, P p) throws IOException { + return visitor.visitFile(file, this, p); + } + + public interface Visitor { + public R visitFile(File f, ClassPath cp, P p) throws IOException; + public R visitDir(File dir, ClassPath cp, P p) throws IOException; + public R visitJarFile(JarFile jf, ClassPath cp, P p) throws IOException; + } + } + + static class JarFileClassPath extends ClassPath { + JarFile jarfile; + JarFileClassPath(File f) throws IOException { + super(f); + this.jarfile = new JarFile(f); + } + + @Override + R accept(Visitor visitor, P p) throws IOException { + return visitor.visitJarFile(jarfile, this, p); + } + } + + class DirClassPath extends ClassPath { + + DirClassPath(File f) throws IOException { + super(f); + } + + @Override + R accept(final Visitor visitor, P p) throws IOException { + return visitor.visitDir(getFile(), this, p); + } + } + + static class ClassResourceVisitor implements FileVisitor { + + private final Set classes; + private final Set resources; + private final boolean parseDeps; + private final boolean apiOnly; + + ClassResourceVisitor(Set classes, + Set resources, + boolean parseDeps, + boolean apiOnly) { + this.classes = classes; + this.resources = resources; + this.apiOnly = apiOnly; + this.parseDeps = parseDeps; + } + + @Override + public void visitClass(File f, String cn) throws IOException { + ClassFileParser cfparser = ClassFileParser.newParser(f, true); + classes.add(cfparser.this_klass); + if (parseDeps) { + cfparser.parseDependency(apiOnly); + } + } + + @Override + public void visitClass(JarFile jf, JarEntry e) throws IOException { + ClassFileParser cfparser = ClassFileParser.newParser(jf.getInputStream(e), e.getSize(), true); + classes.add(cfparser.this_klass); + if (parseDeps) { + cfparser.parseDependency(apiOnly); + } + } + + @Override + public void visitResource(File f, String rn) throws IOException { + BufferedInputStream in = new BufferedInputStream(new FileInputStream(f)); + try { + ResourceFile res = ResourceFile.addResource(rn, in); + resources.add(res); + } finally { + in.close(); + } + } + + @Override + public void visitResource(JarFile jf, JarEntry e) throws IOException { + ResourceFile res = ResourceFile.addResource(e.getName(), jf.getInputStream(e)); + resources.add(res); + } + } + + static class ClassPathVisitor implements ClassPath.Visitor { + + private final FileVisitor visitor; + private final Filter filter; + + ClassPathVisitor(FileVisitor fv, Filter filter) { + this.visitor = fv; + this.filter = filter; + } + + @Override + public Void visitFile(File f, ClassPath cp, Void v) throws IOException { + if (filter != null && !filter.accept(f)) { + return null; + } + + String name = f.getName(); + String pathname = f.getCanonicalPath(); + String root = cp.getName(); + if (!name.equals(root) && !pathname.equals(root)) { + if (!pathname.startsWith(root)) { + throw new RuntimeException("Incorrect pathname " + pathname); + } + + name = pathname.substring(root.length() + 1, pathname.length()); + } + + if (name.endsWith(".class")) { + visitor.visitClass(f, name); + } else if (!f.isDirectory() && ResourceFile.isResource(f.getCanonicalPath())) { + visitor.visitResource(f, name); + } + return null; + } + + @Override + public Void visitDir(final File dir, final ClassPath cp, Void v) throws IOException { + List ls = Files.walkTree(dir, null); + for (File f : ls) { + visitFile(f, (ClassPath) cp, null); + } + return null; + } + + @Override + public Void visitJarFile(JarFile jf, ClassPath cp, Void v) throws IOException { + if (filter != null && !filter.accept(cp.getFile())) { + return null; + } + + Enumeration entries = jf.entries(); + while (entries.hasMoreElements()) { + JarEntry e = entries.nextElement(); + String name = e.getName(); + if (filter != null && !filter.accept(jf, e)) { + continue; + } + if (name.endsWith(".class")) { + visitor.visitClass(jf, e); + } else if (!e.isDirectory() && ResourceFile.isResource(name)) { + visitor.visitResource(jf, e); + } + } + return null; + } + }; + + public static InputStream open(String pathname) throws IOException { + ClassPath.Visitor fv = new ClassPath.Visitor() { + + @Override + public InputStream visitFile(File f, ClassPath cp, String pathname) throws IOException { + if (cp.getName().endsWith(File.separator + pathname)) { + return new FileInputStream(f); + } else { + return null; + } + } + + @Override + public InputStream visitDir(File dir, ClassPath cp, String pathname) throws IOException { + File f = new File(cp.getFile(), pathname); + if (f.exists()) { + return new FileInputStream(f); + } else { + return null; + } + } + + @Override + public InputStream visitJarFile(JarFile jf, ClassPath cp, String pathname) throws IOException { + String p = pathname.replace(File.separatorChar, '/'); + JarEntry e = jf.getJarEntry(p); + if (e != null) { + return jf.getInputStream(e); + } else { + return null; + } + } + }; + + for (ClassPath cp : instance.entries) { + InputStream in = cp.accept(fv, pathname); + if (in != null) { + return in; + } + } + return null; + } + + public ClassFileParser parserForClass(String classname) throws IOException { + String pathname = classname.replace('.', File.separatorChar) + ".class"; + ClassPath.Visitor fv = new ClassPath.Visitor() { + + @Override + public ClassFileParser visitFile(File f, ClassPath cp, String pathname) throws IOException { + if (cp.getName().endsWith(File.separator + pathname)) { + return ClassFileParser.newParser(f, true); + } else { + return null; + } + } + + @Override + public ClassFileParser visitDir(File dir, ClassPath cp, String pathname) throws IOException { + File f = new File(cp.getFile(), pathname); + if (f.exists()) { + return ClassFileParser.newParser(f, true); + } else { + return null; + } + } + + @Override + public ClassFileParser visitJarFile(JarFile jf, ClassPath cp, String pathname) throws IOException { + String p = pathname.replace(File.separatorChar, '/'); + JarEntry e = jf.getJarEntry(p); + if (e != null) { + return ClassFileParser.newParser(jf.getInputStream(e), e.getSize(), true); + } else { + return null; + } + } + }; + + for (ClassPath cp : entries) { + ClassFileParser cfparser = cp.accept(fv, pathname); + if (cfparser != null) { + return cfparser; + } + } + return null; + } +} --- /dev/null Wed Oct 20 09:31:10 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/Files.java Wed Oct 20 09:31:09 2010 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms openfile the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty openfile MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy openfile the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.classanalyzer; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Convenient methods for accessing files. + * + * @author Mandy Chung + */ +public class Files { + + static void ensureIsDirectory(File path) + throws IOException { + if (!path.exists() || !path.isDirectory()) { + throw new IOException(path + ": Not a directory"); + } + } + + static String[] list(File dir) + throws IOException { + ensureIsDirectory(dir); + String[] fs = dir.list(); + if (fs == null) { + throw new IOException(dir + ": Cannot list directory contents"); + } + return fs; + } + + static File[] listFiles(File dir) + throws IOException { + ensureIsDirectory(dir); + File[] fs = dir.listFiles(); + if (fs == null) { + throw new IOException(dir + ": Cannot list directory contents"); + } + return fs; + } + + static void createFile(File path) + throws IOException { + File dir = path.getParentFile(); + if (!dir.exists() && !dir.mkdirs()) { + throw new IOException(path + ": Cannot create directory"); + } + if (!path.createNewFile()) { + throw new IOException(path + ": Cannot create"); + } + } + + public static interface Filter { + public boolean accept(T x) throws IOException; + } + + public static List walkTree(File src, Filter filter) + throws IOException { + ensureIsDirectory(src); + List result = new ArrayList(); + String[] sls = list(src); + for (int i = 0; i < sls.length; i++) { + File sf = new File(src, sls[i]); + if (filter == null || filter.accept(sf)) { + if (sf.isDirectory()) { + result.addAll(walkTree(sf, filter)); + } else { + result.add(sf); + } + } + } + return result; + } + + public static void mkdirs(File d) + throws IOException { + String what = d.getName(); + if (!d.mkdirs()) { + throw new IOException(d + ": Cannot create " + what + " directory"); + } + } + + public static String resolve(File dir, String name, String suffix) + throws IOException { + if (!dir.exists()) { + mkdirs(dir); + } + + File f = new File(dir, name + "." + suffix); + return f.toString(); + } +} --- /dev/null Wed Oct 20 09:31:11 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/Modularizer.java Wed Oct 20 09:31:11 2010 @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.classanalyzer; + +import com.sun.classanalyzer.ResourceFile.ServiceProviderConfigFile; +import com.sun.classanalyzer.ClassPaths.*; +import java.io.*; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * Modularize classes and resources from legacy class paths to + * a module path containing a list of modules, one directory per module. + * + * @author Mandy Chung + */ +public class Modularizer { + + private final File modulepath; + private final Set modules; + private final ClassPaths cpaths; + public Modularizer(ClassPaths cpaths, File modulepath, Set modules) { + this.cpaths = cpaths; + this.modulepath = modulepath; + this.modules = modules; + } + + /** + * Modularizes the legacy class path files into + * multiple modules. + * @param update true if only modules with newer classes + * resources are updated. + */ + void run(boolean update) throws IOException { + for (Module m : modules) { + File mdir = new File(modulepath, m.name()); + ModuleContent mc = new ModuleContent(m, mdir); + mc.copy(update); + if (mc.isUpdated()) { + mc.printStats(); + } + } + } + + class ModuleContent { + int classes = 0; + int resources = 0; + long classBytes = 0; + long resourceBytes = 0; + final Module module; + final File classDir; // destination for classes + final File resDir; // destination for resources + final File mdir; + + /** + * Module content. The + * + * @param m module + * @param dir directory of the module content + */ + ModuleContent(Module m, File dir) throws IOException { + this.module = m; + this.mdir = dir; + // ## classes & resource files should be put in the + // ## same directory when jpkg tool is modified to + // ## support that + this.classDir = new File(dir, "classes"); + this.resDir = new File(dir, "resources"); + } + + /** + * Tests if any file in this module content is updated. + */ + boolean isUpdated() { + return (classes + resources) > 0; + } + + /** + * Copies the module content (classes and resources file) + * to the destination. + * + * @param update true if only modified files are copied; + * otherwise, all classes and resource files for this module + * are copied. + */ + void copy(boolean update) throws IOException { + if (!classDir.exists()) { + Files.mkdirs(classDir); + // override all files + update = false; + } + if (!resDir.exists()) { + Files.mkdirs(resDir); + // override all files + update = false; + } + + final boolean copyAll = update == false; + Module.Visitor visitor = new Module.Visitor() { + @Override + public Void visitClass(Klass k, File dir) { + String pathname = k.getClassFilePathname(); + Filter filter = copyAll ? null : new Filter(classDir, pathname); + long bytes; + try { + bytes = writeClass(k, filter); + if (bytes > 0) { + classes++; + classBytes += bytes; + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + return null; + } + + @Override + public Void visitResource(ResourceFile r, File dir) { + String pathname = r.getPathname(); + Filter filter = copyAll ? null : new Filter(resDir, pathname); + try { + long bytes = writeResource(r, filter); + if (bytes > 0) { + resources++; + resourceBytes += bytes; + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + return null; + } + }; + + module.visit(visitor, mdir); + } + + void printStats() { + System.out.format("%s: %d classes (%d bytes) %d resource files (%d bytes) copied%n", + module.name(), classes, classBytes, resources, resourceBytes); + } + + private ClassPath lastVisitedClassPath = null; + /** + * Write the classfile of the given class if not filtered + * + * @param k a Klass + * @param filter a Filter + * @return the number of bytes copied + */ + long writeClass(Klass k, Filter filter) throws IOException { + String pathname = k.getClassFilePathname(); + Copier visitor = new Copier(classDir, filter); + if (lastVisitedClassPath != null) { + ClassPath cp = lastVisitedClassPath.accept(visitor, pathname); + if (cp != null) { + assert cp == lastVisitedClassPath; + return visitor.bytes; + } + } + + // locate the source of the given class from the class paths + for (ClassPath cp : cpaths.entries()) { + ClassPath src = cp.accept(visitor, pathname); + if (src != null) { + // cache the ClassPath from which this class is copied + // Most of the files in a module likely come from the + // same jar or directory. + lastVisitedClassPath = src; + return visitor.bytes; + } + } + return 0; + } + + /** + * Write the resource file if not filtered + * + * @param res a ResourceFile + * @param filter a Filter + * @return the number of bytes copied + */ + long writeResource(ResourceFile res, Filter filter) throws IOException { + String pathname = res.getPathname(); + Copier visitor = new Copier(resDir, filter); + if (lastVisitedClassPath != null) { + ClassPath cp = lastVisitedClassPath.accept(visitor, pathname); + if (cp != null) { + assert cp == lastVisitedClassPath; + return visitor.bytes; + } + } + + // locate the source of the given resource file from the class paths + for (ClassPath cp : cpaths.entries()) { + ClassPath src = cp.accept(visitor, pathname); + if (src != null) { + // cache the ClassPath from which this class is copied + // Most of the files in a module likely come from the + // same jar or directory. + lastVisitedClassPath = src; + return visitor.bytes; + } + } + return 0; + } + + /** + * A ClassPath visitor to copy a file to the given destination + * if not filtered. + */ + class Copier implements ClassPath.Visitor { + final Filter filter; + final File dest; + long bytes = 0; + + Copier(File dest, Filter filter) { + this.filter = filter; + this.dest = dest; + } + + @Override + public ClassPath visitFile(File src, ClassPath cp, String pathname) throws IOException { + String name = pathname.replace(File.separatorChar, '/'); + if (cp.getName().endsWith(File.separator + pathname) + && matches(src, name)) { + if (filter == null || filter.accept(src)) { + File dst = new File(dest, pathname); + bytes += copy(src, dst); + } + return cp; + } else { + return null; + } + } + + @Override + public ClassPath visitDir(File dir, ClassPath cp, String pathname) throws IOException { + File src = new File(cp.getFile(), pathname); + File dst = new File(dest, pathname); + String name = pathname.replace(File.separatorChar, '/'); + + if (src.exists() && matches(src, name)) { + if (filter == null || filter.accept(src)) { + bytes += copy(src, dst); + } + return cp; + } else { + return null; + } + } + + @Override + public ClassPath visitJarFile(JarFile jf, ClassPath cp, String pathname) throws IOException { + String name = pathname.replace(File.separatorChar, '/'); + JarEntry e = jf.getJarEntry(name); + if (e != null && matches(jf, e, name)) { + if (filter == null || filter.accept(jf, e)) { + bytes += copy(jf, e); + } + return cp; + } else { + return null; + } + } + + boolean matches(File src, String name) throws IOException { + if (!name.startsWith("META-INF/services")) { + return true; + } + + BufferedInputStream in = new BufferedInputStream(new FileInputStream(src)); + try { + return matches(in, name); + } finally { + in.close(); + } + } + + boolean matches(JarFile jf, JarEntry e, String name) throws IOException { + if (!name.startsWith("META-INF/services")) { + return true; + } + return matches(jf.getInputStream(e), name); + } + + boolean matches(InputStream in, String name) throws IOException { + ServiceProviderConfigFile sp = new ServiceProviderConfigFile(name, in); + for (String p : sp.providers) { + Klass k = Klass.findKlass(p); + if (k == null) { + Trace.trace("Service %s: provider class %s not found%n", sp.service, p); + continue; + } + if (module.contains(k)) { + return true; + } + } + // return true if no provider; otherwise false + return sp.providers.isEmpty(); + } + + long copy(JarFile jf, JarEntry e) throws IOException { + File dst = new File(dest, e.getName().replace('/', File.separatorChar)); + if (!dst.exists()) { + Files.createFile(dst); + } + + byte[] buf = new byte[8192]; + InputStream in = jf.getInputStream(e); + long bytes = 0; + try { + FileOutputStream out = new FileOutputStream(dst); + try { + int n; + while ((n = in.read(buf)) > 0) { + out.write(buf, 0, n); + bytes += n; + } + } finally { + out.close(); + } + } finally { + in.close(); + } + + long lastModified = e.getTime(); + if (lastModified > 0) { + dst.setLastModified(lastModified); + } + return bytes; + } + + long copy(File src, File dst) + throws IOException { + assert src.exists(); + + if (!dst.exists()) { + Files.createFile(dst); + } + + BufferedInputStream in = new BufferedInputStream(new FileInputStream(src)); + byte[] buf = new byte[8192]; + long bytes = 0; + try { + FileOutputStream out = new FileOutputStream(dst); + try { + int n; + while ((n = in.read(buf)) > 0) { + out.write(buf, 0, n); + bytes += n; + } + } finally { + out.close(); + } + } finally { + in.close(); + } + dst.setLastModified(src.lastModified()); + if (src.canExecute()) { + dst.setExecutable(true, false); + } + return bytes; + } + } + } + + /** + * A filter that accepts files that don't exist in the given + * location or modified since it's copied. + */ + class Filter implements ClassPaths.Filter { + private final long timestamp; + Filter(File dir, String pathname) { + File destfile = new File(dir, pathname); + this.timestamp = destfile.exists() ? destfile.lastModified() : -1L; + } + + @Override + public boolean accept(File f) throws IOException { + if (f.isDirectory()) { + return true; + } + + long ts = f.lastModified(); + return (timestamp < 0 || ts < 0 || timestamp < ts); + } + + @Override + public boolean accept(JarFile jf, JarEntry e) throws IOException { + long ts = e.getTime(); + return timestamp < 0 || ts < 0 || timestamp < ts; + } + } + + public static void main(String[] args) throws Exception { + String jdkhome = null; + String cpath = null; + String classlistDir = null; + String modulepath = null; + boolean update = 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("-classpath")) { + cpath = getOption(args, i++); + } else if (arg.equals("-modulepath")) { + modulepath = getOption(args, i++); + } else if (arg.equals("-classlist")) { + classlistDir = getOption(args, i++); + } else if (arg.equals("-update")) { + // copy new files only + update = true; + } else { + error("Invalid option: " + arg); + } + } + + if (jdkhome == null && cpath == null) { + error("-jdkhome and -classpath not set"); + } + + if (jdkhome != null && cpath != null) { + error("Both -jdkhome and -classpath are set"); + } + + if (classlistDir == null || modulepath == null) { + error("-modulepath or -classlist not set"); + } + + ClassPaths cpaths = null; + if (jdkhome != null) { + cpaths = ClassPaths.newJDKClassPaths(jdkhome); + } else if (cpath != null) { + cpaths = ClassPaths.newInstance(cpath); + } + + ClassListReader reader = new ClassListReader(); + Set modules = reader.loadModulesFrom(new File(classlistDir)); + Modularizer modularizer = new Modularizer(cpaths, new File(modulepath), modules); + modularizer.run(update); + } + + private static String getOption(String[] args, int index) { + if (index < args.length) { + return args[index]; + } else { + error(args[index-1] + ": Missing argument"); + } + return null; + } + + 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: Modularizer \n"); + sb.append("Options: \n"); + sb.append("\t-jdkhome where all jars will be parsed\n"); + sb.append("\t-classpath where classes and jars will be parsed\n"); + sb.append("\t Either -jdkhome or -classpath option can be used.\n"); + sb.append("\t-classlist \n"); + sb.append("\t-update update modules with newer files\n"); + sb.append("\t-modulepath for writing modules\n"); + return sb.toString(); + } +} --- /dev/null Wed Oct 20 09:31:12 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ModuleBuilder.java Wed Oct 20 09:31:12 2010 @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.classanalyzer; + +import com.sun.classanalyzer.AnnotatedDependency.OptionalDependency; +import com.sun.classanalyzer.Module.ModuleVisitor; +import com.sun.classanalyzer.ModuleInfo.Dependence; +import com.sun.classanalyzer.ModuleInfo.PackageInfo; +import static com.sun.classanalyzer.ModuleInfo.Dependence.Modifier.*; +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * Module builder that creates modules as defined in the given + * module configuration files. The run() method assigns + * all classes and resources according to the module definitions. + * Additional dependency information can be specified e.g. + * Class.forName, JNI_FindClass, and service providers. + * + * @see DependencyConfig + * @author mchung + */ +public class ModuleBuilder { + + private final List depConfigs = new ArrayList(); + private final Map moduleinfos = + new LinkedHashMap(); + private final String version; + private final boolean mergeModules; + + public ModuleBuilder(List configs, String version) + throws IOException { + this(configs, null, true, version); + } + + public ModuleBuilder(List configs, + List depconfigs, + boolean merge, + String version) + throws IOException { + if (configs != null) { + // create modules based on the input config files + for (String file : configs) { + for (ModuleConfig mconfig : ModuleConfig.readConfigurationFile(file)) { + newModule(mconfig); + } + } + } + if (depconfigs != null) { + this.depConfigs.addAll(depconfigs); + } + this.mergeModules = merge; + this.version = version; + } + + /** + * Returns a module of a given name with no main entry point. + */ + public Module newModule(String name) throws IOException { + return newModule(new ModuleConfig(name, null)); + } + + /** + * Returns a module of a given ModuleConfig. + */ + public Module newModule(ModuleConfig mconfig) { + return Module.addModule(mconfig); + } + + /** + * Loads modules from the .classlist and .resources files in + * the given classListDir. + * + */ + public Set loadModulesFrom(File classlistDir) throws IOException { + ClassListReader reader = new ClassListReader(this); + return reader.loadModulesFrom(classlistDir); + } + + /** + * This method assigns the classes and resource files + * to modules and generates the package information and + * the module information. + * + * This method can be overridden in a subclass implementation. + */ + public void run() throws IOException { + // assign classes and resource files to the modules and + // group fine-grained modules per configuration files + buildModules(); + + // generate package information + buildPackageInfos(); + + // analyze cross-module dependencies and generate ModuleInfo + buildModuleInfos(); + } + + /** + * Returns the resulting top-level, non-empty modules. + */ + public Set getModules() { + return moduleinfos.keySet(); + } + + /** + * Builds modules from the existing list of classes and resource + * files according to the module configuration files. + * + */ + protected void buildModules() throws IOException { + // Add additional dependencies after classes are added to the modules + DependencyConfig.parse(depConfigs); + + // process the roots and dependencies to get the classes for each module + Collection modules = Module.getAllModules(); + for (Module m : modules) { + m.processRootsAndReferences(); + } + + if (mergeModules) { + // group fine-grained modules + Module.buildModuleMembers(); + } + } + + /** + * Build ModuleInfo for the top level modules. + */ + protected void buildModuleInfos() { + // backedges (i.e. reverse dependences) + Map> backedges = new TreeMap>(); + + // analyze the module's dependences and create ModuleInfo + for (Module m : Module.getAllModules()) { + if (m.isTopLevel()) { + ModuleInfo mi = buildModuleInfo(m); + m.setModuleInfo(mi); + moduleinfos.put(m, mi); + // keep track of the backedges + for (Dependence d : mi.requires()) { + // only add the top level modules + Module dep = d.getModule(); + Set set = backedges.get(dep); + if (set == null) { + set = new TreeSet(); + backedges.put(dep, set); + } + set.add(m); + } + } + } + + // fixup permits after all ModuleInfo are created in two passes: + // 1. permits the requesting module if it requires local dependence + // 2. if permits set is non-empty, permits + // all of its requesting modules + for (ModuleInfo mi : moduleinfos.values()) { + for (Dependence d : mi.requires()) { + if (d.isLocal()) { + Module dm = d.getModule(); + moduleinfos.get(dm).addPermit(mi.getModule()); + } + } + } + + for (Map.Entry> e : backedges.entrySet()) { + Module dm = e.getKey(); + ModuleInfo dmi = moduleinfos.get(dm); + if (dmi == null) { + throw new RuntimeException(dm + " null moduleinfo"); + } + if (dmi.permits().size() > 0) { + for (Module m : e.getValue()) { + dmi.addPermit(m); + } + } + } + } + + // module to packages + private final Map> packagesForModule = + new TreeMap>(); + // package name to PackageInfo set + private final Map> packages = + new TreeMap>(); + // module with split packages + private final Map> modulesWithSplitPackage = + new TreeMap>(); + + /** + * Builds PackageInfo for each top level module. + */ + protected void buildPackageInfos() { + for (Module m : Module.getAllModules()) { + if (m.isTopLevel()) { + Set pkgs = getPackageInfos(m); + packagesForModule.put(m, pkgs); + } + } + + for (Map.Entry> e : packagesForModule.entrySet()) { + Module m = e.getKey(); + for (PackageInfo p : e.getValue()) { + Set set = packages.get(p.pkgName); + if (set == null) { + set = new TreeSet(); + packages.put(p.pkgName, set); + } + set.add(p); + } + } + + for (Map.Entry> e : packages.entrySet()) { + String pkg = e.getKey(); + if (e.getValue().size() > 1) { + for (PackageInfo pi : e.getValue()) { + Set set = modulesWithSplitPackage.get(pi.module); + if (set == null) { + set = new TreeSet(); + modulesWithSplitPackage.put(pi.module, set); + } + set.add(pi); + } + } + } + } + + public Map> getSplitPackages() { + Map> result = new LinkedHashMap>(); + for (Map.Entry> e : packages.entrySet()) { + String pkg = e.getKey(); + if (e.getValue().size() > 1) { + for (PackageInfo pi : e.getValue()) { + Set set = result.get(pkg); + if (set == null) { + set = new TreeSet(); + result.put(pkg, set); + } + set.add(pi.module); + } + } + } + return result; + } + + private Set getPackageInfos(final Module m) { + Map packages = new TreeMap(); + Module.Visitor> visitor = + new Module.Visitor>() { + + @Override + public Void visitClass(Klass k, Map packages) { + // update package statistics + String pkg = k.getPackageName(); + PackageInfo pkginfo = packages.get(pkg); + if (pkginfo == null) { + pkginfo = new PackageInfo(m, pkg); + packages.put(pkg, pkginfo); + } + + if (k.exists()) { + // only count the class that is parsed + pkginfo.add(k.getFileSize()); + } + return null; + } + + @Override + public Void visitResource(ResourceFile r, Map packages) { + // nop + return null; + } + }; + + m.visit(visitor, packages); + return new TreeSet(packages.values()); + } + + private ModuleInfo buildModuleInfo(Module m) { + Map requires = new LinkedHashMap(); + Set permits = new TreeSet(); + + // add static dependences + for (Klass from : m.classes()) { + for (Klass to : from.getReferencedClasses()) { + if (m.isModuleDependence(to)) { + // is this dependence overridden as optional? + boolean optional = OptionalDependency.isOptional(from, to); + addDependence(requires, new Dependence(from, to, optional)); + } + } + } + + // add dependency due to the main class + Klass k = m.mainClass(); + if (k != null && m.isModuleDependence(k)) { + addDependence(requires, new Dependence(k.getModule())); + } + + // add requires and permits specified in the config files + processModuleConfigs(m, requires, permits); + + // add dependencies due to the AnnotatedDependency + for (Dependence d : AnnotatedDependency.getDependencies(m)) { + addDependence(requires, d); + } + + // Add LOCAL to the dependence and permits will be added + // in the separate phase + Set splitPkgs = modulesWithSplitPackage.get(m); + if (splitPkgs != null) { + for (PackageInfo sp : splitPkgs) { + Set pis = packages.get(sp.pkgName); + for (PackageInfo pi : pis) { + // is the package splitted with its dependence? + if (requires.containsKey(pi.module)) { + // If so, the dependence has to be LOCAL + requires.get(pi.module).addModifier(LOCAL); + } + } + } + } + + // use the module's exporter in the dependence + Set depset = new TreeSet(); + for (Dependence d : requires.values()) { + Dependence dep = d; + if (!d.isLocal()) { + Module exp = d.getModule().exporter(m); + if (exp == null) { + throw new RuntimeException(d.getModule() + " null exporter"); + } + if (d.getModule() != exp && exp != m) { + dep = new Dependence(exp, d.modifiers()); + } + } + // ## not to include optional dependences in jdk.boot + // ## should move this to jdk.base + if (m instanceof PlatformModuleBuilder.BootModule && d.isOptional()) { + continue; + } + + depset.add(dep); + } + ModuleInfo mi = new ModuleInfo(m, version, packagesForModule.get(m), depset, permits); + return mi; + } + + private void addDependence(Map requires, Dependence d) { + Module dm = d.getModule(); + Dependence dep = requires.get(dm); + if (dep == null || dep.equals(d)) { + requires.put(dm, d); + } else { + if (dep.getModule() != d.getModule()) { + throw new RuntimeException("Unexpected dependence " + dep + " != " + d); + } + + // update the modifiers + dep.update(d); + requires.put(dm, dep); + } + } + + private void processModuleConfigs(final Module module, + final Map requires, + final Set permits) { + ModuleVisitor v = new ModuleVisitor() { + + public void preVisit(Module p, Void dummy) { + } + + public void visited(Module p, Module m, Void dummy) { + for (Dependence d : m.config().requires()) { + addDependence(requires, d); + } + for (String name : m.config().permits()) { + Module pm = Module.findModule(name); + if (pm != null) { + permits.add(pm.group()); + } else { + throw new RuntimeException("module " + name + + " specified in the permits rule for " + m.name() + + " doesn't exist"); + } + } + } + + public void postVisit(Module p, Void dummy) { + } + }; + + Set visited = new TreeSet(); + // first add requires and permits for the module + v.visited(module, module, null); + // then visit their members + module.visitMembers(visited, v, null); + } +} --- /dev/null Wed Oct 20 09:31:14 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ModuleInfo.java Wed Oct 20 09:31:13 2010 @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.classanalyzer; + +import java.util.*; + +/** + * Information about a module. ModuleInfo.toString() returns + * a string representation of the module-info.java source file. + * + * @author Mandy Chung + */ +public class ModuleInfo { + + private final Module module; + private final String version; + private final Set packages; + private final Set requires; + private final Set permits; + + ModuleInfo(Module m, String version, + Collection packages, + Collection reqs, + Collection permits) { + this.module = m; + this.version = version; + this.packages = new TreeSet(packages); + this.permits = new TreeSet(permits); + + this.requires = new TreeSet(); + // filter non-top level module + for (Dependence d : reqs) { + if (d.getModule().isTopLevel()) + requires.add(d); + } + } + + public Module getModule() { + return module; + } + + /** + * This module's identifier + */ + public String name() { + return module.name(); + } + + public String id() { + return module.name() + " @ " + version; + } + + public Set packages() { + return Collections.unmodifiableSet(packages); + } + + /** + * The dependences of this module + */ + public Set requires() { + return Collections.unmodifiableSet(requires); + } + + /** + * The modules that are permitted to require this module + */ + public Set permits() { + return Collections.unmodifiableSet(permits); + } + + public void addPermit(Module m) { + permits.add(m); + } + + /** + * The fully qualified name of the main class of this module + */ + public String mainClass() { + Klass k = module.mainClass(); + return k != null ? k.getClassName() : ""; + } + + private void visitDependence(Dependence.Filter filter, Set visited, Set result) { + if (!visited.contains(module)) { + visited.add(module); + + for (Dependence d : requires()) { + if (filter == null || filter.accept(d)) { + ModuleInfo dmi = d.getModule().getModuleInfo(); + dmi.visitDependence(filter, visited, result); + } + } + result.add(module); + } + } + + Set dependences(Dependence.Filter filter) { + Set visited = new TreeSet(); + Set result = new LinkedHashSet(); + + visitDependence(filter, visited, result); + return result; + } + + private static final String INDENT = " "; + /** + * Returns a string representation of module-info.java for + * this module. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("module ").append(id()).append(" {\n"); + + for (Dependence d : requires()) { + sb.append(INDENT).append("requires"); + for (Dependence.Modifier mod : d.mods) { + sb.append(" ").append(mod); + } + String did = d.getModule().getModuleInfo().id(); + sb.append(" ").append(did).append(";\n"); + } + + String permits = INDENT + "permits "; + int i = 0; + for (Module pm : permits()) { + if (i > 0) { + permits += ", "; + if ((i % 5) == 0) + permits += "\n" + INDENT + " "; // "permits" + } + permits += pm.name(); + i++; + } + + if (permits().size() > 0) { + sb.append(permits).append(";\n"); + } + if (module.mainClass() != null) { + sb.append(INDENT).append("class ").append(mainClass()).append(";\n"); + } + sb.append("}\n"); + return sb.toString(); + } + + static class Dependence implements Comparable { + + static enum Modifier { + + PUBLIC("public"), + OPTIONAL("optional"), + LOCAL("local"); + private final String name; + + Modifier(String n) { + this.name = n; + } + + @Override + public String toString() { + return name; + } + } + private final String id; + private EnumSet mods; + private Module sm = null; + + public Dependence(Module sm) { + this(sm, false); + } + + public Dependence(Module sm, boolean optional) { + this(sm, modifier(optional)); + } + + public Dependence(Klass from, Klass to, boolean optional) { + this(to.getModule(), modifier(optional)); + } + + public Dependence(Module sm, EnumSet mods) { + this.sm = sm.group(); + this.id = this.sm.name(); + this.mods = mods; + } + public Dependence(String name, boolean optional) { + this(name, optional, false, false); + } + + public Dependence(String name, boolean optional, boolean reexport, boolean local) { + Set ms = new TreeSet(); + if (optional) { + ms.add(Modifier.OPTIONAL); + } + if (reexport) { + ms.add(Modifier.PUBLIC); + } + if (local) { + ms.add(Modifier.LOCAL); + } + this.id = name; + this.mods = ms.isEmpty() + ? EnumSet.noneOf(Modifier.class) + : EnumSet.copyOf(ms); + } + + private static EnumSet modifier(boolean optional) { + return optional ? EnumSet.of(Modifier.OPTIONAL) : + EnumSet.noneOf(Modifier.class); + } + + synchronized Module getModule() { + if (sm == null) { + Module m = Module.findModule(id); + if (m == null) { + throw new RuntimeException("Module " + id + " doesn't exist"); + } + sm = m.group(); + } + return sm; + } + + public boolean isOptional() { + return mods.contains(Modifier.OPTIONAL); + } + + public boolean isLocal() { + return mods.contains(Modifier.LOCAL); + } + + public boolean isPublic() { + return mods.contains(Modifier.PUBLIC); + } + + public void addModifier(Modifier e) { + mods.add(e); + } + + public void update(Dependence d) { + // static dependence overrides the optional + if (isOptional() && !d.isOptional()) { + mods.remove(Modifier.OPTIONAL); + } + // local dependence overrides non-local dependence + if (!isLocal() && d.isLocal()) { + mods.add(Modifier.LOCAL); + } + + // reexport + if (!isPublic() && d.isPublic()) { + mods.add(Modifier.PUBLIC); + } + } + + public EnumSet modifiers() { + return mods; + } + + static interface Filter { + public boolean accept(Dependence d); + } + + @Override + public int compareTo(Dependence d) { + if (this.equals(d)) { + return 0; + } + return id.compareTo(d.id); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Dependence)) { + return false; + } + if (this == obj) { + return true; + } + + Dependence d = (Dependence) obj; + return this.id.equals(d.id) && mods.equals(d.mods); + } + + @Override + public int hashCode() { + int hash = 3; + hash = 19 * hash + this.id.hashCode(); + return hash; + } + } + + static class PackageInfo implements Comparable { + + final Module module; + final String pkgName; + int count; + long filesize; + + public PackageInfo(Module m, String name) { + this.module = m; + this.pkgName = name; + this.count = 0; + this.filesize = 0; + } + + void add(PackageInfo pkg) { + this.count += pkg.count; + this.filesize += pkg.filesize; + } + + void add(long size) { + count++; + filesize += size; + + } + + @Override + public int hashCode() { + int hash = 5; + hash = 59 * hash + (this.module != null ? this.module.hashCode() : 0); + hash = 59 * hash + (this.pkgName != null ? this.pkgName.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object o) { + if (o instanceof PackageInfo) { + PackageInfo p = (PackageInfo) o; + return p.module.equals(this.module) && p.pkgName.equals(this.pkgName); + } + return false; + } + + @Override + public int compareTo(PackageInfo p) { + if (this.equals(p)) { + return 0; + } else if (pkgName.compareTo(p.pkgName) == 0) { + return module.compareTo(p.module); + } else { + return pkgName.compareTo(p.pkgName); + } + } + } +} --- old/make/tools/classanalyzer/src/com/sun/classanalyzer/Platform.java Wed Oct 20 09:31:15 2010 +++ /dev/null Wed Oct 20 09:31:15 2010 @@ -1,320 +0,0 @@ -/* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.sun.classanalyzer; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; - -import java.util.Collection; -import java.util.Deque; -import java.util.LinkedList; -import java.util.Properties; -import java.util.Set; -import java.util.TreeSet; -import static com.sun.classanalyzer.Module.*; - -/** - * Platform module. - * - * The name of the platform modules starts with either "jdk." or "sun.". - * All sun.* and jdk.boot are local modules. Any requesting module - * of a local module has to be explicitly permitted. - * - * The input module config files can define "sun.*" and "jdk.*" - * modules. For any sun.* module, it will have a corresponding - * non-local platform module that is defined for application modules - * to require. - * - * The tool will create the following platform modules: - * 1) jdk. for each sun. module - * 2) jdk module - represents the entire JDK - * 3) jdk.jre module - represents the entire JRE - * - */ -public class Platform { - - static final String DEFAULT_BOOT_MODULE = "jdk.boot"; - static final String JDK_BASE_MODULE = "jdk.base"; - // platform modules created but not defined in the input module configs. - static final String JDK_MODULE = "jdk"; - static final String JRE_MODULE = "jdk.jre"; - static final String LEGACY_MODULE = "jdk.legacy"; - // the following modules are expected to be defined in - // the input module config files. - static final String JDK_TOOLS = "jdk.tools"; - static final String JRE_TOOLS = "jdk.tools.jre"; - static final String JDK_BASE_TOOLS = "jdk.tools.base"; - static final String JDK_LANGTOOLS = "jdk.langtools"; - - static boolean isBootModule(String name) { - return name.equals(DEFAULT_BOOT_MODULE); - } - private static BootModule bootModule; - - static Module createBootModule(ModuleConfig config) { - bootModule = new BootModule(config); - return bootModule; - } - - static Module bootModule() { - return bootModule; - } - private static Module jdkBaseModule; - - static Module jdkBaseModule() { - if (jdkBaseModule == null) { - jdkBaseModule = findModule(JDK_BASE_MODULE); - } - return jdkBaseModule; - } - private static Module jdkBaseToolModule; - - static Module jdkBaseToolModule() { - if (jdkBaseToolModule == null) { - jdkBaseToolModule = findModule(JDK_BASE_TOOLS); - } - return jdkBaseToolModule; - } - private static Module jdkModule; - private static Module jreModule; - private static Module legacyModule; - - static Module jdkModule() { - return jdkModule; - } - - static Module jreModule() { - return jreModule; - } - - static Module legacyModule() { - return legacyModule; - } - - private static Module addPlatformModule(String name, String mainClass) { - ModuleConfig config = null; - try { - config = new ModuleConfig(name, mainClass); - return Module.addModule(config); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - static boolean isAggregator(String name) { - return name.equals(JDK_MODULE) || - name.equals(JRE_MODULE) || - name.equals(JDK_TOOLS) || - name.equals(JRE_TOOLS) || - name.equals(JDK_BASE_TOOLS) || - name.equals(LEGACY_MODULE) || - name.startsWith(JDK_LANGTOOLS); - } - - // returns the module that is used by the requires statement - // in other module's module-info - static Module toRequiresModule(Module m) { - Module moduleForRequires = m; - if (m == bootModule()) { - moduleForRequires = jdkBaseModule(); - } else if (m.name().startsWith("sun.")) { - // create an aggregate module for each sun.* module - String mn = m.name().replaceFirst("sun", "jdk"); - String mainClassName = m.mainClass() == null ? null : m.mainClass().getClassName(); - - Module rm = findModule(mn); - if (rm != null) { - if (rm.mainClass() != m.mainClass()) { - throw new RuntimeException(mn + - " module already exists but mainClass not matched"); - } - return rm; - } - - if (m.hasPlatformAPIs()) { - ModuleConfig config = null; - try { - config = new ModuleConfig(mn, mainClassName); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - moduleForRequires = Module.addModule(config); - moduleForRequires.addRequiresModule(m); - } - } - return moduleForRequires; - } - - static void fixupPlatformModules() { - // Create the full jdk and jre modules - jdkModule = addPlatformModule(JDK_MODULE, null /* no main class */); - jreModule = addPlatformModule(JRE_MODULE, null /* no main class */); - - Module jreTools = findModule(JRE_TOOLS); - Module jdkTools = findModule(JDK_TOOLS); - Module jdkBaseTools = findModule(JDK_BASE_TOOLS); - - for (Module m : getTopLevelModules()) { - String mn = m.name(); - - // initialize module-info - m.fixupModuleInfo(); - - // set up the jdk, jdk.jre and jdk.legacy modules - if (mn.startsWith("jdk.") || mn.startsWith("sun.")) { - Module req = m.toRequiredModule(); - - if (!m.isAggregator() && !mn.equals(JDK_MODULE) && !mn.equals(JRE_MODULE)) { - // all platform modules are required jdk module - jdkModule.addRequiresModule(req); - if (m.isBootConnected() || mn.equals(JRE_TOOLS)) { - // add all modules that are strongly connected to jdk.boot to JRE - jreModule.addRequiresModule(req); - } - } - } else { - Trace.trace("Non-platform module: %s%n", m.name()); - } - } - // fixup the base module to include optional dependences from boot - // ## It adds jndi, logging, and xml optional dependences - // bootModule.fixupBase(); - } - private static String[] corePkgs = new String[]{ - "java", "javax", - "org.omg", "org.w3c.dom", - "org.xml.sax", "org.ietf.jgss" - }; - private static Set nonCorePkgs = new TreeSet(); - - static void addNonCorePkgs(String file) throws FileNotFoundException, IOException { - File f = new File(file); - Properties props = new Properties(); - BufferedReader reader = null; - - try { - reader = new BufferedReader(new FileReader(f)); - props.load(reader); - String s = props.getProperty("NON_CORE_PKGS"); - String[] ss = s.split("\\s+"); - Deque values = new LinkedList(); - - for (String v : ss) { - values.add(v.trim()); - } - - String pval; - while ((pval = values.poll()) != null) { - if (pval.startsWith("$(") && pval.endsWith(")")) { - String key = pval.substring(2, pval.length() - 1); - String value = props.getProperty(key); - if (value == null) { - throw new RuntimeException("key " + key + " not found"); - } - ss = value.split("\\s+"); - for (String v : ss) { - values.add(v.trim()); - } - continue; - } - if (pval.startsWith("java.") || pval.startsWith("javax") || - pval.startsWith("com.") || pval.startsWith("org.")) { - nonCorePkgs.add(pval); - } else { - throw new RuntimeException("Invalid non core package: " + pval); - } - } - } finally { - if (reader != null) { - reader.close(); - } - } - } - - static boolean isPlatformAPI(String classname) { - for (String pkg : corePkgs) { - if (classname.startsWith(pkg + ".")) { - return true; - } - } - return false; - } - - static boolean isNonCoreAPI(String pkgName) { - for (String pkg : nonCorePkgs) { - if (pkgName.startsWith(pkg)) { - return true; - } - } - return false; - } - - static class BootModule extends Module { - - BootModule(ModuleConfig config) { - super(config); - } - - Collection dependences() { - Set result = new TreeSet(); - for (Dependency d : dependents()) { - // filter out optional dependences from jdk.boot module - if (!d.optional) { - result.add(d); - } - } - return result; - } - - Module toRequiresModule() { - return jdkBaseModule(); - } - - boolean isBootConnected() { - return true; - } - - boolean requirePermits() { - return true; - } - - void fixupBase() { - // fixup jdk.boot optional dependences - for (Dependency d : dependents()) { - if (d.optional) { - Module m = d.module().toRequiredModule(); - jdkBaseModule().addRequiresModule(m); - Trace.trace("add requires %s to %s%n", m, jdkBaseModule().name()); - if (m != d.module()) { - m.permits().remove(this); - } - - } - } - - } - } -} --- /dev/null Wed Oct 20 09:31:15 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/PlatformModuleBuilder.java Wed Oct 20 09:31:15 2010 @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.classanalyzer; + +import com.sun.classanalyzer.ModuleInfo.Dependence; +import static com.sun.classanalyzer.PlatformModuleBuilder.PlatformModuleNames.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.*; + +/** + * A platform module builder for JDK. JDK's boot module, base module, + * and JRE tools module must be defined in the modules.config files. + * The platform module builder will create the following platform + * modules in addition to the ones specified in the configuration files: + * - JDK and JRE aggregator modules + * - one jdk. aggregator module for each sun. module to + * reexport its public APIs + * + * @author Mandy Chung + */ +public class PlatformModuleBuilder extends ModuleBuilder { + /** + * Platform modules that must be defined in the modules.properties + */ + static class PlatformModuleNames { + static final String BOOT_MODULE = + getValue("platform.boot.module"); + static final String BASE_MODULE = + getValue("platform.base.module"); + static final String JDK_MODULE = + getValue("platform.jdk.module"); + static final String JRE_MODULE = + getValue("platform.jre.module"); + static final String JRE_TOOLS_MODULE = + getValue("platform.jre.tools.module"); + + private static String getValue(String key) { + String value = Module.getModuleProperty(key); + if (value == null || value.isEmpty()) { + throw new RuntimeException("Null or empty module property: " + key); + } + return value; + } + } + + private BootModule bootModule; + private final PlatformModule jdkModule; + private final PlatformModule jreModule; + + public PlatformModuleBuilder(List configs, String version) + throws IOException { + this(configs, null, true, version); + } + + public PlatformModuleBuilder(List configs, + List depconfigs, + boolean merge, + String version) + throws IOException { + super(null, depconfigs, merge, version); + + Module.setBaseModule(BASE_MODULE); + + // create modules based on the input config files + for (String file : configs) { + for (ModuleConfig mconfig : ModuleConfig.readConfigurationFile(file)) { + newModule(mconfig); + } + } + + // Create the full jdk and jre modules + jdkModule = (PlatformModule) newModule(JDK_MODULE); + jreModule = (PlatformModule) newModule(JRE_MODULE); + } + + @Override + public Module newModule(ModuleConfig mconfig) { + return addPlatformModule(mconfig); + } + + @Override + public void run() throws IOException { + // assign classes and resource files to the modules + // group fine-grained modules per configuration files + buildModules(); + + // build public jdk modules to reexport sun.* modules + buildJDKModules(); + + // generate package information + buildPackageInfos(); + + // analyze cross-module dependencies and generate ModuleInfo + buildModuleInfos(); + + // ## Hack: add local to all requires + for (Module m : Module.getAllModules()) { + if (m.isTopLevel()) { + PlatformModule pm = (PlatformModule) m; + if (pm.isBootConnected()) { + for (Dependence d : pm.getModuleInfo().requires()) { + d.addModifier(Dependence.Modifier.LOCAL); + } + } + } + } + } + + private void buildJDKModules() { + Set modules = new LinkedHashSet(); + PlatformModule jreToolModule = (PlatformModule) + Module.findModule(JRE_TOOLS_MODULE); + + for (Module m : Module.getAllModules()) { + if (m.isTopLevel()) { + PlatformModule pm = (PlatformModule) m; + modules.add(pm); + } + } + + // set exporter + for (PlatformModule pm : modules) { + PlatformModule exporter = pm; + String name = pm.name(); + if (name.startsWith("sun.")) { + // create an aggregate module for each sun.* module + String mn = name.replaceFirst("sun", "jdk"); + String mainClassName = + pm.mainClass() == null ? null : pm.mainClass().getClassName(); + + PlatformModule rm = (PlatformModule) Module.findModule(mn); + if (rm != null) { + if (pm.mainClass() != rm.mainClass()) { + // propagate the main class to its aggregator module + rm.setMainClass(mainClassName); + } + exporter = rm; + } else if (pm.hasPlatformAPIs()) { + ModuleConfig config = null; + try { + config = new ModuleConfig(mn, mainClassName); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + exporter = addPlatformModule(config); + } + + if (pm != exporter) { + pm.reexportBy(exporter); + } + } + } + + // base module to reexport boot module + bootModule.reexportBy((PlatformModule) Module.findModule(BASE_MODULE)); + + // set up the jdk, jdk.jre and jdk.legacy modules + for (Module m : Module.getAllModules()) { + if (m.isTopLevel()) { + PlatformModule pm = (PlatformModule) m; + String name = pm.name(); + if (name.startsWith("jdk.") || name.startsWith("sun.")) { + if (pm != jdkModule && pm != jreModule) { + Module exp = pm.exporter(jdkModule); + // the "jdk" module requires all platform modules (public ones) + jdkModule.config().export(exp); + if (pm.isBootConnected() || pm == jreToolModule) { + // add all modules that are strongly connected to jdk.boot to JRE + jreModule.config().export(exp); + } + } + } + } + } + + } + + /* + * Returns an ordered list of platform modules according to + * their dependencies with jdk.boot always be the first. + */ + @Override + public Set getModules() { + Set modules = new LinkedHashSet(); + // put the boot module first + modules.add(bootModule); + Module base = Module.findModule(BASE_MODULE); + modules.addAll(base.getModuleInfo().dependences( + new Dependence.Filter() { + @Override + public boolean accept(Dependence d) { + return !d.isOptional(); + } + })); + modules.addAll(jdkModule.getModuleInfo().dependences(null)); + for (Module m : Module.getAllModules()) { + if (m.isTopLevel() && !modules.contains(m)) { + modules.addAll(m.getModuleInfo().dependences(null)); + } + } + return modules; + } + + void readNonCorePackagesFrom(String nonCorePkgsFile) throws IOException { + PlatformPackage.addNonCorePkgs(nonCorePkgsFile); + } + + private PlatformModule addPlatformModule(ModuleConfig config) { + PlatformModule m; + if (config.module.equals(BOOT_MODULE)) { + bootModule = new BootModule(config); + m = bootModule; + } else { + m = new PlatformModule(config); + } + Module.addModule(m); + return m; + } + + public class PlatformModule extends Module { + private Module exporter; // module that reexports this platform module + private String mainClass; + public PlatformModule(ModuleConfig config) { + super(config); + this.exporter = this; + this.mainClass = config.mainClass(); + } + + // support for incremental build + // an aggregate module "jdk.*" is not defined in modules.config + // files but created by the platform module builder + // Set to the main class of sun.* module + void setMainClass(String classname) { + String mn = name(); + if (!mn.startsWith("jdk") || !isEmpty()) { + throw new RuntimeException("module " + name() + + " not an aggregator"); + } + + if (classname == null) + throw new RuntimeException("Null main class for module " + name()); + + mainClass = classname; + } + + @Override + Klass mainClass() { + if (mainClass != null) + return Klass.findKlass(mainClass); + else + return null; + } + + @Override + boolean allowEmpty() { + return this == jdkModule || this == jreModule || super.allowEmpty(); + } + + // requires local for JRE modules that are strongly + // connected with the boot module + boolean isBootConnected() { + // ## should it check for local? + return config().requires.containsKey(BOOT_MODULE); + } + + private int platformAPIs; + boolean hasPlatformAPIs() { + platformAPIs = 0; + Visitor v = new Visitor() { + public Void visitClass(Klass k, PlatformModule pm) { + if (PlatformPackage.isOfficialClass(k.getClassName())) { + pm.platformAPIs++; + } + return null; + } + + public Void visitResource(ResourceFile r, PlatformModule pm) { + return null; + } + }; + + this.visit(v, this); + return platformAPIs > 0; + } + + // returns the module that is used by the requires statement + // in other module's module-info + @Override + public Module exporter(Module from) { + PlatformModule pm = (PlatformModule) from; + if (pm.isBootConnected()) { + // If it's a local module requiring jdk.boot, retain + // the original requires; otherwise, use its external + // module + return this; + } else { + return exporter; + } + } + + void reexportBy(PlatformModule pm) { + exporter = pm; + // sun. permits jdk. + this.config().addPermit(pm); + // jdk. requires public sun.; + pm.config().export(this); + } + } + + public class BootModule extends PlatformModule { + BootModule(ModuleConfig config) { + super(config); + } + + @Override + boolean isBootConnected() { + return true; + } + } + + static class PlatformPackage { + + private static String[] corePkgs = new String[]{ + "java", "javax", + "org.omg", "org.w3c.dom", + "org.xml.sax", "org.ietf.jgss" + }; + private static Set nonCorePkgs = new TreeSet(); + + static boolean isOfficialClass(String classname) { + for (String pkg : corePkgs) { + if (classname.startsWith(pkg + ".")) { + return true; + } + } + + // TODO: include later + /* + for (String pkg : nonCorePkgs) { + if (classname.startsWith(pkg + ".")) { + return true; + } + } + */ + return false; + } + + // process a properties file listing the non core packages + static void addNonCorePkgs(String file) throws IOException { + File f = new File(file); + Properties props = new Properties(); + BufferedReader reader = null; + + try { + reader = new BufferedReader(new FileReader(f)); + props.load(reader); + String s = props.getProperty("NON_CORE_PKGS"); + String[] ss = s.split("\\s+"); + Deque values = new LinkedList(); + + for (String v : ss) { + values.add(v.trim()); + } + + String pval; + while ((pval = values.poll()) != null) { + if (pval.startsWith("$(") && pval.endsWith(")")) { + String key = pval.substring(2, pval.length() - 1); + String value = props.getProperty(key); + if (value == null) { + throw new RuntimeException("key " + key + " not found"); + } + ss = value.split("\\s+"); + for (String v : ss) { + values.add(v.trim()); + } + continue; + } + if (pval.startsWith("java.") || pval.startsWith("javax") + || pval.startsWith("com.") || pval.startsWith("org.")) { + nonCorePkgs.add(pval); + } else { + throw new RuntimeException("Invalid non core package: " + pval); + } + } + } finally { + if (reader != null) { + reader.close(); + } + } + } + } +} --- /dev/null Wed Oct 20 09:31:16 2010 +++ new/make/tools/classanalyzer/src/com/sun/classanalyzer/ShowRefs.java Wed Oct 20 09:31:16 2010 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.classanalyzer; + +import java.io.IOException; +import java.util.*; + +/** + * A simple tool to print out the reverse dependencies. + */ +public class ShowRefs { + + static void usage() { + System.out.println("java ShowRefs -classpath classname ...."); + System.out.println("Example usages:"); + System.out.println(" java ShowRefs -classpath Foo.jar com.foo.Foo"); + System.out.println(" java ShowRefs sun.misc.VM"); + System.exit(-1); + } + + public static void main(String[] args) throws IOException { + String cps = null; + List classnames = new ArrayList(); + + int i = 0; + while (i < args.length) { + String arg = args[i++]; + if (arg.equals("-classpath")) { + if (i == args.length) { + System.err.println("Invalid option -classpath"); + usage(); + } + + cps = args[i++]; + } else { + classnames.add(arg); + } + } + + ClassPaths cpaths = cps == null ? + ClassPaths.newJDKClassPaths(System.getProperty("java.home")) : + ClassPaths.newInstance(cps); + cpaths.parse(); + + for (String c : classnames) { + Klass k = Klass.findKlass(c); + if (k == null) { + System.err.println(c + " not found"); + } else { + showRefs(k); + } + } + } + + static void showRefs(Klass k) { + System.out.format("References to %s:%n", k.getClassName()); + for (Klass ref : k.getReferencingClasses()) { + System.out.format(" <- %s%n", ref.getClassName()); + } + System.out.println(); + } + +}