buildr 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +151 -0
- data/LICENSE +202 -0
- data/README +0 -0
- data/lib/buildr.rb +43 -0
- data/lib/core/artifact.rb +394 -0
- data/lib/core/build.rb +108 -0
- data/lib/core/core.rb +49 -0
- data/lib/core/project.rb +313 -0
- data/lib/core/transports.rb +381 -0
- data/lib/java/compile.rb +316 -0
- data/lib/java/java.rb +117 -0
- data/lib/java/javacc.rb +88 -0
- data/lib/java/openjpa.rb +52 -0
- data/lib/java/packaging.rb +217 -0
- data/lib/java/xmlbeans.rb +64 -0
- data/lib/tasks/download.rb +38 -0
- data/lib/tasks/filter.rb +96 -0
- data/lib/tasks/zip.rb +313 -0
- metadata +134 -0
@@ -0,0 +1,381 @@
|
|
1
|
+
require "net/ssh"
|
2
|
+
require "net/sftp"
|
3
|
+
require "uri/sftp"
|
4
|
+
require "digest/md5"
|
5
|
+
require "digest/sha1"
|
6
|
+
require "facet/progressbar"
|
7
|
+
require "highline"
|
8
|
+
|
9
|
+
|
10
|
+
# Monkeypatching: SFTP never defines the mkdir method on its session or the underlying
|
11
|
+
# driver, it just redirect calls through method_missing. Rake, on the other hand, decides
|
12
|
+
# to define mkdir on Object, and so routes our calls to FileUtils.
|
13
|
+
class Net::SFTP::Session
|
14
|
+
def mkdir(path, attrs = {})
|
15
|
+
method_missing :mkdir, path, attrs
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Net::SFTP::Protocol::Driver
|
20
|
+
def mkdir(first, path, attrs = {})
|
21
|
+
method_missing :mkdir, first, path, attrs
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
module Buildr
|
27
|
+
module Transports
|
28
|
+
|
29
|
+
class NotFound < Exception
|
30
|
+
end
|
31
|
+
|
32
|
+
# Perform one or more operations using an open connection to the
|
33
|
+
# specified URL. For examples, see Transport#download and Transport#upload.
|
34
|
+
def self.perform(url, options = nil, &block)
|
35
|
+
uri = URI.parse(url.to_s)
|
36
|
+
const_get(uri.scheme.upcase).perform(uri, options, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Convenience method for downloading a single file from the specified
|
40
|
+
# URL to the target file.
|
41
|
+
def self.download(url, target, options = nil)
|
42
|
+
uri = URI.parse(url.to_s)
|
43
|
+
path, uri.path = uri.path, ""
|
44
|
+
const_get(uri.scheme.upcase).perform(uri, options) do |transport|
|
45
|
+
transport.download(path, target)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Transport
|
50
|
+
|
51
|
+
class << self
|
52
|
+
|
53
|
+
# Perform one or more operations using an open connection to the
|
54
|
+
# specified URL. For examples, see #download and #upload.
|
55
|
+
def perform(url, options = nil)
|
56
|
+
instance = new(url, options)
|
57
|
+
begin
|
58
|
+
yield instance
|
59
|
+
ensure
|
60
|
+
instance.close
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# The server URI.
|
66
|
+
attr_reader :uri
|
67
|
+
# The base path on the server, always ending with a slash.
|
68
|
+
attr_reader :base_path
|
69
|
+
# Options passed during construction.
|
70
|
+
attr_reader :options
|
71
|
+
|
72
|
+
def initialize(url, options)
|
73
|
+
@uri = URI.parse(url.to_s)
|
74
|
+
@base_path = @uri.path || "/"
|
75
|
+
@base_path += "/" unless @base_path[-1] == ?/
|
76
|
+
@options = options || {}
|
77
|
+
end
|
78
|
+
|
79
|
+
# Downloads a file from the specified path, relative to the
|
80
|
+
# server URI. Downloads to either the target file, or by
|
81
|
+
# calling the block with each chunk of the file.
|
82
|
+
#
|
83
|
+
# For example:
|
84
|
+
# Transports.perform("http://server/libs") do |http|
|
85
|
+
# http.download("my_project/test.jar", "test.jar")
|
86
|
+
# http.download("my_project/readme") { |text| $stdout.write text }
|
87
|
+
# end
|
88
|
+
def download(path, target, &block)
|
89
|
+
fail "Upload not implemented for this transport"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Uploads a file (source) to the server at the specified path,
|
93
|
+
# relative to the server URI.
|
94
|
+
#
|
95
|
+
# For example:
|
96
|
+
# Transports.perform("sftp://server/libs") do |sftp|
|
97
|
+
# sftp.mkpath "my_project"
|
98
|
+
# sftp.upload("target/test.jar", "my_project/test.jar")
|
99
|
+
# end
|
100
|
+
def upload(source, path)
|
101
|
+
fail "Upload not implemented for this transport"
|
102
|
+
end
|
103
|
+
|
104
|
+
# Creates a path on the server relative to the server URI.
|
105
|
+
# See #upload for example.
|
106
|
+
def mkpath(path)
|
107
|
+
fail "Upload not implemented for this transport"
|
108
|
+
end
|
109
|
+
|
110
|
+
protected
|
111
|
+
|
112
|
+
# :call-seq:
|
113
|
+
# with_progress_bar(file_name, length) { |progress| ... }
|
114
|
+
#
|
115
|
+
# Displays a progress bar while executing the block. The first
|
116
|
+
# argument provides a filename to display, the second argument
|
117
|
+
# its size in bytes.
|
118
|
+
#
|
119
|
+
# The block is yielded with a progress object that implements
|
120
|
+
# a single method. Call << for each block of bytes down/uploaded.
|
121
|
+
def with_progress_bar(file_name, length)
|
122
|
+
if verbose && $stdout.isatty
|
123
|
+
progress_bar = Console::ProgressBar.new(file_name, length)
|
124
|
+
# Extend the progress bar so we can display count/total.
|
125
|
+
class << progress_bar
|
126
|
+
def total()
|
127
|
+
convert_bytes(@total)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
# Squeeze the filename into 30 characters.
|
131
|
+
if file_name.size > 30
|
132
|
+
base, ext = file_name.split(".")
|
133
|
+
truncated = "#{base[0..26-ext.size]}...#{ext}"
|
134
|
+
else
|
135
|
+
truncated = file_name
|
136
|
+
end
|
137
|
+
progress_bar.format = "#{truncated}: %3d%% %s %s/%s %s"
|
138
|
+
progress_bar.format = "%3d%% %s %s/%s %s"
|
139
|
+
progress_bar.format_arguments = [:percentage, :bar, :bytes, :total, :stat]
|
140
|
+
progress_bar.bar_mark = "."
|
141
|
+
|
142
|
+
|
143
|
+
begin
|
144
|
+
class << progress_bar
|
145
|
+
def <<(bytes)
|
146
|
+
inc bytes.respond_to?(:size) ? bytes.size : bytes
|
147
|
+
end
|
148
|
+
end
|
149
|
+
yield progress_bar
|
150
|
+
ensure
|
151
|
+
progress_bar.finish
|
152
|
+
end
|
153
|
+
else
|
154
|
+
progress_bar = Object.new
|
155
|
+
class << progress_bar
|
156
|
+
def <<(bytes)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
yield progress_bar
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# :call-seq:
|
164
|
+
# with_digests(types?) { |digester| ... } => hash
|
165
|
+
#
|
166
|
+
# Use the Digester to create digests for files you are downloading or
|
167
|
+
# uploading, and either verify their signatures (download) or create
|
168
|
+
# signatures (upload).
|
169
|
+
#
|
170
|
+
# The method takes one argument with the list of digest algorithms to
|
171
|
+
# support. Leave if empty and it will default to MD5 and SHA1.
|
172
|
+
#
|
173
|
+
# The method then yields the block passing it a Digester. The Digester
|
174
|
+
# supports two methods. Use << to pass data that you are down/uploading.
|
175
|
+
# Once all data is transmitted, use each to iterate over the digests.
|
176
|
+
# The each method calls the block with the digest type (e.g. "md5")
|
177
|
+
# and the hexadecimal digest value.
|
178
|
+
#
|
179
|
+
# For example:
|
180
|
+
# with_digests do |digester|
|
181
|
+
# download url do |block|
|
182
|
+
# digester << block
|
183
|
+
# end
|
184
|
+
# digester.each do |type, hexdigest|
|
185
|
+
# signature = download "#{url}.#{type}"
|
186
|
+
# fail "Mismatch" unless signature == hexdigest
|
187
|
+
# end
|
188
|
+
# end
|
189
|
+
def with_digests(types = nil)
|
190
|
+
digester = Digester.new(types)
|
191
|
+
yield digester
|
192
|
+
digester.to_hash
|
193
|
+
end
|
194
|
+
|
195
|
+
class Digester
|
196
|
+
|
197
|
+
def initialize(types)
|
198
|
+
types ||= [ "md5", "sha1" ]
|
199
|
+
@digests = types.inject({}) do |hash, type|
|
200
|
+
hash[type.to_s.downcase] = Digest.const_get(type.to_s.upcase).new
|
201
|
+
hash
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Add bytes for digestion.
|
206
|
+
def <<(bytes)
|
207
|
+
@digests.each { |type, digest| digest << bytes }
|
208
|
+
end
|
209
|
+
|
210
|
+
# Iterate over all the digests calling the block with two arguments:
|
211
|
+
# the digest type (e.g. "md5") and the hexadecimal digest value.
|
212
|
+
def each()
|
213
|
+
@digests.each { |type, digest| yield type, digest.hexdigest }
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns a hash that maps each digest type to its hexadecimal digest value.
|
217
|
+
def to_hash()
|
218
|
+
@digests.keys.inject({}) do |hash, type|
|
219
|
+
hash[type] = @digests[type].hexdigest
|
220
|
+
hash
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
class HTTP < Transport
|
230
|
+
|
231
|
+
def initialize(url, options)
|
232
|
+
super
|
233
|
+
@http = Net::HTTP.start(@uri.host, @uri.port)
|
234
|
+
end
|
235
|
+
|
236
|
+
def download(path, target = nil, &block)
|
237
|
+
puts "Requesting #{path} from #{@uri}" if Rake.application.options.trace
|
238
|
+
@http.request_get(@base_path + path) do |response|
|
239
|
+
case response
|
240
|
+
when Net::HTTPRedirection
|
241
|
+
# Try to download from the new URI, handle relative redirects.
|
242
|
+
puts "Redirected" if Rake.application.options.trace
|
243
|
+
Transports.download(@uri + URI.parse(response["location"]), target, @options)
|
244
|
+
|
245
|
+
when Net::HTTPOK
|
246
|
+
puts "Downloading #{@uri}/#{path}" if verbose
|
247
|
+
with_progress_bar path.split("/").last, response.content_length do |progress|
|
248
|
+
with_digests(@options[:digests]) do |digester|
|
249
|
+
|
250
|
+
download = proc do |write|
|
251
|
+
# Read the body of the page and write it out.
|
252
|
+
response.read_body do |chunk|
|
253
|
+
write[chunk]
|
254
|
+
digester << chunk
|
255
|
+
progress << chunk
|
256
|
+
end
|
257
|
+
# Check server digests before approving the download.
|
258
|
+
digester.each do |type, hexdigest|
|
259
|
+
@http.request_get("#{@base_path}#{path}.#{type.to_s.downcase}") do |response|
|
260
|
+
if Net::HTTPOK === response
|
261
|
+
puts "Checking signature from #{@uri}/#{path}" if Rake.application.options.trace
|
262
|
+
fail "Checksum failure for #{@uri}/#{path}: #{type.to_s.upcase} digest on server did not match downloaded file" unless
|
263
|
+
response.read_body.split.first == hexdigest
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
if target
|
270
|
+
# If download breaks we end up with a partial file which is
|
271
|
+
# worse than not having a file at all, so download to temporary
|
272
|
+
# file and then move over.
|
273
|
+
temp = Tempfile.new(File.basename(target))
|
274
|
+
download[ proc { |chunk| temp.write chunk } ]
|
275
|
+
temp.close
|
276
|
+
File.move temp.path, target if target
|
277
|
+
else
|
278
|
+
download[ block ]
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
end
|
283
|
+
when Net::HTTPNotFound
|
284
|
+
raise NotFound
|
285
|
+
else
|
286
|
+
fail "Failed to download #{@uri}/#{path}: #{response.message}"
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def close()
|
292
|
+
@http.finish
|
293
|
+
@http = nil
|
294
|
+
end
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
class SFTP < Transport
|
300
|
+
|
301
|
+
class << self
|
302
|
+
def passwords()
|
303
|
+
@passwords ||= {}
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
attr_reader :sftp
|
308
|
+
|
309
|
+
def initialize(url, options)
|
310
|
+
super
|
311
|
+
# SSH options are based on the username/password from the URI.
|
312
|
+
ssh_options = { :port=>@uri.port, :username=>@uri.user }.merge(options || {})
|
313
|
+
ssh_options[:password] ||= SFTP.passwords[@uri.host]
|
314
|
+
begin
|
315
|
+
puts "Connecting to #{@uri.host}" if Rake.application.options.trace
|
316
|
+
session = Net::SSH.start(@uri.host, ssh_options)
|
317
|
+
SFTP.passwords[@uri.host] = ssh_options[:password]
|
318
|
+
rescue Net::SSH::AuthenticationFailed=>ex
|
319
|
+
# Only if running with console, prompt for password.
|
320
|
+
if !ssh_options[:password] && $stdout.isatty
|
321
|
+
password = HighLine.new.ask("Password for #{@uri.host}:") { |q| q.echo = "*" }
|
322
|
+
ssh_options[:password] = password
|
323
|
+
retry
|
324
|
+
end
|
325
|
+
raise
|
326
|
+
end
|
327
|
+
@sftp = session.sftp.connect
|
328
|
+
puts "connected" if Rake.application.options.trace
|
329
|
+
end
|
330
|
+
|
331
|
+
def upload(source, path)
|
332
|
+
File.open(source) do |file|
|
333
|
+
with_progress_bar path.split("/").last, File.size(source) do |progress|
|
334
|
+
with_digests(@options[:digests]) do |digester|
|
335
|
+
puts "Uploading to #{@base_path}#{path}" if Rake.application.options.trace
|
336
|
+
@sftp.open_handle(@base_path + path, "w") do |handle|
|
337
|
+
# Writing in chunks gives us the benefit of a progress bar,
|
338
|
+
# but also require that we maintain a position in the file,
|
339
|
+
# since write() with two arguments always writes at position 0.
|
340
|
+
pos = 0
|
341
|
+
while chunk = file.read(32 * 4096)
|
342
|
+
@sftp.write(handle, chunk, pos)
|
343
|
+
pos += chunk.size
|
344
|
+
digester << chunk
|
345
|
+
progress << chunk
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
# Upload all the digests.
|
350
|
+
digester.each do |type, hexdigest|
|
351
|
+
puts "Uploading signature to #{@base_path}#{path}.#{type}" if Rake.application.options.trace
|
352
|
+
@sftp.open_handle("#{@base_path}#{path}.#{type}", "w") do |handle|
|
353
|
+
@sftp.write(handle, "#{hexdigest} #{path}")
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def mkpath(path)
|
363
|
+
# To create a path, we need to create all its parent.
|
364
|
+
# We use realpath to determine if the path already exists,
|
365
|
+
# otherwise mkdir fails.
|
366
|
+
puts "Creating path #{@base_path}" if Rake.application.options.trace
|
367
|
+
path.split("/").inject(@base_path) do |base, part|
|
368
|
+
@sftp.realpath(base+part) rescue @sftp.mkdir base + part
|
369
|
+
"#{base}#{part}/"
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def close()
|
374
|
+
@sftp.close
|
375
|
+
@sftp = nil
|
376
|
+
end
|
377
|
+
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|
381
|
+
end
|
data/lib/java/compile.rb
ADDED
@@ -0,0 +1,316 @@
|
|
1
|
+
module Buildr
|
2
|
+
module Java
|
3
|
+
|
4
|
+
class CompileTask < Rake::Task
|
5
|
+
|
6
|
+
# Compiler options, accessible from Compiler#options.
|
7
|
+
#
|
8
|
+
# Supported options are:
|
9
|
+
# - warnings -- Generate warnings if true (opposite of -nowarn).
|
10
|
+
# - verbose -- Output messages about what the compiler is doing.
|
11
|
+
# - deprecation -- Output source locations where deprecated APIs
|
12
|
+
# are used.
|
13
|
+
# - source -- Source compatibility with specified release.
|
14
|
+
# - target -- Class file compatibility with specified release.
|
15
|
+
# - lint -- Value to pass to xlint argument. Use true to enable
|
16
|
+
# default lint options, or pass a specific setting as string
|
17
|
+
# or array of strings.
|
18
|
+
# - debug -- Generate debugging info.
|
19
|
+
# - other -- Array of options to pass to the Java compiler as is.
|
20
|
+
#
|
21
|
+
# For example:
|
22
|
+
# options.verbose = true
|
23
|
+
# options.source = options.target = "1.6"
|
24
|
+
class Options
|
25
|
+
|
26
|
+
include Attributes
|
27
|
+
|
28
|
+
OPTIONS = [:warnings, :verbose, :deprecation, :source, :target, :lint, :debug, :other]
|
29
|
+
|
30
|
+
# Generate warnings (opposite of -nowarn).
|
31
|
+
inherited_attr :warnings
|
32
|
+
# Output messages about what the compiler is doing.
|
33
|
+
inherited_attr :verbose
|
34
|
+
# Output source locations where deprecated APIs are used.
|
35
|
+
inherited_attr :deprecation
|
36
|
+
# Provide source compatibility with specified release.
|
37
|
+
inherited_attr :source
|
38
|
+
# Generate class files for specific VM version.
|
39
|
+
inherited_attr :target
|
40
|
+
# Values to pass to Xlint: string or array. Use true to enable
|
41
|
+
# Xlint with no values.
|
42
|
+
inherited_attr :lint
|
43
|
+
# Generate all debugging info.
|
44
|
+
inherited_attr :debug
|
45
|
+
# Array of arguments passed to the Java compiler as is.
|
46
|
+
inherited_attr :other
|
47
|
+
|
48
|
+
def initialize(parent = nil)
|
49
|
+
@parent = parent
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_reader :parent
|
53
|
+
|
54
|
+
def clear()
|
55
|
+
OPTIONS.each { |name| send "#{name}=", nil }
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s()
|
59
|
+
OPTIONS.inject({}){ |hash, name| hash[name] = send(name) ; hash }.reject{ |name,value| value.nil? }.inspect
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns Javac command line arguments from the set of options.
|
63
|
+
def javac_args()
|
64
|
+
args = []
|
65
|
+
args << "-nowarn" unless warnings && Rake.application.options.trace
|
66
|
+
args << "-verbose" if verbose
|
67
|
+
args << "-g" if debug
|
68
|
+
args << "-deprecation" if deprecation
|
69
|
+
args << ["-source", source.to_s] if source
|
70
|
+
args << ["-target", target.to_s] if target
|
71
|
+
case lint
|
72
|
+
when Array
|
73
|
+
args << "-Xlint:#{lint.join(',')}"
|
74
|
+
when String
|
75
|
+
args << "-Xlint:#{lint}"
|
76
|
+
when true
|
77
|
+
args << "-Xlint"
|
78
|
+
end
|
79
|
+
args << other if other
|
80
|
+
args
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
# The target directory for the generated class files.
|
86
|
+
attr_accessor :target
|
87
|
+
|
88
|
+
def initialize(*args)
|
89
|
+
super
|
90
|
+
if name[":"] # Only if in namespace
|
91
|
+
parent = Rake::Task["^compile"]
|
92
|
+
if parent && parent.respond_to?(:options)
|
93
|
+
@options = Options.new(parent.options)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
enhance do |task|
|
98
|
+
# Do we have any files to compile?
|
99
|
+
if target && files.empty?
|
100
|
+
puts "All source files are up to date" if Rake.application.options.trace && !sources.empty?
|
101
|
+
elsif target
|
102
|
+
mkpath target, :verbose=>false
|
103
|
+
Java.javac files, :sourcepath=>sourcepath, :classpath=>classpath,
|
104
|
+
:output=>target, :javac_args=>options.javac_args, :name=>task.name
|
105
|
+
# By touching the target we let other tasks know we did something,
|
106
|
+
# and also prevent recompiling again for classpath dependencies.
|
107
|
+
touch target, :verbose=>false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# An array of source directories and files.
|
113
|
+
def sources()
|
114
|
+
@sources ||= []
|
115
|
+
end
|
116
|
+
|
117
|
+
def sources=(paths)
|
118
|
+
@sources = paths.flatten
|
119
|
+
end
|
120
|
+
|
121
|
+
# Array of classpath dependencies: files, file tasks, artifacts specs.
|
122
|
+
def classpath()
|
123
|
+
@classpath ||= []
|
124
|
+
end
|
125
|
+
|
126
|
+
def classpath=(paths)
|
127
|
+
@classpath = paths.flatten
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns the compiler options.
|
131
|
+
def options()
|
132
|
+
@options ||= Options.new
|
133
|
+
end
|
134
|
+
|
135
|
+
# Sets the compile options based on a hash of values, or reset to
|
136
|
+
# the default by passing nil.
|
137
|
+
def options=(arg)
|
138
|
+
case arg
|
139
|
+
when Options
|
140
|
+
@options = arg
|
141
|
+
when Hash
|
142
|
+
options.clear
|
143
|
+
arg.each { |k,v| options.send "#{k}=", v }
|
144
|
+
when nil
|
145
|
+
options.clear
|
146
|
+
else
|
147
|
+
raise ArgumentError, "Expecting Options, hash or nil (to reset)"
|
148
|
+
end
|
149
|
+
@options
|
150
|
+
end
|
151
|
+
|
152
|
+
# Sets the target directory and returns self. For example:
|
153
|
+
# compile(src_dir).to(target_dir).with(artifacts)
|
154
|
+
def to(dir)
|
155
|
+
self.target = dir
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
# Adds files and artifacts to the classpath and returns self.
|
160
|
+
# For example:
|
161
|
+
# compile(src_dir).to(target_dir).with(artifact, file, task)
|
162
|
+
def with(*args)
|
163
|
+
self.classpath |= args.flatten
|
164
|
+
self
|
165
|
+
end
|
166
|
+
|
167
|
+
# Sets the compiler options and returns self. For example:
|
168
|
+
# compile(src_dir).using(:warnings=>true, :verbose=>true)
|
169
|
+
def using(hash)
|
170
|
+
self.options = hash
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns true if any classes were compiled.
|
175
|
+
def compiled?()
|
176
|
+
@files && !@files.empty?
|
177
|
+
end
|
178
|
+
|
179
|
+
protected
|
180
|
+
|
181
|
+
# Returns the real classpath. Uses the values of #classpath, but resolves
|
182
|
+
# artifact specifications, projects and other conveniences, executes tasks
|
183
|
+
# (see #sanitize) and returns a compact array of unique file names.
|
184
|
+
def real_classpath()
|
185
|
+
@real_classpath ||= sanitize(artifacts(classpath))
|
186
|
+
end
|
187
|
+
|
188
|
+
# Return the sourcepath, essentialy compact array of directory and file names,
|
189
|
+
# as set by the user, but after invoking dependencies (see #sanitize).
|
190
|
+
def sourcepath()
|
191
|
+
@real_sources ||= sanitize(sources)
|
192
|
+
@real_sources.select { |source| File.directory?(source) }
|
193
|
+
end
|
194
|
+
|
195
|
+
# Returns the files to compile. This list is derived from the list of sources,
|
196
|
+
# expanding directories into files, and includes only source files that are
|
197
|
+
# newer than the corresponding class file. Includes all files if one or more
|
198
|
+
# classpath dependency has been updated.
|
199
|
+
def files()
|
200
|
+
unless @files
|
201
|
+
@real_sources ||= sanitize(sources)
|
202
|
+
# Compile all files if we compiled nothing, or one of the classpath
|
203
|
+
# dependencies is newer than the compiled classes.
|
204
|
+
all = !File.exist?(target) ||
|
205
|
+
File.stat(target).mtime < (real_classpath.collect{ |file| File.stat(file).mtime } + [ Rake::EARLY ]).max
|
206
|
+
|
207
|
+
if all # Do not restrict to changed files
|
208
|
+
@files = @real_sources.collect do |source|
|
209
|
+
File.directory?(source) ? Dir[File.join(source, "**", "*.java")] : source
|
210
|
+
end.flatten
|
211
|
+
else
|
212
|
+
# Only changed files.
|
213
|
+
@files = @real_sources.collect do |source|
|
214
|
+
if File.directory?(source)
|
215
|
+
Dir[File.join(source, "**", "*.java")].select do |java|
|
216
|
+
klass = java.sub(source, target).ext(".class")
|
217
|
+
!File.exist?(klass) || File.stat(java).mtime > File.stat(klass).mtime
|
218
|
+
end
|
219
|
+
else
|
220
|
+
source
|
221
|
+
end
|
222
|
+
end.flatten
|
223
|
+
end
|
224
|
+
end
|
225
|
+
@files
|
226
|
+
end
|
227
|
+
|
228
|
+
def sanitize(paths)
|
229
|
+
# Flatten to handle nested arrays often used with dependencies.
|
230
|
+
# Invoke all tasks, treated as prerequisites (e.g. download artifacts).
|
231
|
+
# Return task name or file name, ignoring nils and duplicates.
|
232
|
+
paths.flatten.each { |path| path.invoke if path.respond_to?(:invoke) }.
|
233
|
+
collect { |path| path.respond_to?(:name) ? path.name : path.to_s }.
|
234
|
+
compact.uniq
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
# Create and return a compiler task. The task is name "compile" in the current
|
243
|
+
# namespace. Method arguments are passed as sources to compile, and options are
|
244
|
+
# inherited from any compile task in a parent namespace.
|
245
|
+
#
|
246
|
+
# For example:
|
247
|
+
# compile("src").to("classes").with(artifact, file, task)
|
248
|
+
def self.compile(*sources)
|
249
|
+
returning(Java::CompileTask.define_task("compile")) do |task|
|
250
|
+
task.sources |= sources
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def compile(*sources)
|
255
|
+
Buildr.compile(*sources)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Global task compiles all projects.
|
259
|
+
desc "Compile all projects"
|
260
|
+
LocalDirectoryTask.define_task "compile"
|
261
|
+
|
262
|
+
|
263
|
+
class Project
|
264
|
+
|
265
|
+
# The source directory. The default is "src".
|
266
|
+
inherited_attr :src_dir, "src"
|
267
|
+
# The Java source code directory. The default is <src_dir>/main/java.
|
268
|
+
inherited_attr :java_src_dir do File.join(src_dir, "main", "java") end
|
269
|
+
# The resources source directory. The default is <src_dir>/main/resources.
|
270
|
+
inherited_attr :resources_dir do File.join(src_dir, "main", "resources") end
|
271
|
+
# The target directory. The default is "target".
|
272
|
+
inherited_attr :target_dir, "target"
|
273
|
+
# The Java target directory. The default is <target_dir>/classes.
|
274
|
+
inherited_attr :java_target_dir do File.join(target_dir, "classes") end
|
275
|
+
|
276
|
+
def resources(*tasks, &block)
|
277
|
+
returning(@resources_task ||= Filter.define_task("resources")) do |task|
|
278
|
+
task.enhance tasks, &block
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def compile(*sources, &block)
|
283
|
+
returning(@compile_task ||= Java::CompileTask.define_task("compile")) do |task|
|
284
|
+
task.sources |= sources
|
285
|
+
task.enhance &block if block
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def prepare(*tasks, &block)
|
290
|
+
returning(@prepare_task ||= task("prepare")) do |task|
|
291
|
+
task.enhance tasks, &block
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
Project.on_create do |project|
|
299
|
+
# Prepare is prerequisite for compile, resources follows compile.
|
300
|
+
project.compile.enhance([project.prepare]) { |task| project.resources.invoke }
|
301
|
+
project.recursive_task("compile")
|
302
|
+
task("build").enhance [ project.compile ]
|
303
|
+
task("clean") { rm_rf project.path_to(:target_dir), :verbose=>false }
|
304
|
+
|
305
|
+
project.enhance do |project|
|
306
|
+
# Automagic compilation only if the source directory exists.
|
307
|
+
project.compile.sources << project.path_to(:java_src_dir) if File.exist?(project.path_to(:java_src_dir))
|
308
|
+
project.compile.target ||= project.path_to(:java_target_dir)
|
309
|
+
project.resources.include project.path_to(:resources_dir, "*") if File.exist?(project.path_to(:resources_dir))
|
310
|
+
project.resources.target ||= project.compile.target
|
311
|
+
file(project.compile.target).enhance([ project.compile, project.resources ])
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|