buildr 0.18.0 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +62 -4
- data/LICENSE +1 -1
- data/README +64 -1
- data/lib/buildr.rb +27 -28
- data/lib/core/build.rb +39 -30
- data/lib/core/project.rb +323 -247
- data/lib/core/rake_ext.rb +116 -0
- data/lib/core/transports.rb +81 -36
- data/lib/java/ant.rb +78 -0
- data/lib/{core → java}/artifact.rb +212 -109
- data/lib/java/compile.rb +158 -78
- data/lib/java/eclipse.rb +27 -12
- data/lib/java/java.rb +269 -111
- data/lib/java/javacc.rb +53 -69
- data/lib/java/jetty.rb +203 -92
- data/lib/java/jetty/JettyWrapper$BuildrHandler.class +0 -0
- data/lib/java/jetty/JettyWrapper.class +0 -0
- data/lib/java/jetty/JettyWrapper.java +69 -20
- data/lib/java/openjpa.rb +57 -45
- data/lib/java/packaging.rb +248 -116
- data/lib/java/test.rb +261 -128
- data/lib/java/xmlbeans.rb +55 -49
- data/lib/tasks/concat.rb +34 -0
- data/lib/tasks/download.rb +5 -0
- data/lib/tasks/filter.rb +107 -55
- data/lib/tasks/zip.rb +283 -155
- metadata +25 -5
- data/lib/core/core.rb +0 -155
@@ -0,0 +1,116 @@
|
|
1
|
+
module Rake #:nodoc:
|
2
|
+
class Task
|
3
|
+
|
4
|
+
# Access the base directory. The base directory is set when the class
|
5
|
+
# is defined from the current directory. The current directory is set
|
6
|
+
# to the base directory when the class is executed.
|
7
|
+
attr_accessor :base_dir
|
8
|
+
|
9
|
+
def initialize_with_base_dir(*args) #:nodoc:
|
10
|
+
@base_dir = Dir.pwd
|
11
|
+
initialize_without_base_dir *args
|
12
|
+
end
|
13
|
+
alias_method_chain :initialize, :base_dir
|
14
|
+
|
15
|
+
def invoke #:nodoc:
|
16
|
+
tasks = (Thread.current[:tasks] || [])
|
17
|
+
if tasks.include?(name)
|
18
|
+
fail "Circular dependency " + (tasks + [name]).join("=>")
|
19
|
+
end
|
20
|
+
@lock.synchronize do
|
21
|
+
if application.options.trace
|
22
|
+
puts "** Invoke #{name} #{format_trace_flags}"
|
23
|
+
end
|
24
|
+
return if @already_invoked
|
25
|
+
begin
|
26
|
+
Thread.current[:tasks] = tasks + [name]
|
27
|
+
@already_invoked = true
|
28
|
+
Dir.chdir(@base_dir || Dir.pwd) do
|
29
|
+
invoke_prerequisites
|
30
|
+
execute if needed?
|
31
|
+
end
|
32
|
+
ensure
|
33
|
+
Thread.current[:tasks] = tasks
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def invoke_prerequisites() #:nodoc:
|
39
|
+
prerequisites.each { |n| application[n, @scope].invoke }
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class Application #:nodoc:
|
45
|
+
|
46
|
+
def in_namespace_with_global_scope(name, &block)
|
47
|
+
if name =~ /^:/
|
48
|
+
begin
|
49
|
+
scope, @scope = @scope, name.split(":")[1...-1]
|
50
|
+
in_namespace_without_global_scope name.split(":").last, &block
|
51
|
+
ensure
|
52
|
+
@scope = scope
|
53
|
+
end
|
54
|
+
else
|
55
|
+
in_namespace_without_global_scope name, &block
|
56
|
+
end
|
57
|
+
end
|
58
|
+
alias_method_chain :in_namespace, :global_scope
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
class CheckTask < Rake::Task
|
63
|
+
|
64
|
+
def execute()
|
65
|
+
@warnings = []
|
66
|
+
super
|
67
|
+
report if verbose
|
68
|
+
end
|
69
|
+
|
70
|
+
def note(*msg)
|
71
|
+
@warnings += msg
|
72
|
+
end
|
73
|
+
|
74
|
+
def report()
|
75
|
+
if @warnings.empty?
|
76
|
+
puts HighLine.new.color("No warnings", :green)
|
77
|
+
else
|
78
|
+
warn "These are possible problems with your Rakefile"
|
79
|
+
@warnings.each { |msg| warn " #{msg}" }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
desc "Check your Rakefile for common errors"
|
87
|
+
CheckTask.define_task "check"
|
88
|
+
|
89
|
+
# Check for circular dependencies
|
90
|
+
task "check" do
|
91
|
+
# Keep track of tasks we already checked, to avoid death circles.
|
92
|
+
checked = {}
|
93
|
+
# The stack keeps track of all the tasks we visit, so we can display the tasks
|
94
|
+
# involved in the circular dependency.
|
95
|
+
expand = lambda do |stack, task|
|
96
|
+
# Already been here, no need to check again, but make sure we're not seeing
|
97
|
+
# the same task twice due to a circular dependency.
|
98
|
+
fail "Circular " + (stack + [task]).join("=>") if stack.include?(task)
|
99
|
+
unless checked[task]
|
100
|
+
checked[task] = true
|
101
|
+
# Variable task may be a Task, but may also be a task name. In the later
|
102
|
+
# case, we need to resolve it into a Task. But it may also be a filename,
|
103
|
+
# pointing to a file that may exist, just not during the check, so we need
|
104
|
+
# this check to avoid dying on "Don't know how to build ..."
|
105
|
+
if real_task = Rake.application.lookup(task, [])
|
106
|
+
one_deeper = stack + [task.to_s]
|
107
|
+
real_task.prerequisites.each { |prereq| expand[one_deeper, prereq.to_s] }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
Rake.application.tasks.each do |task|
|
112
|
+
expand[ [], task.to_s ]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
data/lib/core/transports.rb
CHANGED
@@ -12,46 +12,81 @@ require "highline"
|
|
12
12
|
# Monkeypatching: SFTP never defines the mkdir method on its session or the underlying
|
13
13
|
# driver, it just redirect calls through method_missing. Rake, on the other hand, decides
|
14
14
|
# to define mkdir on Object, and so routes our calls to FileUtils.
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
module Net #:nodoc:all
|
16
|
+
class Session
|
17
|
+
def mkdir(path, attrs = {})
|
18
|
+
method_missing :mkdir, path, attrs
|
19
|
+
end
|
18
20
|
end
|
19
|
-
end
|
20
21
|
|
21
|
-
class
|
22
|
-
|
23
|
-
|
22
|
+
class SFTP::Protocol::Driver
|
23
|
+
def mkdir(first, path, attrs = {})
|
24
|
+
method_missing :mkdir, first, path, attrs
|
25
|
+
end
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
27
29
|
|
28
30
|
module Buildr
|
31
|
+
|
32
|
+
# Transports are used for downloading artifacts from remote repositories, uploading
|
33
|
+
# artifacts to deployment repositories, and anything else you need to move around.
|
34
|
+
#
|
35
|
+
# The HTTP transport is used for all URLs with the scheme http or https. You can only
|
36
|
+
# use the HTTP transport to download artifacts.
|
37
|
+
#
|
38
|
+
# The SFTP transport is used for all URLs with the schema sftp. You can only use the
|
39
|
+
# SFTP transport to upload artifacts.
|
40
|
+
#
|
41
|
+
# The SFTP transport supports the following options:
|
42
|
+
# * :username -- The username.
|
43
|
+
# * :password -- A password. If unspecified, you will be prompted to enter a password.
|
44
|
+
# * :permissions -- Permissions to set on the uploaded file.
|
45
|
+
# You can also pass the username/password in the URL.
|
46
|
+
#
|
47
|
+
# The SFTP transport will automatically create MD5 and SHA1 digest files for each file
|
48
|
+
# it uploads.
|
29
49
|
module Transports
|
30
50
|
|
51
|
+
# Indicates the requested resource was not found.
|
31
52
|
class NotFound < Exception
|
32
53
|
end
|
33
54
|
|
34
|
-
|
35
|
-
# specified URL. For examples, see Transport#download and Transport#upload.
|
36
|
-
def self.perform(url, options = nil, &block)
|
37
|
-
uri = URI.parse(url.to_s)
|
38
|
-
const_get(uri.scheme.upcase).perform(uri, options, &block)
|
39
|
-
end
|
55
|
+
class << self
|
40
56
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
57
|
+
# :call-seq:
|
58
|
+
# perform(url, options?) { |transport| ... }
|
59
|
+
#
|
60
|
+
# Perform one or more operations using an open connection to the
|
61
|
+
# specified URL. For examples, see Transport#download and Transport#upload.
|
62
|
+
def perform(url, options = nil, &block)
|
63
|
+
uri = URI.parse(url.to_s)
|
64
|
+
const_get(uri.scheme.upcase).perform(uri, options, &block)
|
48
65
|
end
|
66
|
+
|
67
|
+
# :call-seq:
|
68
|
+
# download(url, target, options?)
|
69
|
+
#
|
70
|
+
# Convenience method for downloading a single file from the specified
|
71
|
+
# URL to the target file.
|
72
|
+
def download(url, target, options = nil)
|
73
|
+
uri = URI.parse(url.to_s)
|
74
|
+
path, uri.path = uri.path, ""
|
75
|
+
const_get(uri.scheme.upcase).perform(uri, options) do |transport|
|
76
|
+
transport.download(path, target)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
49
80
|
end
|
50
81
|
|
82
|
+
# Extend this class if you are implementing a new transport.
|
51
83
|
class Transport
|
52
84
|
|
53
85
|
class << self
|
54
86
|
|
87
|
+
# :call-seq:
|
88
|
+
# perform(url, options?) { |transport| ... }
|
89
|
+
#
|
55
90
|
# Perform one or more operations using an open connection to the
|
56
91
|
# specified URL. For examples, see #download and #upload.
|
57
92
|
def perform(url, options = nil)
|
@@ -71,6 +106,7 @@ module Buildr
|
|
71
106
|
# Options passed during construction.
|
72
107
|
attr_reader :options
|
73
108
|
|
109
|
+
# Initialize the transport with the specified URL and options.
|
74
110
|
def initialize(url, options)
|
75
111
|
@uri = URI.parse(url.to_s)
|
76
112
|
@base_path = @uri.path || "/"
|
@@ -138,10 +174,10 @@ module Buildr
|
|
138
174
|
end
|
139
175
|
progress_bar.format = "#{truncated}: %3d%% %s %s/%s %s"
|
140
176
|
progress_bar.format = "%3d%% %s %s/%s %s"
|
141
|
-
progress_bar.format_arguments = [:percentage, :bar, :bytes, :total, :stat]
|
177
|
+
progress_bar.format_arguments = [:percentage, :bar, :bytes, :total, :stat]
|
142
178
|
progress_bar.bar_mark = "."
|
143
179
|
|
144
|
-
|
180
|
+
|
145
181
|
begin
|
146
182
|
class << progress_bar
|
147
183
|
def <<(bytes)
|
@@ -194,7 +230,7 @@ module Buildr
|
|
194
230
|
digester.to_hash
|
195
231
|
end
|
196
232
|
|
197
|
-
class Digester
|
233
|
+
class Digester #:nodoc:
|
198
234
|
|
199
235
|
def initialize(types)
|
200
236
|
types ||= [ "md5", "sha1" ]
|
@@ -210,7 +246,7 @@ module Buildr
|
|
210
246
|
end
|
211
247
|
|
212
248
|
# Iterate over all the digests calling the block with two arguments:
|
213
|
-
# the digest type (e.g. "md5") and the hexadecimal digest value.
|
249
|
+
# the digest type (e.g. "md5") and the hexadecimal digest value.
|
214
250
|
def each()
|
215
251
|
@digests.each { |type, digest| yield type, digest.hexdigest }
|
216
252
|
end
|
@@ -228,7 +264,7 @@ module Buildr
|
|
228
264
|
end
|
229
265
|
|
230
266
|
|
231
|
-
class HTTP < Transport
|
267
|
+
class HTTP < Transport #:nodoc:
|
232
268
|
|
233
269
|
def initialize(url, options)
|
234
270
|
super
|
@@ -254,7 +290,7 @@ module Buildr
|
|
254
290
|
response.read_body do |chunk|
|
255
291
|
write[chunk]
|
256
292
|
digester << chunk
|
257
|
-
progress << chunk
|
293
|
+
progress << chunk
|
258
294
|
end
|
259
295
|
# Check server digests before approving the download.
|
260
296
|
digester.each do |type, hexdigest|
|
@@ -267,7 +303,7 @@ module Buildr
|
|
267
303
|
end
|
268
304
|
end
|
269
305
|
end
|
270
|
-
|
306
|
+
|
271
307
|
if target
|
272
308
|
# If download breaks we end up with a partial file which is
|
273
309
|
# worse than not having a file at all, so download to temporary
|
@@ -297,8 +333,11 @@ module Buildr
|
|
297
333
|
|
298
334
|
end
|
299
335
|
|
336
|
+
# Use the HTTP transport for HTTPS connections.
|
337
|
+
HTTPS = HTTP #:nodoc:
|
300
338
|
|
301
|
-
|
339
|
+
|
340
|
+
class SFTP < Transport #:nodoc:
|
302
341
|
|
303
342
|
class << self
|
304
343
|
def passwords()
|
@@ -310,6 +349,7 @@ module Buildr
|
|
310
349
|
|
311
350
|
def initialize(url, options)
|
312
351
|
super
|
352
|
+
@permissions = options.delete :permissions
|
313
353
|
# SSH options are based on the username/password from the URI.
|
314
354
|
ssh_options = { :port=>@uri.port, :username=>@uri.user }.merge(options || {})
|
315
355
|
ssh_options[:password] ||= SFTP.passwords[@uri.host]
|
@@ -334,30 +374,34 @@ module Buildr
|
|
334
374
|
File.open(source) do |file|
|
335
375
|
with_progress_bar path.split("/").last, File.size(source) do |progress|
|
336
376
|
with_digests(@options[:digests]) do |digester|
|
337
|
-
|
338
|
-
|
377
|
+
target_path = "#{@base_path}#{path}"
|
378
|
+
puts "Uploading to #{target_path}" if Rake.application.options.trace
|
379
|
+
@sftp.open_handle(target_path, "w") do |handle|
|
339
380
|
# Writing in chunks gives us the benefit of a progress bar,
|
340
381
|
# but also require that we maintain a position in the file,
|
341
382
|
# since write() with two arguments always writes at position 0.
|
342
383
|
pos = 0
|
343
|
-
while chunk = file.read(32 * 4096)
|
384
|
+
while chunk = file.read(32 * 4096)
|
344
385
|
@sftp.write(handle, chunk, pos)
|
345
386
|
pos += chunk.size
|
346
387
|
digester << chunk
|
347
388
|
progress << chunk
|
348
389
|
end
|
349
390
|
end
|
391
|
+
@sftp.setstat(target_path, :permissions => @permissions) if @permissions
|
350
392
|
|
351
393
|
# Upload all the digests.
|
352
394
|
digester.each do |type, hexdigest|
|
353
|
-
|
354
|
-
|
395
|
+
digest_file = "#{@base_path}#{path}.#{type}"
|
396
|
+
puts "Uploading signature to #{digest_file}" if Rake.application.options.trace
|
397
|
+
@sftp.open_handle(digest_file, "w") do |handle|
|
355
398
|
@sftp.write(handle, "#{hexdigest} #{path}")
|
356
399
|
end
|
400
|
+
@sftp.setstat(digest_file, :permissions => @permissions) if @permissions
|
357
401
|
end
|
358
402
|
end
|
359
403
|
|
360
|
-
end
|
404
|
+
end
|
361
405
|
end
|
362
406
|
end
|
363
407
|
|
@@ -367,8 +411,9 @@ module Buildr
|
|
367
411
|
# otherwise mkdir fails.
|
368
412
|
puts "Creating path #{@base_path}" if Rake.application.options.trace
|
369
413
|
path.split("/").inject(@base_path) do |base, part|
|
370
|
-
|
371
|
-
|
414
|
+
combined = base + part
|
415
|
+
@sftp.realpath combined rescue @sftp.mkdir combined, {}
|
416
|
+
"#{combined}/"
|
372
417
|
end
|
373
418
|
end
|
374
419
|
|
data/lib/java/ant.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require "core/project"
|
2
|
+
require "java/java"
|
3
|
+
|
4
|
+
module Buildr
|
5
|
+
module Java
|
6
|
+
module Ant
|
7
|
+
|
8
|
+
# Libraries used by #ant.
|
9
|
+
REQUIRES = [ "ant:ant:jar:1.6.5", "ant:ant-launcher:jar:1.6.5", "xerces:xercesImpl:jar:2.6.2" ]
|
10
|
+
|
11
|
+
# Make sure Ant and friends show on the classpath. Antwrap must only be loaded after RJB.
|
12
|
+
Java.rjb.classpath += REQUIRES
|
13
|
+
Java.rjb.onload { require "antwrap" }
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
# :call-seq:
|
18
|
+
# declarative(name, options?) => AntProject
|
19
|
+
# declarative(name, options?) { |AntProject| ... } => AntProject
|
20
|
+
#
|
21
|
+
# Returns a declarative AntProject with the specified name. Ant tasks created in this project
|
22
|
+
# are not executed until you tell them to.
|
23
|
+
#
|
24
|
+
# See #ant for more information about options and the block.
|
25
|
+
def declarative(name, options = nil, &block)
|
26
|
+
options ||= {}
|
27
|
+
options = (options || {}).merge(:name=>name, :base_dir=>Dir.pwd, :declarative=>false)
|
28
|
+
Java.rjb { AntProject.new(options).tap { |project| yield project if block_given? } }
|
29
|
+
end
|
30
|
+
|
31
|
+
# :call-seq:
|
32
|
+
# executable(name, options?) => AntProject
|
33
|
+
# executable(name, options?) { |AntProject| ... } => AntProject
|
34
|
+
#
|
35
|
+
# Returns an executable AntProject with the specified name. Ant tasks created in this project
|
36
|
+
# are executed immediately.
|
37
|
+
#
|
38
|
+
# See #ant for more information about options and the block.
|
39
|
+
def executable(name, options = nil, &block)
|
40
|
+
options ||= {}
|
41
|
+
options = (options || {}).merge(:name=>name, :base_dir=>Dir.pwd, :declarative=>true)
|
42
|
+
Java.rjb { AntProject.new(options).tap { |project| yield project if block_given? } }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# :call-seq:
|
47
|
+
# ant(name, options?) => AntProject
|
48
|
+
# ant(name, options?) { |AntProject| ... } => AntProject
|
49
|
+
#
|
50
|
+
# Returns a new AntProject with the specified name. Ant tasks created in this project are
|
51
|
+
# executed immediately.
|
52
|
+
#
|
53
|
+
# The options hash is passed to the Ant project definition, along with the current directory.
|
54
|
+
# When used in a Buildr project, the Ant project will have the same base directory.
|
55
|
+
# If you pass a block, yields to the block with the Ant project.
|
56
|
+
#
|
57
|
+
# For example:
|
58
|
+
# ant("hibernatedoclet") do |doclet|
|
59
|
+
# doclet.taskdef :name=>"hibernatedoclet",
|
60
|
+
# :classname=>"xdoclet.modules.hibernate.HibernateDocletTask", :classpath=>DOCLET
|
61
|
+
# doclet.hibernatedoclet :destdir=>dest_dir, :force=>"true" do
|
62
|
+
# hibernate :version=>"3.0"
|
63
|
+
# fileset :dir=>source, :includes=>"**/*.java"
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
def ant(name, options=nil, &block)
|
67
|
+
Java::Ant.executable(name, options, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
class Project
|
75
|
+
include Java::Ant
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -1,16 +1,25 @@
|
|
1
|
+
require "core/project"
|
2
|
+
require "core/transports"
|
3
|
+
|
1
4
|
module Buildr
|
2
5
|
|
3
6
|
desc "Download all artifacts"
|
4
7
|
task "artifacts"
|
5
8
|
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
+
# Mixin with a task to make it behave like an artifact. Implemented by the packaging tasks.
|
10
|
+
#
|
11
|
+
# An artifact has an identifier, group identifier, type, version number and
|
12
|
+
# optional classifier. All can be used to locate it in the local repository,
|
13
|
+
# download from or upload to a remote repository.
|
14
|
+
#
|
15
|
+
# The #to_spec and #to_hash methods allow it to be used everywhere an artifact is
|
16
|
+
# accepted.
|
9
17
|
module ActsAsArtifact
|
10
18
|
|
11
19
|
ARTIFACT_ATTRIBUTES = [:group, :id, :type, :classifier, :version]
|
12
20
|
|
13
21
|
class << self
|
22
|
+
private
|
14
23
|
def included(mod)
|
15
24
|
mod.extend self
|
16
25
|
end
|
@@ -27,21 +36,44 @@ module Buildr
|
|
27
36
|
# Optional artifact classifier.
|
28
37
|
attr_reader :classifier
|
29
38
|
|
30
|
-
#
|
39
|
+
# :call-seq:
|
40
|
+
# to_spec_hash() => Hash
|
41
|
+
#
|
42
|
+
# Returns the artifact specification as a hash. For example:
|
43
|
+
# com.example:app:jar:1.2
|
44
|
+
# becomes:
|
45
|
+
# { :group=>"com.example",
|
46
|
+
# :id=>"app",
|
47
|
+
# :type=>"jar",
|
48
|
+
# :version=>"1.2" }
|
31
49
|
def to_spec_hash()
|
32
50
|
base = { :group=>group, :id=>id, :type=>type, :version=>version }
|
33
51
|
classifier.blank? ? base : base.merge(:classifier=>classifier)
|
34
52
|
end
|
35
53
|
alias_method :to_hash, :to_spec_hash
|
36
54
|
|
55
|
+
# :call-seq:
|
56
|
+
# to_spec() => String
|
57
|
+
#
|
37
58
|
# Returns the artifact specification, in the structure:
|
38
59
|
# <group>:<artifact>:<type>:<version>
|
39
60
|
# or
|
40
|
-
# <group>:<artifact>:<type>:<classifier
|
61
|
+
# <group>:<artifact>:<type>:<classifier><:version>
|
41
62
|
def to_spec()
|
42
63
|
classifier.blank? ? "#{group}:#{id}:#{type}:#{version}" : "#{group}:#{id}:#{type}:#{classifier}:#{version}"
|
43
64
|
end
|
44
65
|
|
66
|
+
# :call-seq:
|
67
|
+
# pom() => Artifact
|
68
|
+
#
|
69
|
+
# Convenience method that returns a POM artifact.
|
70
|
+
def pom()
|
71
|
+
return self if type.to_s == "pom"
|
72
|
+
artifact(:group=>group, :id=>id, :version=>version, :type=>"pom", :classifier=>classifier)
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
45
77
|
# Apply specification to this artifact.
|
46
78
|
def apply_spec(spec)
|
47
79
|
spec = Artifact.to_hash(spec)
|
@@ -49,20 +81,17 @@ module Buildr
|
|
49
81
|
self
|
50
82
|
end
|
51
83
|
|
52
|
-
# Convenience method that returns a POM artifact.
|
53
|
-
def pom()
|
54
|
-
return self if type.to_s == "pom"
|
55
|
-
artifact(:group=>group, :id=>id, :version=>version, :type=>"pom", :classifier=>classifier)
|
56
|
-
end
|
57
|
-
|
58
84
|
end
|
59
85
|
|
60
|
-
|
61
|
-
#
|
62
|
-
#
|
86
|
+
|
87
|
+
# A file task referencing an artifact in the local repository.
|
88
|
+
#
|
89
|
+
# This task includes all the artifact attributes (group, id, version, etc). It points
|
90
|
+
# to the artifact's path in the local repository. When invoked, it will download the
|
91
|
+
# artifact into the local repository if the artifact does not already exist.
|
63
92
|
#
|
64
|
-
#
|
65
|
-
#
|
93
|
+
# Note: You can enhance this task to create the artifact yourself, e.g. download it from
|
94
|
+
# a site that doesn't have a remote repository structure, copy it from a different disk, etc.
|
66
95
|
class Artifact < Rake::FileCreationTask
|
67
96
|
|
68
97
|
# The default file type for artifacts, if not specified.
|
@@ -72,37 +101,44 @@ module Buildr
|
|
72
101
|
|
73
102
|
class << self
|
74
103
|
|
75
|
-
#
|
76
|
-
#
|
104
|
+
# :call-seq:
|
105
|
+
# lookup(spec) => Artifact
|
106
|
+
#
|
107
|
+
# Lookup a previously registered artifact task based on its specification (String or Hash).
|
77
108
|
def lookup(spec)
|
78
109
|
@artifacts ||= {}
|
79
110
|
@artifacts[to_spec(spec)]
|
80
111
|
end
|
81
112
|
|
113
|
+
# :call-seq:
|
114
|
+
# register(artifacts) => artifacts
|
115
|
+
#
|
82
116
|
# Register an artifact task(s) for later lookup (see #lookup).
|
83
117
|
def register(*tasks)
|
84
118
|
@artifacts ||= {}
|
85
|
-
fail "You can only register an artifact task,
|
119
|
+
fail "You can only register an artifact task, one of the arguments is not a Task that responds to to_spec()" unless
|
86
120
|
tasks.all? { |task| task.respond_to?(:to_spec) && task.respond_to?(:invoke) }
|
87
121
|
tasks.each { |task| @artifacts[task.to_spec] = task }
|
88
122
|
tasks
|
89
123
|
end
|
90
124
|
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
125
|
+
# :call-seq:
|
126
|
+
# to_hash(spec_hash) => spec_hash
|
127
|
+
# to_hash(spec_string) => spec_hash
|
128
|
+
# to_hash(artifact) => spec_hash
|
129
|
+
#
|
130
|
+
# Turn a spec into a hash. This method accepts a String, Hash or any object that responds to
|
131
|
+
# the method to_spec. There are several reasons to use this method:
|
94
132
|
# * You can pass anything that could possibly be a spec, and get a hash.
|
95
133
|
# * It will check that the spec includes the group identifier, artifact
|
96
134
|
# identifier and version number and set the file type, if missing.
|
97
135
|
# * It will always return a new specs hash.
|
98
|
-
#
|
99
|
-
# :nodoc:
|
100
136
|
def to_hash(spec)
|
101
137
|
if spec.respond_to?(:to_spec)
|
102
138
|
to_hash spec.to_spec
|
103
139
|
elsif Hash === spec
|
104
140
|
# Sanitize the hash and check it's valid.
|
105
|
-
spec = ARTIFACT_ATTRIBUTES.inject({}) { |h, k| h[k] = spec[k] ; h }
|
141
|
+
spec = ARTIFACT_ATTRIBUTES.inject({}) { |h, k| h[k] = spec[k].to_s if spec[k] ; h }
|
106
142
|
fail "Missing group identifier for #{spec.inspect}" if spec[:group].blank?
|
107
143
|
fail "Missing artifact identifier for #{spec.inspect}" if spec[:id].blank?
|
108
144
|
fail "Missing version for #{spec.inspect}" if spec[:version].blank?
|
@@ -121,9 +157,11 @@ module Buildr
|
|
121
157
|
end
|
122
158
|
end
|
123
159
|
|
160
|
+
# :call-seq:
|
161
|
+
# to_spec(spec_hash) => spec_string
|
162
|
+
#
|
124
163
|
# Convert a hash back to a spec string. This method accepts
|
125
164
|
# a string, hash or any object that responds to to_spec.
|
126
|
-
# :nodoc:
|
127
165
|
def to_spec(hash)
|
128
166
|
hash = to_hash(hash) unless Hash === hash
|
129
167
|
version = ":#{hash[:version]}" unless hash[:version].blank?
|
@@ -131,8 +169,10 @@ module Buildr
|
|
131
169
|
"#{hash[:group]}:#{hash[:id]}:#{hash[:type] || DEFAULT_FILE_TYPE}#{classifier}#{version}"
|
132
170
|
end
|
133
171
|
|
134
|
-
#
|
135
|
-
#
|
172
|
+
# :call-seq:
|
173
|
+
# hash_to_file_name(spec_hash) => file_name
|
174
|
+
#
|
175
|
+
# Convert a hash spec to a file name.
|
136
176
|
def hash_to_file_name(hash)
|
137
177
|
version = "-#{hash[:version]}" unless hash[:version].blank?
|
138
178
|
classifier = "-#{hash[:classifier]}" unless hash[:classifier].blank?
|
@@ -141,15 +181,20 @@ module Buildr
|
|
141
181
|
|
142
182
|
end
|
143
183
|
|
144
|
-
def
|
145
|
-
# Default behavior: download the artifact from one of the remote
|
146
|
-
# repositories if the file does not exist. But this default behavior
|
147
|
-
# is counter useful if the artifact knows how to build itself
|
148
|
-
# (e.g. download from a different location), so don't perform it
|
149
|
-
# if the task found a different way to create the artifact.
|
184
|
+
def initialize(*args) #:nodoc:
|
150
185
|
super
|
151
|
-
|
152
|
-
|
186
|
+
enhance do |task|
|
187
|
+
# Default behavior: download the artifact from one of the remote
|
188
|
+
# repositories if the file does not exist. But this default behavior
|
189
|
+
# is counter useful if the artifact knows how to build itself
|
190
|
+
# (e.g. download from a different location), so don't perform it
|
191
|
+
# if the task found a different way to create the artifact.
|
192
|
+
# For that, we need to be the last piece of code run by the task.
|
193
|
+
task.enhance do
|
194
|
+
unless Rake.application.options.dryrun || File.exist?(name)
|
195
|
+
repositories.download(to_spec)
|
196
|
+
end
|
197
|
+
end
|
153
198
|
end
|
154
199
|
end
|
155
200
|
|
@@ -158,25 +203,41 @@ module Buildr
|
|
158
203
|
|
159
204
|
# Holds the path to the local repository, URLs for remote repositories, and
|
160
205
|
# settings for the deployment repository.
|
206
|
+
#
|
207
|
+
# You can access this object from the #repositories method. For example:
|
208
|
+
# puts repositories.local
|
209
|
+
# repositories.remote << "http://example.com/repo"
|
210
|
+
# repositories.deploy_to = "sftp://example.com/var/www/public/repo"
|
161
211
|
class Repositories
|
162
212
|
include Singleton
|
163
213
|
|
214
|
+
# :call-seq:
|
215
|
+
# local() => path
|
216
|
+
#
|
164
217
|
# Returns the path to the local repository.
|
165
218
|
#
|
166
219
|
# The default path is .m2/repository relative to the home directory.
|
167
|
-
# You can change the location of the local repository by using a symbol
|
168
|
-
# link or by setting a different path. If you set a different path, do it
|
169
|
-
# in the buildr.rb file instead of the Rakefile.
|
170
220
|
def local()
|
171
|
-
@local ||= ENV["local_repo"] || File.join(ENV["HOME"], ".m2
|
221
|
+
@local ||= ENV["local_repo"] || File.join(ENV["HOME"], ".m2/repository")
|
172
222
|
end
|
173
223
|
|
224
|
+
# :call-seq:
|
225
|
+
# local = path
|
226
|
+
#
|
174
227
|
# Sets the path to the local repository.
|
228
|
+
#
|
229
|
+
# The best place to set the local repository path is from a buildr.rb file
|
230
|
+
# located in your home directory. That way all your projects will share the same
|
231
|
+
# path, without affecting other developers collaborating on these projects.
|
175
232
|
def local=(dir)
|
176
233
|
@local = dir ? File.expand_path(dir) : nil
|
177
234
|
end
|
178
235
|
|
179
|
-
#
|
236
|
+
# :call-seq:
|
237
|
+
# locate(spec) => path
|
238
|
+
#
|
239
|
+
# Locates an artifact in the local repository based on its specification, and returns
|
240
|
+
# a file path.
|
180
241
|
#
|
181
242
|
# For example:
|
182
243
|
# locate :group=>"log4j", :id=>"log4j", :version=>"1.1"
|
@@ -186,16 +247,28 @@ module Buildr
|
|
186
247
|
File.join(local, spec[:group].split("."), spec[:id], spec[:version], Artifact.hash_to_file_name(spec))
|
187
248
|
end
|
188
249
|
|
189
|
-
#
|
190
|
-
#
|
191
|
-
#
|
250
|
+
# :call-seq:
|
251
|
+
# remote() => Array
|
252
|
+
#
|
253
|
+
# Returns an array of all the remote repository URLs.
|
254
|
+
#
|
255
|
+
# When downloading artifacts, repositories are accessed in the order in which they appear here.
|
256
|
+
# The best way is to add repositories individually, for example:
|
257
|
+
# repositories.remote << "http://example.com/repo"
|
192
258
|
def remote()
|
193
259
|
@remote ||= []
|
194
260
|
end
|
195
261
|
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
262
|
+
# :call-seq:
|
263
|
+
# remote = Array
|
264
|
+
# remote = url
|
265
|
+
# remote = nil
|
266
|
+
#
|
267
|
+
# With a String argument, clears the array and set it to that single URL.
|
268
|
+
#
|
269
|
+
# With an Array argument, clears the array and set it to these specific URLs.
|
270
|
+
#
|
271
|
+
# With nil, clears the array.
|
199
272
|
def remote=(urls)
|
200
273
|
case urls
|
201
274
|
when nil
|
@@ -207,9 +280,18 @@ module Buildr
|
|
207
280
|
end
|
208
281
|
end
|
209
282
|
|
210
|
-
|
211
|
-
#
|
212
|
-
#
|
283
|
+
|
284
|
+
# :call-seq:
|
285
|
+
# download(spec) => boolean
|
286
|
+
#
|
287
|
+
# Downloads an artifact from one of the remote repositories, and stores it in the local
|
288
|
+
# repository. Accepts a String or Hash artifact specification, and returns a path to the
|
289
|
+
# artifact in the local repository. Raises an exception if the artifact is not found.
|
290
|
+
#
|
291
|
+
# This method attempts to download the artifact from each repository in the order in
|
292
|
+
# which they are returned from #remote, until successful. If you want to download an
|
293
|
+
# artifact only if not already installed in the local repository, create an #artifact
|
294
|
+
# task and invoke it directly.
|
213
295
|
def download(spec)
|
214
296
|
spec = Artifact.to_hash(spec) unless Hash === spec
|
215
297
|
path = locate(spec)
|
@@ -236,27 +318,38 @@ module Buildr
|
|
236
318
|
fail "Failed to download #{Artifact.to_spec(spec)}, tried the following repositories:\n#{repositories.remote.join("\n")}"
|
237
319
|
end
|
238
320
|
|
239
|
-
#
|
240
|
-
#
|
241
|
-
#
|
321
|
+
# :call-seq:
|
322
|
+
# deploy_to = url
|
323
|
+
# deploy_to = hash
|
324
|
+
#
|
325
|
+
# Specifies the deployment repository. Accepts a Hash with different repository settings
|
326
|
+
# (e.g. url, username, password), or a String to only set the repository URL.
|
327
|
+
#
|
328
|
+
# Besides the URL, all other settings depend on the transport protocol in use. See #Transports
|
329
|
+
# for more details. Common settings include username and password.
|
242
330
|
#
|
243
331
|
# For example:
|
244
|
-
# repositories.deploy_to = { :url=>"sftp://example.com/var/www/
|
332
|
+
# repositories.deploy_to = { :url=>"sftp://example.com/var/www/repo/",
|
245
333
|
# :username="john", :password=>"secret" }
|
246
334
|
# or:
|
247
|
-
# repositories.deploy_to = "sftp://john:secret@example.com/var/www/
|
335
|
+
# repositories.deploy_to = "sftp://john:secret@example.com/var/www/repo/"
|
248
336
|
def deploy_to=(options)
|
249
337
|
options = { :url=>options } unless Hash === options
|
250
338
|
@deploy_to = options
|
251
339
|
end
|
252
340
|
|
253
|
-
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
258
|
-
#
|
259
|
-
#
|
341
|
+
# :call-seq:
|
342
|
+
# deploy_to() => hash
|
343
|
+
#
|
344
|
+
# Returns the current deployment repository setting as a Hash. This is a more convenient
|
345
|
+
# way to specify the deployment repository, as it allows you to specify the settings
|
346
|
+
# progressively.
|
347
|
+
#
|
348
|
+
# For example, the Rakefile will contain the repository URL used by all developers:
|
349
|
+
# repositories.deploy_to[:url] ||= "sftp://example.com/var/www/repo"
|
350
|
+
# Your private buildr.rb will contain your credentials:
|
351
|
+
# repositories.deploy_to[:username] = "john"
|
352
|
+
# repositories.deploy_to[:password] = "secret"
|
260
353
|
def deploy_to()
|
261
354
|
@deploy_to ||= {}
|
262
355
|
end
|
@@ -264,94 +357,99 @@ module Buildr
|
|
264
357
|
end
|
265
358
|
|
266
359
|
|
267
|
-
#
|
360
|
+
# :call-seq:
|
361
|
+
# repositories() => Repositories
|
362
|
+
#
|
363
|
+
# Returns an object you can use for setting the local repository path, remote repositories
|
364
|
+
# URL and deployment repository settings.
|
365
|
+
#
|
268
366
|
# See Repositories.
|
269
367
|
def repositories()
|
270
368
|
Repositories.instance
|
271
369
|
end
|
272
370
|
|
273
|
-
#
|
371
|
+
# :call-seq:
|
372
|
+
# artifact(spec) => Artifact
|
373
|
+
# artifact(spec) { |task| ... } => Artifact
|
374
|
+
#
|
375
|
+
# Creates a file task to download and install the specified artifact in the local repository.
|
274
376
|
#
|
275
|
-
#
|
276
|
-
#
|
377
|
+
# You can use a String or a Hash for the artifact specification. The file task will point at
|
378
|
+
# the artifact's path inside the local repository. You can then use this tasks as a prerequisite
|
379
|
+
# for other tasks.
|
277
380
|
#
|
278
|
-
#
|
279
|
-
#
|
381
|
+
# This task will download and install the artifact only once. In fact, it will download and
|
382
|
+
# install the artifact if the artifact does not already exist. You can enhance it if you have
|
383
|
+
# a different way of creating the artifact in the local repository. See Artifact for more details.
|
280
384
|
#
|
281
385
|
# For example, to specify an artifact:
|
282
386
|
# artifact("log4j:log4j:jar:1.1")
|
283
387
|
#
|
284
388
|
# To use the artifact in a task:
|
285
|
-
#
|
389
|
+
# compile.with artifact("log4j:log4j:jar:1.1")
|
286
390
|
#
|
287
391
|
# To specify an artifact and the means for creating it:
|
288
392
|
# download(artifact("dojo:dojo-widget:zip:2.0")=>
|
289
393
|
# "http://download.dojotoolkit.org/release-2.0/dojo-2.0-widget.zip")
|
290
|
-
def artifact(spec, &block)
|
394
|
+
def artifact(spec, &block) #:yields:task
|
291
395
|
spec = Artifact.to_hash(spec)
|
292
396
|
unless task = Artifact.lookup(spec)
|
293
397
|
task = Artifact.define_task(repositories.locate(spec))
|
398
|
+
task.send :apply_spec, spec
|
294
399
|
Rake::Task["rake:artifacts"].enhance [ task ]
|
295
400
|
Artifact.register(task)
|
296
401
|
end
|
297
|
-
task.apply_spec spec
|
298
402
|
task.enhance &block
|
299
403
|
end
|
300
404
|
|
301
|
-
#
|
302
|
-
#
|
405
|
+
# :call-seq:
|
406
|
+
# artifacts(*spec) => artifacts
|
407
|
+
#
|
408
|
+
# Handles multiple artifacts at a time. This method is the plural equivalent of
|
409
|
+
# #artifacts, but can do more things.
|
303
410
|
#
|
304
411
|
# You can pass any number of arguments, each of which can be:
|
305
|
-
# * An artifact specification
|
306
|
-
#
|
307
|
-
# *
|
308
|
-
# * A
|
309
|
-
# * An array of artifacts.
|
412
|
+
# * An artifact specification (String or Hash). Returns the appropriate Artifact task.
|
413
|
+
# * An artifact of any other task. Returns the task as is.
|
414
|
+
# * A project. Returns all artifacts created (packaged) by that project.
|
415
|
+
# * A string. Returns that string, assumed to be a file name.
|
416
|
+
# * An array of artifacts. Calls #artifacts on the array, flattens the result.
|
310
417
|
#
|
311
|
-
#
|
312
|
-
# to help in managing large combinations of artifacts. For example:
|
418
|
+
# For example, handling a collection of artifacts:
|
313
419
|
# xml = [ xerces, xalan, jaxp ]
|
314
420
|
# ws = [ axis, jax-ws, jaxb ]
|
315
421
|
# db = [ jpa, mysql, sqltools ]
|
316
|
-
#
|
317
|
-
# artifacts(base, models, services)
|
318
|
-
#
|
319
|
-
# You can also pass tasks and project. This is particularly useful for
|
320
|
-
# dealing with dependencies between projects that are part of the same
|
321
|
-
# build.
|
322
|
-
#
|
323
|
-
# For example:
|
324
|
-
# artifacts(base, models, services, module1, module2)
|
422
|
+
# artifacts(xml, ws, db)
|
325
423
|
#
|
326
|
-
#
|
327
|
-
#
|
328
|
-
#
|
329
|
-
# project, without installing them in the local repository.
|
424
|
+
# Using artifacts created by a project:
|
425
|
+
# artifact project("my-app") # All packages
|
426
|
+
# artifact project("mu-app").package(:war) # Only the WAR
|
330
427
|
def artifacts(*specs)
|
331
428
|
specs.inject([]) do |set, spec|
|
332
429
|
case spec
|
430
|
+
when Array
|
431
|
+
set |= artifacts(*spec)
|
333
432
|
when Hash
|
334
433
|
set |= [artifact(spec)]
|
335
|
-
when
|
434
|
+
when /([^:]+:){2,4}/ # A spec as opposed to a file name.
|
336
435
|
set |= [artifact(spec)]
|
337
|
-
when String
|
436
|
+
when String # Must always expand path.
|
338
437
|
set |= [File.expand_path(spec)]
|
339
438
|
when Project
|
340
439
|
set |= artifacts(spec.packages)
|
341
440
|
when Rake::Task
|
342
441
|
set |= [spec]
|
343
|
-
when Array
|
344
|
-
set |= artifacts(*spec)
|
345
442
|
else
|
346
|
-
fail "Invalid artifact specification: #{
|
443
|
+
fail "Invalid artifact specification in: #{specs.inspect}"
|
347
444
|
end
|
348
|
-
set
|
349
445
|
end
|
350
446
|
end
|
351
447
|
|
352
|
-
#
|
353
|
-
#
|
354
|
-
#
|
448
|
+
# :call-seq:
|
449
|
+
# groups(ids, :under=>group_name, :version=>number) => artifacts
|
450
|
+
#
|
451
|
+
# Convenience method for defining multiple artifacts that belong to the same group and version.
|
452
|
+
# Accepts multiple artifact identifiers follows by two hash values:
|
355
453
|
# * :under -- The group identifier
|
356
454
|
# * :version -- The version number
|
357
455
|
#
|
@@ -364,27 +462,32 @@ module Buildr
|
|
364
462
|
args.flatten.map { |id| artifact :group=>hash[:under], :version=>hash[:version], :id=>id }
|
365
463
|
end
|
366
464
|
|
367
|
-
#
|
368
|
-
#
|
369
|
-
#
|
465
|
+
# :call-seq:
|
466
|
+
# deploy(*files)
|
467
|
+
# deploy(*files, deploy_options)
|
468
|
+
#
|
469
|
+
# Deploys all the specified artifacts/files. If the last argument is a Hash, it is used to
|
470
|
+
# specify the deployment repository. Otherwise, obtains the deployment repository by calling
|
471
|
+
# Repositories#deploy_to.
|
370
472
|
#
|
371
473
|
# For example:
|
372
|
-
# deploy(
|
474
|
+
# deploy(foo.packages, :url=>"sftp://example.com/var/www/repo")
|
373
475
|
def deploy(*args)
|
374
476
|
# Where do we release to?
|
375
477
|
if Hash === args.last
|
376
478
|
options = args.pop
|
377
479
|
else
|
378
|
-
options = repositories.deploy_to
|
480
|
+
options = repositories.deploy_to.clone
|
379
481
|
options = { :url=>options.to_s } unless Hash === options
|
380
482
|
end
|
483
|
+
# Strip all options since the transport requires them separately from the URL.
|
381
484
|
url = options[:url]
|
382
485
|
options = options.reject { |k,v| k === :url }
|
383
486
|
fail "Don't know where to deploy, perhaps you forgot to set repositories.deploy_to" if url.blank?
|
384
487
|
|
385
|
-
args.each { |arg| arg.invoke if arg.respond_to?(:invoke) }
|
488
|
+
args.flatten.each { |arg| arg.invoke if arg.respond_to?(:invoke) }
|
386
489
|
Transports.perform url, options do |session|
|
387
|
-
args.each do |artifact|
|
490
|
+
args.flatten.each do |artifact|
|
388
491
|
if artifact.respond_to?(:to_spec)
|
389
492
|
# Upload artifact relative to base URL, need to create path before uploading.
|
390
493
|
puts "Deploying #{artifact.to_spec}" if verbose
|