pangolin 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,179 @@
1
+ require 'java'
2
+
3
+
4
+ module Pangolin
5
+
6
+ include_class 'java.io.FileOutputStream'
7
+ include_class 'java.util.zip.ZipOutputStream'
8
+ include_class 'java.util.zip.ZipEntry'
9
+
10
+
11
+ class Jar
12
+
13
+ # The path to the directory which should be considered the root of the archive.
14
+ #
15
+ # If you set +base_dir+ to "build" and add the file "build/Main.class", that file
16
+ # will be put in the archive as "Main.class".
17
+ attr_accessor :base_dir
18
+
19
+ # The ZIP compression rate from 0 (no compression) to 9 (maximal compression)
20
+ attr_accessor :compression
21
+
22
+ # Whether or not to print the equivalent command string to the output (see #execute)
23
+ attr_accessor :verbose
24
+
25
+ # The path to the JAR file that will be generated
26
+ attr_reader :output
27
+
28
+
29
+ def initialize( output, files = nil, base_dir = nil )
30
+ @output = output
31
+ @base_dir = base_dir
32
+ @entries = { } # archive_path => IO
33
+ @verbose = false
34
+ @compression = 1
35
+
36
+ self.manifest = { }
37
+
38
+ add_files(files) unless files.nil? || files.empty?
39
+ end
40
+
41
+ # Sets the attributes that will end up in the JAR manifest.
42
+ #
43
+ # Attribute names are treated case insensitively, so setting
44
+ # both Main-Class and main-class will result in only one being used.
45
+ #
46
+ # Will raise an error on malformed attribute names (must start with a
47
+ # letter or digit and contain only letter, digits, dashes and underscores).
48
+ #
49
+ # The manifest you set will be merged with a set of default attributes (but
50
+ # yours will override).
51
+ def manifest=( manifest_hash )
52
+ @manifest = default_manifest
53
+
54
+ manifest_hash.each do |key, value|
55
+ raise ArgumentError, "Malformed attribute name #{key}" if key !~ /^[\w\d][\w\d\-_]*$/
56
+
57
+ remove_manifest_attribute(key)
58
+
59
+ @manifest[key] = value
60
+ end
61
+ end
62
+
63
+ # Removes a manifest attribute, the comparison is case insensitive.
64
+ def remove_manifest_attribute( name ) # :nodoc:
65
+ @manifest.delete_if do |key, value|
66
+ key.downcase == name.downcase
67
+ end
68
+ end
69
+
70
+ # Convenience method for setting the Main-Class manifest attribute
71
+ def main_class=( class_name )
72
+ if class_name
73
+ @manifest['Main-Class'] = class_name
74
+ else
75
+ remove_manifest_attribute('Main-Class')
76
+ end
77
+ end
78
+
79
+ def default_manifest # :nodoc:
80
+ {
81
+ 'Built-By' => "Pangolin v#{Pangolin::version}",
82
+ 'Manifest-Version' => '1.0'
83
+ }
84
+ end
85
+
86
+ def manifest_string # :nodoc:
87
+ @manifest.keys.inject("") do |str, key|
88
+ str + "#{key}: #{@manifest[key]}\n"
89
+ end
90
+ end
91
+
92
+ def commit_manifest # :nodoc:
93
+ add_blob(manifest_string, 'META-INF/MANIFEST.MF')
94
+ end
95
+
96
+ # Adds the file at +file_path+ to the archive and put it at +archive_path+
97
+ # (the same as +file_path+ by default) inside the archive.
98
+ def add_file( file_path, archive_path = nil )
99
+ archive_path = find_archive_path(file_path, @base_dir) unless archive_path
100
+
101
+ if File.directory?(file_path)
102
+ raise ArgumentError, "\"#{file_path}\" is a directory"
103
+ elsif ! File.exists?(file_path)
104
+ raise ArgumentError, "\"#{file_path}\" does not exist"
105
+ end
106
+
107
+ @entries[archive_path || file_path] = File.new(file_path)
108
+ end
109
+
110
+ # Adds a list of files to the archive, at paths relative to +base_dir+
111
+ # (defaults to #base_dir) inside the archive.
112
+ def add_files( files, base_dir = nil )
113
+ files.each do |file|
114
+ add_file(file, find_archive_path(file, base_dir || @base_dir))
115
+ end
116
+ end
117
+
118
+ # Adds a string to the archive at +archive_path+.
119
+ def add_blob( str, archive_path )
120
+ @entries[archive_path] = StringIO.new(str)
121
+ end
122
+
123
+ def remove_entry( archive_path ) # :nodoc:
124
+ @entries.delete(archive_path)
125
+ end
126
+
127
+ def entries # :nodoc:
128
+ @entries.keys
129
+ end
130
+
131
+ # Creates the archive. If #verbose is true the equivalent command string
132
+ # for the +jar+ command will be printed to the stream passed as +io+ (or
133
+ # +$stdout+ by default)
134
+ def execute( io = $stderr )
135
+ raise "Output not set" unless @output
136
+
137
+ compression_flag = @compression == 0 ? '0' : ''
138
+
139
+ io.puts 'jar …' if @verbose
140
+
141
+ commit_manifest
142
+ create_zipfile
143
+
144
+ true
145
+ end
146
+
147
+ def create_zipfile # :nodoc:
148
+ buffer_size = 65536
149
+
150
+ zipstream = ZipOutputStream.new(FileOutputStream.new(@output))
151
+
152
+ @entries.each do |path, io|
153
+ while buffer = io.read(buffer_size)
154
+ zipstream.put_next_entry(ZipEntry.new(path))
155
+ zipstream.write(buffer.to_java_bytes, 0, buffer.length)
156
+ zipstream.close_entry
157
+ end
158
+
159
+ io.close
160
+ end
161
+
162
+ zipstream.close
163
+ end
164
+
165
+ def find_archive_path( path, base_dir ) # :nodoc:
166
+ if base_dir
167
+ prefix = base_dir + (base_dir =~ /\/$/ ? '' : '/')
168
+
169
+ if 0 == path.index(prefix)
170
+ return path.slice(prefix.length, path.length - prefix.length)
171
+ end
172
+ end
173
+
174
+ path
175
+ end
176
+
177
+ end
178
+
179
+ end
@@ -0,0 +1,128 @@
1
+ require 'java'
2
+
3
+
4
+ module Pangolin
5
+
6
+ # must use include_class instead of import because import interferes with Rake's import
7
+ include_class 'java.io.PrintWriter'
8
+ include_class 'java.io.StringWriter'
9
+
10
+
11
+ class Javac
12
+
13
+ # The files to compile.
14
+ attr_accessor :source_files
15
+
16
+ # Additional directories where source files can be found.
17
+ attr_accessor :source_path
18
+
19
+ # The directory where the generated class files should be written, defaults to "build"
20
+ attr_accessor :destination
21
+
22
+ # The compilation class path
23
+ attr_accessor :class_path
24
+
25
+ # Show deprecation warnings, true by default
26
+ attr_accessor :deprecation_warnings
27
+
28
+ # Generate warnings, true by default
29
+ attr_accessor :warnings
30
+
31
+ # The encoding of the source files
32
+ attr_accessor :encoding
33
+
34
+ # Whether or not to print the equivalent command string to the output (see #execute)
35
+ attr_accessor :verbose
36
+
37
+ # Enable or diable specific warnings by adding their names to this list.
38
+ # The possible values are:
39
+ # * all
40
+ # * cast
41
+ # * deprecation
42
+ # * divzero
43
+ # * empty
44
+ # * unchecked
45
+ # * fallthrough
46
+ # * path
47
+ # * serial
48
+ # * finally
49
+ # * overrides
50
+ # * none
51
+ # Prefix with '-' to disable.
52
+ attr_accessor :lint
53
+
54
+ # Determines the maximum number of errors to print, default (nil) is to print all
55
+ attr_accessor :max_errors
56
+
57
+ # Determines the maximum number of warnings to print, default (nil) is to print all
58
+ attr_accessor :max_warnings
59
+
60
+
61
+ def initialize( *source_files )
62
+ @source_files = source_files || [ ]
63
+
64
+ @source_path = [ ]
65
+ @destination = 'build'
66
+ @class_path = [ ]
67
+ @deprecation_warnings = true
68
+ @warnings = true
69
+ @encoding = nil
70
+ @verbose = false
71
+ @lint = [ ]
72
+ end
73
+
74
+ # Run javac. If #verbose is true the equivalent command string for
75
+ # the +javac+ command will be printed to the stream passed as +io+ (or
76
+ # +$stdout+ by default)
77
+ def execute( io = $stderr )
78
+ output_writer = StringWriter.new
79
+
80
+ io.puts 'javac …' if @verbose
81
+
82
+ result = execute_compiler(output_writer)
83
+
84
+ output_str = output_writer.to_s
85
+
86
+ io.puts output_str if output_str !~ /^\s*$/
87
+
88
+ if 0 == result
89
+ true
90
+ else
91
+ false
92
+ end
93
+ end
94
+
95
+ def command_args # :nodoc:
96
+ args = [ ]
97
+ args << '-sourcepath' << formatted_path(@source_path) unless @source_path.empty?
98
+ args << '-d' << @destination unless (@destination.nil? || @destination =~ /^\s*$/)
99
+ args << '-classpath' << formatted_path(@class_path) unless @class_path.empty?
100
+ args << '-deprecation' if @deprecation_warnings
101
+ args << '-nowarn' unless @warnings
102
+ args << '-encoding' << @encoding if @encoding
103
+ args << '-Xmaxerrs' << @max_errors if @max_errors
104
+ args << '-Xmaxwarns' << @max_warnings if @max_warnings
105
+ args + lint_flags + @source_files
106
+ end
107
+
108
+ private
109
+
110
+ def lint_flags
111
+ @lint.map { |lint| '-Xlint:' + lint }
112
+ end
113
+
114
+ def execute_compiler(output_writer)
115
+ com.sun.tools.javac.Main.compile(command_args.to_java(java.lang.String), PrintWriter.new(output_writer))
116
+ end
117
+
118
+ def formatted_path(items)
119
+ if items.respond_to? :join
120
+ items.join(':')
121
+ else
122
+ items.to_s
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ end
@@ -0,0 +1,120 @@
1
+ JUNIT_JAR_PATH = File.dirname(__FILE__) + '/ext/junit.jar'
2
+
3
+
4
+ require JUNIT_JAR_PATH
5
+
6
+
7
+ include_class 'java.lang.ClassLoader'
8
+ include_class 'java.lang.ClassNotFoundException'
9
+ include_class 'java.net.URLClassLoader'
10
+ include_class 'java.net.URL'
11
+
12
+
13
+ module Pangolin
14
+
15
+ class Junit
16
+
17
+ include Output::Formatting
18
+
19
+
20
+ attr_accessor :class_path
21
+ attr_accessor :colorize
22
+ attr_accessor :verbose
23
+
24
+
25
+ def initialize(*classes)
26
+ @class_names = classes || [ ]
27
+ @class_path = [ ]
28
+ @colorize = false
29
+ @verbose = false
30
+ end
31
+
32
+ def classes
33
+ @class_names
34
+ end
35
+
36
+ def execute(io=$stderr)
37
+ io.puts 'junit …' if @verbose
38
+
39
+ junit = load_class('org.junit.runner.JUnitCore').new_instance
40
+ result = junit.run(class_instances)
41
+
42
+ print_result(result)
43
+
44
+ 0 == result.failure_count
45
+ end
46
+
47
+ private
48
+
49
+ def print_result(result)
50
+ if result.was_successful
51
+ puts format_header('%d tests run in %.1f seconds, no failures' % [result.run_count, result.run_time/1000])
52
+ else
53
+ args = [
54
+ result.run_count,
55
+ result.run_time/1000,
56
+ result.failure_count,
57
+ result.failure_count == 1 ? '' : 's'
58
+ ]
59
+
60
+ puts format_header('%d tests run in %.1f seconds with %d failure%s' % args)
61
+ puts ''
62
+
63
+ result.failures.each do |failure|
64
+ puts format_error_header('- ' + failure.test_header)
65
+ puts format_error(' ' + failure.message) unless failure.message.nil? || failure.message =~ /^\s*$/
66
+
67
+ filtered_stack_trace_array(failure.trace).each do |trace_frame|
68
+ puts format_stack_trace(' ' + trace_frame.strip)
69
+ end
70
+
71
+ puts '' #unless failure == result.failures.to_a.last
72
+ end
73
+ end
74
+ end
75
+
76
+ def filtered_stack_trace_array(trace)
77
+ trace.split("\n").reject do |line|
78
+ case line
79
+ when /java.lang.AssertionError/, /at org.junit/, /at sun.reflect/, /at java.lang.reflect/, /at org.jruby/, /jrake/
80
+ true
81
+ else
82
+ false
83
+ end
84
+ end
85
+ end
86
+
87
+ def full_class_path
88
+ @class_path + [JUNIT_JAR_PATH]
89
+ end
90
+
91
+ def pretty_class_path
92
+ full_class_path.join(':')
93
+ end
94
+
95
+ def class_path_urls
96
+ full_class_path.map do |e|
97
+ full_path = File.expand_path(e)
98
+ full_path += '/' if File.directory?(full_path) && full_path[-1] != '/'
99
+
100
+ URL.new('file', 'localhost', full_path)
101
+ end
102
+ end
103
+
104
+ def class_loader
105
+ @class_loader ||= URLClassLoader.new(class_path_urls.to_java(URL), ClassLoader.system_class_loader)
106
+ end
107
+
108
+ def class_instances
109
+ @class_names.map { |class_name| load_class(class_name) }.to_java(java.lang.Class)
110
+ end
111
+
112
+ def load_class(class_name)
113
+ class_loader.load_class(class_name)
114
+ rescue ClassNotFoundException => e
115
+ raise LoadError, "Class not found: #{class_name} (looking in #{pretty_class_path})"
116
+ end
117
+
118
+ end
119
+
120
+ end
@@ -0,0 +1,65 @@
1
+ module Pangolin
2
+ module Output
3
+ module Formatting
4
+
5
+ FORMATTING = {
6
+ # colors
7
+ :black => "\e[30m",
8
+ :red => "\e[31m",
9
+ :green => "\e[32m",
10
+ :yellow => "\e[33m",
11
+ :blue => "\e[34m",
12
+ :magenta => "\e[35m",
13
+ :cyan => "\e[36m",
14
+ :white => "\e[37m",
15
+
16
+ # text styles
17
+ :bold => "\e[1m",
18
+ :underline => "\e[4m",
19
+ :blink => "\e[5m",
20
+ :reverse => "\e[7m",
21
+ :concealed => "\e[8m",
22
+
23
+ # backgrounds
24
+ :black_bg => "\e[40m",
25
+ :red_bg => "\e[41m",
26
+ :green_bg => "\e[42m",
27
+ :yellow_bg => "\e[43m",
28
+ :blue_bg => "\e[44m",
29
+ :magenta_bg => "\e[45m",
30
+ :cyan_bg => "\e[46m",
31
+ :white_bg => "\e[47m"
32
+ }
33
+
34
+ FORMATTING_OFF = "\e[0m"
35
+
36
+ def format_text(text, formatting)
37
+ if @colorize
38
+ format_codes = [formatting].flatten
39
+ format_codes.reject! { |f| FORMATTING[f].nil? }
40
+ format_codes.map! { |f| FORMATTING[f] }
41
+ format_codes.join + text + FORMATTING_OFF
42
+ else
43
+ text
44
+ end
45
+ end
46
+
47
+ def format_header(text)
48
+ format_text(text, :bold)
49
+ end
50
+
51
+ def format_error_header(text)
52
+ format_text(text, [:red, :bold])
53
+ end
54
+
55
+ def format_error(text)
56
+ format_text(text, :red)
57
+ end
58
+
59
+ def format_stack_trace(text)
60
+ text
61
+ end
62
+
63
+ end
64
+ end
65
+ end