jartools 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in jartools.gemspec
4
+ gemspec
@@ -0,0 +1,66 @@
1
+ jartools
2
+ ========
3
+
4
+ `jartools` is a command-line utility for examining java archives (JARs
5
+ and WARs). It is a complement to (not a replacement for) the standard
6
+ `jar` utility.
7
+
8
+ It is designed to use streams in the standard unix style so it can be
9
+ composed with other stream-processing tools (e.g., `grep`, `xargs`).
10
+
11
+ Installing `jartools`
12
+ ---------------------
13
+
14
+ `jartools` is distributed as a rubygem. Install it like so:
15
+
16
+ $ gem install jartools
17
+
18
+ Depending on how your ruby is installed, you may need root privileges
19
+ to do this.
20
+
21
+ It's been tested on Ruby 1.8.7, JRuby 1.5.6, and Ruby 1.9.2.
22
+
23
+ Using `jartools`
24
+ ----------------
25
+
26
+ To see what tools are included in the version you have installed, you
27
+ can do this:
28
+
29
+ $ jartools help
30
+
31
+ To get details on a particular tool, use, e.g.:
32
+
33
+ $ jartools help packages
34
+
35
+ This online help tells you what arguments may be passed to each command.
36
+
37
+ Tools included
38
+ --------------
39
+
40
+ ### packages
41
+
42
+ Lists all the packages present in a JAR.
43
+
44
+ ### manifest
45
+
46
+ Prints the JAR's manifest (if any) to standard out.
47
+
48
+ ### diff
49
+
50
+ Does a diff of two JARs or WARs, including file content diffs and
51
+ recursive diffs of contained JARs.
52
+
53
+ Project links
54
+ -------------
55
+
56
+ * [Continuous integration](https://ctms-ci.nubic.northwestern.edu/hudson/job/jartools/)
57
+ * [Issue tracking](http://github.com/rsutphin/jartools/issues)
58
+
59
+ Non-issue questions can be sent to rhett@detailedbalance.net.
60
+
61
+ About
62
+ -----
63
+
64
+ `jartools` is copyright 2010 Rhett Sutphin. It was built at [NUBIC][].
65
+
66
+ [NUBIC]: http://www.nucats.northwestern.edu/centers/nubic
@@ -0,0 +1,18 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+ require 'ci/reporter/rake/rspec'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ desc "Run all specs"
8
+ RSpec::Core::RakeTask.new('spec') do |t|
9
+ t.pattern = 'spec/**/*_spec.rb'
10
+ t.verbose = true
11
+ end
12
+
13
+ namespace :ci do
14
+ ENV["CI_REPORTS"] = "reports/spec-xml"
15
+
16
+ desc "Run specs for CI"
17
+ task :spec => ['ci:setup:rspec', 'rake:spec']
18
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jartools'
4
+
5
+ begin
6
+ JarTools::CLI.start
7
+ rescue Interrupt => e
8
+ $stderr.puts "Interrupted"
9
+ exit 1
10
+ end
@@ -0,0 +1,5 @@
1
+ module JarTools
2
+ autoload :CLI, 'jartools/cli'
3
+ autoload :Diff, 'jartools/diff'
4
+ autoload :VERSION, 'jartools/version'
5
+ end
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'jartools'
3
+ require 'thor'
4
+
5
+ require 'zip/zipfilesystem'
6
+
7
+ module JarTools
8
+ class CLI < Thor
9
+ desc "packages JARFILE", "Print the java packages found in the JAR to standard out"
10
+ long_desc <<-DESC
11
+ Prints all the java packages found in the specified JAR to standard out, one per line.
12
+ "Packages" are defined as unique paths in the JAR which contain at least one class.
13
+ DESC
14
+ def packages(jarfile)
15
+ entry_names = []
16
+ Zip::ZipFile.foreach(jarfile) { |entry|
17
+ entry_names << entry.name
18
+ }
19
+
20
+ entry_names.select { |e| e =~ /\.class$/ }.
21
+ collect { |e| e.sub(/\/[^\/]+$/, '').gsub('/', '.') }.
22
+ uniq.sort.each { |pkg| say pkg }
23
+ end
24
+
25
+ desc "manifest JARFILE", "Print the manifest found in the JAR to standard out"
26
+ long_desc <<-DESC
27
+ Prints the META-INF/MANIFEST.MF if one is found in the JAR. To ease examination,
28
+ lines are unwrapped. Use --raw to get the original manifest with wrapped lines.
29
+ DESC
30
+ method_option :raw, :type => :boolean,
31
+ :desc => "Print the manifest exactly as it appears in the JAR."
32
+ def manifest(jarfile)
33
+ content = Zip::ZipFile.open(jarfile) { |zip|
34
+ zip.file.read("META-INF/MANIFEST.MF") if zip.file.exists?("META-INF/MANIFEST.MF")
35
+ }
36
+ return unless content
37
+ if options.raw
38
+ say content
39
+ else
40
+ say content.gsub("\r\n", "\n").gsub("\r", "\n").split("\n").inject([]) { |result, raw_line|
41
+ if raw_line =~ /^\s/
42
+ result[-1] += raw_line.slice(1, raw_line.size)
43
+ else
44
+ result << raw_line
45
+ end
46
+ result
47
+ }.join("\n")
48
+ end
49
+ end
50
+
51
+ desc "diff JARFILE1 JARFILE2", "Recursively find differences in two JARs or WARs"
52
+ def diff(jar1, jar2)
53
+ Diff.diff(jar1, jar2)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,237 @@
1
+ require 'jartools'
2
+
3
+ require 'diff/lcs'
4
+ require 'diff/lcs/hunk'
5
+ require 'digest/sha1'
6
+
7
+ require 'tempfile'
8
+ require 'zip/zip'
9
+
10
+ module JarTools
11
+ module Diff
12
+ def self.diff(jar1, jar2)
13
+ self.compare(ActualFile.new(jar1), ActualFile.new(jar2), [])
14
+ end
15
+
16
+ private
17
+
18
+ ##
19
+ # @param [ActualFile,ExtractedFile] jar1
20
+ # @param [ActualFile,ExtractedFile] jar2
21
+ # @param [Array<String>] container_files
22
+ def self.compare(jar1, jar2, container_files = [], roots = [])
23
+ # skip check if the files are identical
24
+ return if jar1.sha1 == jar2.sha1
25
+ roots = [jar1, jar2] if roots.empty?
26
+
27
+ entries1 = EntryIterator.new(jar1, container_files)
28
+ entries2 = EntryIterator.new(jar2, container_files)
29
+
30
+ until entries1.exhausted? && entries2.exhausted?
31
+ if entries2.exhausted? || (!entries1.exhausted? && entries1.current < entries2.current)
32
+ puts missing_file_message(entries1.current.display_name, roots.first, roots.last)
33
+ entries1.advance
34
+ elsif entries1.exhausted? || entries1.current > entries2.current
35
+ puts missing_file_message(entries2.current.display_name, roots.last, roots.first)
36
+ entries2.advance
37
+ else
38
+ # at this point, the entry names are identical so we can
39
+ # examine just one
40
+ if entries1.current.archive?
41
+ self.compare(entries1.current.extract, entries2.current.extract,
42
+ container_files + [entries1.current.path], roots)
43
+ elsif entries1.current.extract.sha1 != entries2.current.extract.sha1
44
+ if entries1.current.binary? || entries2.current.binary?
45
+ puts mismatched_binary_file_message(entries1.current.display_name)
46
+ else
47
+ puts mismatched_text_file_message(entries1.current.display_name,
48
+ entries1.current.extract, entries2.current.extract)
49
+ end
50
+ else
51
+ # identical in both
52
+ end
53
+
54
+ entries1.advance
55
+ entries2.advance
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.missing_file_message(display_name, is_in, is_not_in)
61
+ "#{display_name} is in #{is_in.readable_filename} but not in #{is_not_in.readable_filename}"
62
+ end
63
+
64
+ def self.mismatched_binary_file_message(display_name)
65
+ "#{display_name} differs"
66
+ end
67
+
68
+ def self.mismatched_text_file_message(display_name, one, two)
69
+ diff = TextDiff.new(one.contents, two.contents)
70
+ "#{display_name} text differs:\n#{diff.text_description}\n"
71
+ end
72
+
73
+ class EntryIterator
74
+ attr_accessor :index
75
+
76
+ def initialize(jarfile, container_files)
77
+ @jarfile = jarfile
78
+ @entries = read_directory
79
+ @container_files = container_files
80
+ @index = 0
81
+ @current_entry_cache = nil
82
+ end
83
+
84
+ def current
85
+ unless exhausted?
86
+ @current_entry_cache ||=
87
+ AnalyzableEntry.new(@jarfile, @entries[index], @container_files)
88
+ end
89
+ end
90
+
91
+ def exhausted?
92
+ index >= @entries.size
93
+ end
94
+
95
+ def advance
96
+ @index += 1
97
+ @current_entry_cache = nil
98
+ end
99
+
100
+ private
101
+
102
+ def read_directory
103
+ entries = []
104
+ Zip::ZipFile.foreach(@jarfile.readable_filename) do |ze|
105
+ entries << ze.name
106
+ end
107
+ entries.sort
108
+ end
109
+
110
+ class AnalyzableEntry
111
+ include Comparable
112
+
113
+ attr_reader :path
114
+
115
+ def initialize(jarfile, path, container_files)
116
+ @jarfile = jarfile
117
+ @path = path
118
+ @container_files = container_files
119
+ end
120
+
121
+ def display_name
122
+ (@container_files + [path]).join(" | ")
123
+ end
124
+
125
+ def extract
126
+ @extract ||= Zip::ZipFile.open(@jarfile.readable_filename) do |z|
127
+ ExtractedFile.new(z.get_entry(path))
128
+ end
129
+ end
130
+
131
+ def archive?
132
+ path =~ /(zip|jar|war)$/
133
+ end
134
+
135
+ # This logic is odd, but it's what grep does, apparently
136
+ def binary?
137
+ extract.contents.match("\000")
138
+ end
139
+
140
+ def <=>(other)
141
+ path <=> other.path
142
+ end
143
+ end
144
+ end
145
+
146
+ class UniformFile
147
+ def contents
148
+ @contents ||= if RUBY_VERSION =~ /1.8/
149
+ File.read(readable_filename)
150
+ else
151
+ File.read(readable_filename, :encoding => 'binary')
152
+ end
153
+ end
154
+
155
+ def sha1
156
+ @sha1 ||= Digest::SHA1.hexdigest(contents)
157
+ end
158
+ end
159
+
160
+ class ExtractedFile < UniformFile
161
+ attr_reader :entry_path
162
+
163
+ def initialize(zip_entry)
164
+ @entry_path = zip_entry.name
165
+ @temp_file = Tempfile.new(File.basename(@entry_path))
166
+ File.open(@temp_file.path, 'w') do |f|
167
+ zip_entry.get_input_stream do |ze|
168
+ f.write ze.read
169
+ end
170
+ end
171
+ end
172
+
173
+ def readable_filename
174
+ @temp_file.path
175
+ end
176
+ end
177
+
178
+ class ActualFile < UniformFile
179
+ attr_reader :entry_path, :readable_filename
180
+
181
+ def initialize(filename)
182
+ @entry_path = @readable_filename = filename
183
+ end
184
+ end
185
+
186
+ class TextDiff
187
+ def initialize(a, b)
188
+ @s1 = a
189
+ @s2 = b
190
+ end
191
+
192
+ def different?
193
+ @s1 != @s2
194
+ end
195
+
196
+ def text_description
197
+ printable_diff if different?
198
+ end
199
+
200
+ private
201
+
202
+ # Adapted from Diff::LCS::Ldiff by way of Rspec::Expectations::Differ
203
+ def printable_diff
204
+ data_old = @s1.split(/\n/).map! { |e| e.chomp }
205
+ data_new = @s2.split(/\n/).map! { |e| e.chomp }
206
+ output = ""
207
+ diffs = ::Diff::LCS.diff(data_old, data_new)
208
+ return output if diffs.empty?
209
+ oldhunk = hunk = nil
210
+ file_length_difference = 0
211
+ diffs.each do |piece|
212
+ begin
213
+ hunk = ::Diff::LCS::Hunk.new(
214
+ data_old, data_new, piece, 3, file_length_difference
215
+ )
216
+ file_length_difference = hunk.file_length_difference
217
+ next unless oldhunk
218
+ # Hunks may overlap, which is why we need to be careful when our
219
+ # diff includes lines of context. Otherwise, we might print
220
+ # redundant lines.
221
+ if hunk.overlaps?(oldhunk)
222
+ hunk.unshift(oldhunk)
223
+ else
224
+ output << oldhunk.diff(:unified)
225
+ end
226
+ ensure
227
+ oldhunk = hunk
228
+ output << "\n"
229
+ end
230
+ end
231
+ #Handle the last remaining hunk
232
+ output << oldhunk.diff(:unified) << "\n"
233
+ output.lstrip
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,3 @@
1
+ module JarTools
2
+ VERSION = "0.0.0"
3
+ end
@@ -0,0 +1,70 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe JarTools::CLI do
4
+ def cli_lines(*args)
5
+ capture_stdout do
6
+ JarTools::CLI.start(args)
7
+ end.gsub("\r\n", "\n").split(/\n/)
8
+ end
9
+
10
+ describe "#packages" do
11
+ before do
12
+ @result = cli_lines("packages", sample_jar("slf4j-api-1.6.0.jar"))
13
+ end
14
+
15
+ %w(org.slf4j org.slf4j.spi org.slf4j.helpers).each do |expected_pkg|
16
+ it "lists all the packages, including #{expected_pkg}" do
17
+ @result.should include(expected_pkg)
18
+ end
19
+ end
20
+
21
+ it "does not list things which are not packages" do
22
+ @result.should_not include "META-INF"
23
+ end
24
+ end
25
+
26
+ describe "#manifest" do
27
+ describe "by default" do
28
+ before do
29
+ @result = cli_lines("manifest", sample_jar("slf4j-api-1.6.0.jar"))
30
+ end
31
+
32
+ it "prints the whole manifest" do
33
+ @result.should have(16).lines
34
+ end
35
+
36
+ it "unwraps hard-wrapped lines" do
37
+ @result.find { |l| l =~ /^Export-Package/ }.should ==
38
+ "Export-Package: org.slf4j;version=1.6.0, org.slf4j.spi;version=1.6.0, org.slf4j.helpers;version=1.6.0"
39
+ end
40
+ end
41
+
42
+ describe "--raw" do
43
+ before do
44
+ @result = cli_lines("manifest", sample_jar("slf4j-api-1.6.0.jar"), "--raw")
45
+ end
46
+
47
+ it "prints the raw manifest" do
48
+ @result.should have(17).lines
49
+ end
50
+
51
+ it "leaves wrapped lines alone" do
52
+ @result.find { |l| l =~ /^Export-Package/ }.should ==
53
+ "Export-Package: org.slf4j;version=1.6.0, org.slf4j.spi;version=1.6.0, "
54
+ end
55
+ end
56
+
57
+ describe "on a JAR without a manifest" do
58
+ it "prints nothing" do
59
+ cli_lines("manifest", sample_jar("empty.jar")).should == []
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "#diff" do
65
+ it "prints nothing for the same jar" do
66
+ cli_lines("diff", sample_jar("slf4j-api-1.6.0.jar"), sample_jar("slf4j-api-1.6.0.jar")).
67
+ should == []
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,133 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe JarTools::Diff do
4
+ it "reports no differences for the same JAR" do
5
+ same = sample_jar('slf4j-api-1.6.0.jar')
6
+ capture_stdout { JarTools::Diff.diff(same, same) }.should == ""
7
+ end
8
+
9
+ describe "with a text file difference" do
10
+ before do
11
+ tmpdir("sub")
12
+ tmpfile("A.txt", "Description\nFirst Letter")
13
+ tmpfile("sub/B.txt", "Description\nSecond letter")
14
+ jar1 = tmpjar("one.jar", "A.txt", "sub/B.txt")
15
+ tmpfile("sub/B.txt", "Description\n2nd letter")
16
+ jar2 = tmpjar("two.jar", "A.txt", "sub/B.txt")
17
+
18
+ @actual = capture_stdout { JarTools::Diff.diff(jar1, jar2) }
19
+ end
20
+
21
+ it "should note the difference in its text description" do
22
+ @actual.should == <<-EXPECTED
23
+ sub/B.txt text differs:
24
+ @@ -1,3 +1,3 @@
25
+ Description
26
+ -Second letter
27
+ +2nd letter
28
+
29
+ EXPECTED
30
+ end
31
+ end
32
+
33
+ describe "with a classfile difference" do
34
+ before do
35
+ tmpfile("A.java", "public class A { }");
36
+ cd(tmpdir) { system("javac A.java") }
37
+ jar1 = tmpjar("one.jar", "A.class")
38
+
39
+ tmpfile("A.java", %q(public class A { public static final String N = "A"; }))
40
+ cd(tmpdir) { system("javac A.java") }
41
+ jar2 = tmpjar("two.jar", "A.class")
42
+
43
+ @actual = capture_stdout { JarTools::Diff.diff(jar1, jar2) }
44
+ end
45
+
46
+ it "should not include a detailed diff" do
47
+ @actual.should == <<-EXPECTED
48
+ A.class differs
49
+ EXPECTED
50
+ end
51
+ end
52
+
53
+ describe "with files present in one jar but not the other" do
54
+ before do
55
+ tmpdir("sub")
56
+ tmpfile("A.txt", "Description\nFirst Letter")
57
+ tmpfile("sub/B.txt", "Description\nSecond letter")
58
+ tmpfile("sub/C.txt", "Description\Third letter")
59
+ jar1 = tmpjar("one.jar", "sub/B.txt", "sub/C.txt")
60
+ jar2 = tmpjar("two.jar", "sub/C.txt", "A.txt")
61
+
62
+ cd(tmpdir) do
63
+ @actual = capture_stdout { JarTools::Diff.diff("one.jar", "two.jar") }
64
+ end
65
+ end
66
+
67
+ it "should have an appropriate text description" do
68
+ @actual.should == <<-EXPECTED
69
+ A.txt is in two.jar but not in one.jar
70
+ sub/B.txt is in one.jar but not in two.jar
71
+ EXPECTED
72
+ end
73
+ end
74
+
75
+ describe "with a nested JAR difference" do
76
+ before do
77
+ tmpfile("same.txt", "No changes in here")
78
+ tmpfile("altered.txt", "V1")
79
+ tmpjar("nested.jar", "altered.txt")
80
+ jar1 = tmpjar("main1.jar", "nested.jar", "same.txt")
81
+
82
+ tmpfile("altered.txt", "V2")
83
+ tmpfile("extra.txt", "Not in that other one")
84
+ tmpjar("nested.jar", "altered.txt", "extra.txt")
85
+ jar2 = tmpjar("main2.jar", "nested.jar", "same.txt")
86
+
87
+ cd(tmpdir) do
88
+ @actual = capture_stdout { JarTools::Diff.diff("main1.jar", "main2.jar") }
89
+ end
90
+ end
91
+
92
+ it "should output the details of the difference" do
93
+ @actual.should == <<-EXPECTED
94
+ nested.jar | altered.txt text differs:
95
+ @@ -1,2 +1,2 @@
96
+ -V1
97
+ +V2
98
+
99
+ nested.jar | extra.txt is in main2.jar but not in main1.jar
100
+ EXPECTED
101
+ end
102
+ end
103
+
104
+ module JarTools::Diff
105
+ describe TextDiff do
106
+ describe "#different?" do
107
+ it "is true for different values" do
108
+ TextDiff.new("A", "B").should be_different
109
+ end
110
+
111
+ it "is false for identical values" do
112
+ TextDiff.new("C", "C").should_not be_different
113
+ end
114
+ end
115
+
116
+ describe "#text_description" do
117
+ it "is nil for no differences" do
118
+ TextDiff.new("ABC", "ABC").text_description.should be_nil
119
+ end
120
+
121
+ it "gives diff-style output for differences" do
122
+ TextDiff.new("A\nb\nC", "A\nB\nC").text_description.should == <<-EXPECTED
123
+ @@ -1,4 +1,4 @@
124
+ A
125
+ -b
126
+ +B
127
+ C
128
+ EXPECTED
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
Binary file
@@ -0,0 +1,70 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ require 'rspec'
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
7
+
8
+ require 'jartools'
9
+ require 'fileutils'
10
+
11
+ RSpec.configure do |config|
12
+ config.include FileUtils
13
+
14
+ config.after do
15
+ if @tmpdir
16
+ rm_r @tmpdir
17
+ end
18
+ end
19
+
20
+ ##
21
+ # Returns the path to a JAR from the samples directory
22
+ def sample_jar(name)
23
+ File.join(File.expand_path("../samples", __FILE__), name)
24
+ end
25
+
26
+ ##
27
+ # Captures everything printed to stdout during the block
28
+ # and returns it as a string.
29
+ def capture_stdout
30
+ old_stdout, $stdout = $stdout, StringIO.new
31
+ begin
32
+ yield
33
+ $stdout.string
34
+ ensure
35
+ $stdout = old_stdout
36
+ end
37
+ end
38
+
39
+ def tmpdir(sub=nil)
40
+ @tmpdir ||= begin
41
+ dirname = File.expand_path("../tmp", __FILE__)
42
+ mkdir_p dirname
43
+ dirname
44
+ end
45
+ if sub
46
+ full = File.join(@tmpdir, sub)
47
+ mkdir_p full
48
+ full
49
+ else
50
+ @tmpdir
51
+ end
52
+ end
53
+
54
+ def tmpfile(basename, contents=nil)
55
+ full_path = File.join(tmpdir, basename)
56
+ File.open(full_path, 'w') do |f|
57
+ f.write(contents) if contents
58
+ end
59
+ full_path
60
+ end
61
+
62
+ def tmpjar(basename, *files)
63
+ full_path = File.join(tmpdir, basename)
64
+ cd tmpdir do
65
+ cmd = "jar Mcf '#{full_path}' '#{files.join("' '")}'"
66
+ `#{cmd}`
67
+ end
68
+ full_path
69
+ end
70
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jartools
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 0
10
+ version: 0.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Rhett Sutphin
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-04 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thor
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 39
30
+ segments:
31
+ - 0
32
+ - 14
33
+ - 0
34
+ version: 0.14.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rubyzip
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - "="
44
+ - !ruby/object:Gem::Version
45
+ hash: 51
46
+ segments:
47
+ - 0
48
+ - 9
49
+ - 4
50
+ version: 0.9.4
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rspec
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 5
62
+ segments:
63
+ - 2
64
+ - 3
65
+ version: "2.3"
66
+ type: :development
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: ci_reporter
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 1
79
+ - 6
80
+ version: "1.6"
81
+ type: :development
82
+ version_requirements: *id004
83
+ description: |-
84
+ jartools provides a busybox of command-line tools
85
+ for examining the contents and metadata of java archive files (JARs and WARs).
86
+ email:
87
+ - rhett@detailedbalance.net
88
+ executables:
89
+ - jartools
90
+ extensions: []
91
+
92
+ extra_rdoc_files: []
93
+
94
+ files:
95
+ - lib/jartools/cli.rb
96
+ - lib/jartools/diff.rb
97
+ - lib/jartools/version.rb
98
+ - lib/jartools.rb
99
+ - spec/jartools/cli_spec.rb
100
+ - spec/jartools/diff_spec.rb
101
+ - spec/samples/empty.jar
102
+ - spec/samples/slf4j-api-1.6.0.jar
103
+ - spec/spec_helper.rb
104
+ - bin/jartools
105
+ - README.md
106
+ - Rakefile
107
+ - Gemfile
108
+ has_rdoc: true
109
+ homepage: http://github.com/rsutphin/jartools
110
+ licenses: []
111
+
112
+ post_install_message:
113
+ rdoc_options: []
114
+
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ hash: 3
123
+ segments:
124
+ - 0
125
+ version: "0"
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ hash: 3
132
+ segments:
133
+ - 0
134
+ version: "0"
135
+ requirements: []
136
+
137
+ rubyforge_project:
138
+ rubygems_version: 1.3.7
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: A set of command line tools for looking through java archives (JARs and WARs)
142
+ test_files:
143
+ - spec/jartools/cli_spec.rb
144
+ - spec/jartools/diff_spec.rb
145
+ - spec/samples/empty.jar
146
+ - spec/samples/slf4j-api-1.6.0.jar
147
+ - spec/spec_helper.rb