Diff Classes in 2 Jar Files

I had the need to get an overview of the differences between 2 jar files, so I created a simple Ruby script that simply wraps the output of jar, javap, and diff (I’m using Cygwin diff on WinXP).

Here’s jardiff.rb:

#! /usr/bin/ruby

if ARGV.size !=2
  puts “USAGE: ruby jardiff.rb “
  puts “\nNOTE: ‘diff’ command must be on the path and JAVA_HOME environment variable must point to a valid JDK install”
  exit
end

# —————————————————————————–

# check arguments
jar1=ARGV[0].tr(“\\”,“/”)
if (!File.exists? jar1)
 print “#{jar1} can’t be found”
 exit
end

jar2=ARGV[1].tr(“\\”,“/”)
if (!File.exists? jar2)
 print “#{jar2} can’t be found”
 exit
end

# —————————————————————————–

# constants, exec commands : customize, if you wish

WIDTH=100
JAVA_HOME=ENV[“JAVA_HOME”].tr(“\\”,“/”)
JAR_CMD=“#{JAVA_HOME}/bin/jar tf”
JAVAP_CMD=“#{JAVA_HOME}/bin/javap -public”
# diff currently assumed to be on the path
DIFF_CMD=“diff -wBW #{WIDTH}”
HR=“=”*WIDTH

# —————————————————————————–

require “Tempfile”

# Utility functions

# wrapper around diff
def filediff(f1, f2)
  #print “filediff #{f1} #{f2}\n”
  `#{DIFF_CMD} #{f1} #{f2}`
end

# wrapper around jar tf
def get_jar_classes (jar_file)
  `#{JAR_CMD} #{jar_file}`.reject { |v| !v.match(/\.class$/) }.collect { |v| v.strip.tr(“/”,“.”).sub(“.class”, “”) }.sort
end

# wrapper around javap
def javap(jar_file,class_name)
  `#{JAVAP_CMD} -classpath #{jar_file} #{class_name}`
end

# wrapper around Tempfile : returns tempfile or block value
# ? don’t know why Tempfile.open doesn’t return the block value
def tmpfile()
  result = tf = Tempfile.new(“jardiff”)
  if block_given?
    begin
      result = yield tf
    ensure
      tf.close unless tf.nil?
    end
  end
  result
end

# —————————————————————————–

print “Comparing classes in #{jar1} with those in #{jar2}\n”

# examine the jar files, pull out a list of class names from each
classes1 = get_jar_classes jar1
classes2 = get_jar_classes jar2

f1 = tmpfile() { |f| f.puts(classes1.join(“\n”)); f.path }
f2 = tmpfile() { |f| f.puts(classes2.join(“\n”)); f.path }

diff = filediff(f1, f2)
if (diff.strip.length > 0)
  print “#{HR}\n#{“*** Differences ***“.center(WIDTH)}\n#{diff}#{HR}\n”
end

# —————————————————————————–

same_list = (classes1&classes2).sort

print “Comparing method signatures for #{same_list.size} classes\n”

# diff each class that occurs in both jars

same_list.each do |cn|
  f1 = tmpfile() { |f| f.puts javap(jar1, cn); f.path }
  f2 = tmpfile() { |f| f.puts javap(jar2, cn); f.path }
  diff = filediff(f1, f2)
  if (diff.strip.length > 0)
    print “#{HR}\n#{“*** Differences in #{cn} ***”.center(WIDTH)}\n#{diff}#{HR}\n”
  end
end

# —————————————————————————–