buildr 0.14.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.
- 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
|