pangolin 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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