=begin -------------------------------------------------------------------------------- This script will try to show that the JARs in a list are not needed for VIVO (or Vitro). Given a list of JARs, it will determine what packages the JAR contains, and will search the source files of the project for references to those packages. The list of JARs should be created by running the jarlist target of the build file. That will use JarJar to create a list of JAR file that are not directly referenced by the classes in the build. But what about JARs that are needed by a call using reflection? Like Class.forName("this.that.theOther"). We know this is how the MySQL driver is loaded (so we need mysql-connector-java-5.1.16-bin.jar). Are there any others? This script will try to find them. The jarlist target includes a list of JARs that we know are needed, such as the MySQL connector. If this script finds any JARs that are needed, they should be added to that list, and the target run again, in case they depend on other JARs in turn. -------------------------------------------------------------------------------- One of the tricks that this script uses is to prune the list of packages to search for. If a JAR contains both "this.that.other" and "this.that", then we only need to search for "this.that", since it will reveal uses of "this.that.other" as well. -------------------------------------------------------------------------------- pass the name of the file that contains the JAR names pass the root directory of the combined vitro/vivo distro (appbase) -------------------------------------------------------------------------------- Search all of the *.java, *.jsp, *.js, *.tld, *.xml files for mention of these package names For each hit, print the file path, line number, the package name and the JAR name. To search files: find -X . -name \*.db -or -name \*pot | xargs grep 'org\.package\.name' grep -H -n -f file_with_package_patterns or can we do this all with grep on each string? grep -r --include=*.jsp grep -H -n -r --include=*.javaxd --include=*.jsp org\.package\.name . and precede the output with a header that lists the package -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- =end # Just say what we're doing. # def print_args puts "Scanning for JAR usage." puts "Base directory is #{@scan_base_directory}" puts "JAR list is in #{@jar_names_file}" puts end # Build a Hash of JAR names mapped to (reduced) arrays of package names. # def figure_package_names_from_jars @packages_for_jars = {} File.open(@jar_names_file) do |file| file.each do |line| jar_name = line.strip @packages_for_jars[jar_name] = figure_package_names_for_jar(jar_name) end end end # Figure out the reduced set of package names for this JAR # def figure_package_names_for_jar(jar_name) jar_path = "#{@scan_base_directory}/lib/#{jar_name}" jar_contents = `jar -tf '#{jar_path}'` packages = analyze_jar_contents(jar_contents) reduced = reduce_package_names(packages) end # Ask the JAR what it contains. Keep only the names of populated packages. # Ignore packages that are not at least 2 levels deep. # def analyze_jar_contents(jar_contents) packages = [] jar_contents.lines do |line| line.strip! if line.end_with?('/') elsif line.start_with?('META-INF') elsif line.count('/') < 2 else package = line[0...line.rindex('/')].tr('/', '.') packages << package end end packages.uniq.sort! end # Remove the names of any sub-packages. Searching for the parent package will be sufficient. # def reduce_package_names(packages) reduced = [] packages.each do |candidate| redundant = FALSE reduced.each do |result| redundant = TRUE if candidate.start_with?(result) end reduced << candidate unless redundant end reduced end # Show what packages we will search for, and for which JAR # def print_package_names_for_jars puts "Packages for each jar" @packages_for_jars.each do |jar_name, package_array| puts " #{jar_name}" package_array.each do |package_name| puts " #{package_name}" end puts end end # # def show_packages_in_source_files @packages_for_jars.each do |jar_name, package_array| show_packages_for_jar_in_source_files(jar_name, package_array) end end # # def show_packages_for_jar_in_source_files(jar_name, package_array) puts "------------------------------- #{jar_name} ------------------------------" package_array.each do |package_name| show_package_in_source_files(package_name) end end # # def show_package_in_source_files(package_name) puts "#{package_name}" include_parms = build_include_parms(["*.java", "*.jsp", "*.xml", "*.tld", "*.js" ]) package_name_pattern = package_name.sub(/\./, "\\.") system "grep -H -n -r #{include_parms} #{package_name_pattern} '#{@scan_base_directory}'" puts end # # def build_include_parms(file_specs) "--include=" + file_specs.join(" --include=") end # # # ------------------------------------------------------------------------------------ # Standalone calling. # # Do this if this program was called from the command line. That is, if the command # expands to the path of this file. # ------------------------------------------------------------------------------------ # if File.expand_path($0) == File.expand_path(__FILE__) if ARGV.length != 2 raise("usage is: ruby jarUsageScanner.rb ") end @jar_names_file, @scan_base_directory = ARGV if !File.file?(@jar_names_file) raise "File does not exist: '#{@jar_names_file}'." end if !File.directory?(@scan_base_directory) raise "Directory does not exist: '#{@scan_base_directory}'." end print_args figure_package_names_from_jars print_package_names_for_jars show_packages_in_source_files end