buildr 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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
@@ -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
- class Net::SFTP::Session
16
- def mkdir(path, attrs = {})
17
- method_missing :mkdir, path, attrs
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 Net::SFTP::Protocol::Driver
22
- def mkdir(first, path, attrs = {})
23
- method_missing :mkdir, first, path, attrs
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
- # Perform one or more operations using an open connection to the
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
- # Convenience method for downloading a single file from the specified
42
- # URL to the target file.
43
- def self.download(url, target, options = nil)
44
- uri = URI.parse(url.to_s)
45
- path, uri.path = uri.path, ""
46
- const_get(uri.scheme.upcase).perform(uri, options) do |transport|
47
- transport.download(path, target)
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
- class SFTP < Transport
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
- puts "Uploading to #{@base_path}#{path}" if Rake.application.options.trace
338
- @sftp.open_handle(@base_path + path, "w") do |handle|
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
- puts "Uploading signature to #{@base_path}#{path}.#{type}" if Rake.application.options.trace
354
- @sftp.open_handle("#{@base_path}#{path}.#{type}", "w") do |handle|
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
- @sftp.realpath(base+part) rescue @sftp.mkdir base + part
371
- "#{base}#{part}/"
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
- # This module gives you a way to access the individual properties of an
7
- # artifact: id, group, type, classifier and version. It also provides other
8
- # methods commonly used on an artifact, specifically #to_hash and #to_spec.
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
- # Returns the artifact specification as a hash.
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><:<version>
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
- # The Artifact task maps to an artifact file in the local repository
62
- # and knows how to download the file from a remote repository.
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
- # The task will only download the file if it does not exist. You can
65
- # enhance the task to create the artifact yourself.
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
- # Lookup a previously registered artifact task based on the
76
- # artifact specification (string or hash).
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, strings and hashes are just not good enough" unless
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
- # Turn a spec into a hash. This method accepts a string, hash or any object
92
- # that responds to the method to_spec. There are several reasons to use this
93
- # method:
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
- # Convert a hash to a file name.
135
- # :nodoc:
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 execute()
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
- unless Rake.application.options.dryrun || File.exist?(name)
152
- repositories.download(to_spec)
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", "repository")
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
- # Locates an artifact in the local repository based on its specification.
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
- # Returns an array of all the remote repositories. When downloading an artifact,
190
- # the default behavior is to try repositories in the order in which they show in
191
- # the array.
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
- # With a string argument, sets the remote repository (only one) to this URL.
197
- # With an array argument, sets the remote repository to that set of URLs.
198
- # Passing nil is equivalent to an empty array.
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
- # Attempts to download the artifact from one of the remote repositories
211
- # and store it in the local repository. Returns the path if downloaded,
212
- # otherwise raises an exception.
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
- # Specifies the deployment repository. Accepts a hash with the different
240
- # repository settings (e.g. url, username, password). Anything else is
241
- # interepted as the URL.
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/maven/",
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/maven/"
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
- # Returns the current deployment repository configuration. This is a more
254
- # convenient way to specify deployment in the Rakefile, and override it
255
- # locally. For example:
256
- # # Rakefile
257
- # repositories.deploy_to[:url] ||= "sftp://example.com"
258
- # # buildr.rb
259
- # repositories.deploy_to[:url] = "sftp://acme.org"
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
- # Returns a global object for setting local, remote and deploy repositories.
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
- # Creates a file task to download and install the specified artifact.
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
- # The artifact specification can be a string or a hash.
276
- # The file task points to the artifact in the local repository.
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
- # You can provide alternative behavior to create the artifact instead
279
- # of downloading it from a remote repository.
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
- # unzip artifact("org.apache.pxe:db-derby:zip:1.2")
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
- # Creates multiple artifacts from a set of specifications and returns
302
- # an array of tasks.
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, string or hash. Returns a new task for
306
- # each specification, by calling #artifact.
307
- # * An artifact task or any other task. Returns the task as is.
308
- # * A project. Returns all packaging tasks in that project.
309
- # * An array of artifacts. Returns all the artifacts found there.
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
- # This method handles arrays of artifacts as if they are flattend,
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
- # base = [ xml, ws, db ]
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
- # When passing a project as argument, it expands that project to all
327
- # its packaging tasks. You can then use the resulting artifacts as
328
- # dependencies that will force these packages to be build inside the
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: #{spec.to_s || 'nil'}"
443
+ fail "Invalid artifact specification in: #{specs.inspect}"
347
444
  end
348
- set
349
445
  end
350
446
  end
351
447
 
352
- # Convenience method for defining multiple artifacts that belong
353
- # to the same version and group. Accepts multiple artifact identifiers
354
- # (or arrays of) followed by two has keys:
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
- # Deploys all the specified artifacts/files. Specify the deployment
368
- # server by passing a hash as the last argument, or have it use
369
- # repositories.deploy_to.
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(*process.packages, :url=>"sftp://example.com/var/www/maven")
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