buildr 0.15.0 → 0.16.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 CHANGED
@@ -1,3 +1,15 @@
1
+ 0.16 (3/7/2007)
2
+ * Added: zip.include :as=> to include file under specified name.
3
+ * Added: zip.merge to include the (expanded) contents of one zip file in another.
4
+ * Added: experimental test task using JUnit and JMock.
5
+ * Changed: project.to_s returns name, projects returns sorted by name.
6
+ * Changed: project definition now executed using project's base directory
7
+ as the current directory.
8
+ * Fixed: artifact test cases and minor code cleanup.
9
+ * Fixed: attempts to download artifact even if created by task.
10
+ * Fixed: release task now deletes old tagged copy and reports SVN usage.
11
+ * Fixed: OpenJPA not including target directory in classpath.
12
+
1
13
  0.15 (2/28/2007)
2
14
  * Fixed: tasks fail unless deployment server specified.
3
15
  * Changed: deploy method executes deployment, instead of returning a task.
@@ -1,13 +1,17 @@
1
- # Remember that all these need to be Gem dependencies.
2
- gem "facets"
3
- gem "builder"
4
- gem "net-ssh"
5
- gem "net-sftp"
6
- gem "rubyzip"
7
- gem "highline"
1
+ # returning(obj) and with(obj)
2
+ require "facet/kernel/with"
3
+ # &:symbol goodness.
4
+ require "facet/symbol/to_proc"
5
+ # blank? on string and nil
6
+ require "facet/string/blank"
7
+ require "facet/nilclass/blank"
8
+ # What it says.
9
+ require "facet/module/alias_method_chain"
10
+ require "facet/string/starts_with"
11
+
8
12
 
9
13
  module Buildr
10
- VERSION = "0.15.0"
14
+ VERSION = "0.16.0"
11
15
  end
12
16
 
13
17
  $LOAD_PATH.unshift File.join(File.dirname(__FILE__))
@@ -23,6 +27,7 @@ require "tasks/zip"
23
27
 
24
28
  require "java/java"
25
29
  require "java/compile"
30
+ require "java/test"
26
31
  require "java/javacc"
27
32
  require "java/openjpa"
28
33
  require "java/packaging"
@@ -1,25 +1,27 @@
1
1
  module Buildr
2
2
 
3
3
  # This module gives you a way to access the individual properties of an
4
- # artifact: id, group, type and version. It can also return the artifact
5
- # specification.
4
+ # artifact: id, group, type, classifier and version. It also provides other
5
+ # methods commonly used on an artifact, specifically #to_hash and #to_spec.
6
6
  module ActsAsArtifact
7
7
 
8
+ ARTIFACT_ATTRIBUTES = [:group, :id, :type, :classifier, :version]
9
+
8
10
  class << self
9
11
  def included(mod)
10
12
  mod.extend self
11
13
  end
12
14
  end
13
15
 
14
- # The artifact id.
16
+ # The artifact identifier.
15
17
  attr_reader :id
16
- # The group id.
18
+ # The group identifier.
17
19
  attr_reader :group
18
20
  # The file type.
19
21
  attr_reader :type
20
22
  # The version number.
21
23
  attr_reader :version
22
- # Optional classifier.
24
+ # Optional artifact classifier.
23
25
  attr_reader :classifier
24
26
 
25
27
  # Returns the artifact specification as a hash.
@@ -31,28 +33,20 @@ module Buildr
31
33
 
32
34
  # Returns the artifact specification, in the structure:
33
35
  # <group>:<artifact>:<type>:<version>
36
+ # or
37
+ # <group>:<artifact>:<type>:<classifier><:<version>
34
38
  def to_spec()
35
39
  classifier.blank? ? "#{group}:#{id}:#{type}:#{version}" : "#{group}:#{id}:#{type}:#{classifier}:#{version}"
36
40
  end
37
41
 
38
42
  # Apply specification to this artifact.
39
43
  def apply_spec(spec)
40
- case spec
41
- when String
42
- @group, @id, @type, @version, *rest = spec.split(":")
43
- unless rest.empty?
44
- @classifier, @version = @version, rest.shift
45
- fail "Expecting project:id:type:version or project:id:type:classifier:version, found #{spec}" unless rest.empty?
46
- end
47
- when Hash
48
- [:group, :id, :type, :version, :classifier].each { |key| instance_variable_set("@#{key}", spec[key]) }
49
- @type ||= DEFAULT_FILE_TYPE
50
- else
51
- raise ArgumentError, "Expecting a string or a hash"
52
- end
44
+ spec = Artifact.to_hash(spec)
45
+ ARTIFACT_ATTRIBUTES.each { |key| instance_variable_set("@#{key}", spec[key]) }
53
46
  self
54
47
  end
55
48
 
49
+ # Convenience method that returns a POM artifact.
56
50
  def pom()
57
51
  return self if type.to_s == "pom"
58
52
  artifact(:group=>group, :id=>id, :version=>version, :type=>"pom", :classifier=>classifier)
@@ -61,7 +55,11 @@ module Buildr
61
55
  end
62
56
 
63
57
 
64
- # Use the artifact and artifacts method to create artifact tasks.
58
+ # The Artifact task maps to an artifact file in the local repository
59
+ # and knows how to download the file from a remote repository.
60
+ #
61
+ # The task will only download the file if it does not exist. You can
62
+ # enhance the task to create the artifact yourself.
65
63
  class Artifact < Rake::FileCreationTask
66
64
 
67
65
  # The default file type for artifacts, if not specified.
@@ -71,64 +69,67 @@ module Buildr
71
69
 
72
70
  class << self
73
71
 
74
- # Lookup an artifact task based on its specification.
72
+ # Lookup a previously registered artifact task based on the
73
+ # artifact specification (string or hash).
75
74
  def lookup(spec)
76
75
  @artifacts ||= {}
77
- @artifacts[hash_to_spec(spec)]
76
+ @artifacts[to_spec(spec)]
78
77
  end
79
78
 
80
- # Register a task as an artifact. Returns the task when calling
81
- # #artifacts. For example, a project will use this to register the
82
- # packages it creates.
83
- def register(task)
79
+ # Register an artifact task(s) for later lookup (see #lookup).
80
+ def register(*tasks)
84
81
  @artifacts ||= {}
85
- if task.respond_to?(:to_hash)
86
- @artifacts[hash_to_spec(task.to_hash)] = task
87
- else
88
- fail "Can only call with an artifact task"
89
- end
82
+ fail "You can only register an artifact task, strings and hashes are just not good enough" unless
83
+ tasks.all? { |task| task.respond_to?(:to_spec) && task.respond_to?(:invoke) }
84
+ tasks.each { |task| @artifacts[task.to_spec] = task }
85
+ tasks
90
86
  end
91
87
 
92
88
  # Turn a spec into a hash. This method accepts a string, hash or any object
93
- # that responds to the method to_spec. There are several reasons you'll want
94
- # to use this method:
95
- # * You can pass it anything that could possibly be a spec, and get a hash.
96
- # * It will check that your spec includes the group identifier, artifact
97
- # identifier and version number, and set the file type, if not specified.
89
+ # that responds to the method to_spec. There are several reasons to use this
90
+ # method:
91
+ # * You can pass anything that could possibly be a spec, and get a hash.
92
+ # * It will check that the spec includes the group identifier, artifact
93
+ # identifier and version number and set the file type, if missing.
98
94
  # * It will always return a new specs hash.
99
- # * Calling to_s on the hash will return a spec string.
100
95
  #
101
96
  # :nodoc:
102
- def spec_to_hash(spec)
103
- return spec_to_hash(spec.to_spec) if spec.respond_to?(:to_spec)
104
- if String === spec
105
- group, id, type, version, *rest = spec.split(":")
106
- unless rest.empty?
107
- classifier, version = version, rest.shift
108
- fail "Expecting project:id:type:version or project:id:type:classifier:version, found #{spec}" unless rest.empty?
109
- end
110
- fail "Missing file type for #{spec}" if type.blank?
111
- spec_to_hash :group=>group, :id=>id, :type=>type, :version=>version, :classifier=>classifier
112
- elsif Hash === spec
113
- spec = spec.clone
97
+ def to_hash(spec)
98
+ if spec.respond_to?(:to_spec)
99
+ to_hash spec.to_spec
100
+ elsif Hash === spec
101
+ # Sanitize the hash and check it's valid.
102
+ spec = ARTIFACT_ATTRIBUTES.inject({}) { |h, k| h[k] = spec[k] ; h }
114
103
  fail "Missing group identifier for #{spec.inspect}" if spec[:group].blank?
115
104
  fail "Missing artifact identifier for #{spec.inspect}" if spec[:id].blank?
116
105
  fail "Missing version for #{spec.inspect}" if spec[:version].blank?
117
106
  spec[:type] = DEFAULT_FILE_TYPE if spec[:type].blank?
118
107
  spec
108
+ elsif String === spec
109
+ group, id, type, version, *rest = spec.split(":")
110
+ unless rest.empty?
111
+ # Optional classifier comes before version.
112
+ classifier, version = version, rest.shift
113
+ fail "Expecting <project:id:type:version> or <project:id:type:classifier:version>, found <#{spec}>" unless rest.empty?
114
+ end
115
+ to_hash :group=>group, :id=>id, :type=>type, :version=>version, :classifier=>classifier
119
116
  else
120
- fail "Spec must be a string, hash or object that responds to to_spec"
117
+ fail "Expecting a String, Hash or object that responds to to_spec"
121
118
  end
122
119
  end
123
120
 
124
- # Convert a hash back to a spec string.
125
- def hash_to_spec(hash)
121
+ # Convert a hash back to a spec string. This method accepts
122
+ # a string, hash or any object that responds to to_spec.
123
+ # :nodoc:
124
+ def to_spec(hash)
125
+ hash = to_hash(hash) unless Hash === hash
126
126
  version = ":#{hash[:version]}" unless hash[:version].blank?
127
127
  classifier = ":#{hash[:classifier]}" unless hash[:classifier].blank?
128
128
  "#{hash[:group]}:#{hash[:id]}:#{hash[:type] || DEFAULT_FILE_TYPE}#{classifier}#{version}"
129
129
  end
130
130
 
131
131
  # Convert a hash to a file name.
132
+ # :nodoc:
132
133
  def hash_to_file_name(hash)
133
134
  version = "-#{hash[:version]}" unless hash[:version].blank?
134
135
  classifier = "-#{hash[:classifier]}" unless hash[:classifier].blank?
@@ -137,30 +138,32 @@ module Buildr
137
138
 
138
139
  end
139
140
 
140
- def initialize(*args)
141
+ def execute()
142
+ # Default behavior: download the artifact from one of the remote
143
+ # repositories if the file does not exist. But this default behavior
144
+ # is counter useful if the artifact knows how to build itself
145
+ # (e.g. download from a different location), so don't perform it
146
+ # if the task found a different way to create the artifact.
141
147
  super
142
- enhance do |task|
143
- # Download the artifact form one of the remote repositories if the
144
- # file does not exist in the local repository, and no other behavior
145
- # specified. If this task has been enhanced, delegate to the other
146
- # enhancers. We don't know if this is the first or other enhancement,
147
- # but we only need to know it's not the only one.
148
- if @actions.size == 1
149
- repositories.download(to_spec)
150
- end
148
+ unless Rake.application.options.dryrun || File.exist?(name)
149
+ repositories.download(to_spec)
151
150
  end
152
151
  end
153
152
 
154
153
  end
155
154
 
156
155
 
157
- # Singleton object for specifying the local, remote and release repositories.
156
+ # Holds the path to the local repository, URLs for remote repositories, and
157
+ # settings for the deployment repository.
158
158
  class Repositories
159
159
  include Singleton
160
160
 
161
161
  # Returns the path to the local repository.
162
162
  #
163
163
  # The default path is .m2/repository relative to the home directory.
164
+ # You can change the location of the local repository by using a symbol
165
+ # link or by setting a different path. If you set a different path, do it
166
+ # in the buildr.rb file instead of the Rakefile.
164
167
  def local()
165
168
  @local ||= ENV["local_repo"] || File.join(ENV["HOME"], ".m2", "repository")
166
169
  end
@@ -176,7 +179,7 @@ module Buildr
176
179
  # locate :group=>"log4j", :id=>"log4j", :version=>"1.1"
177
180
  # => ~/.m2/repository/log4j/log4j/1.1/log4j-1.1.jar
178
181
  def locate(spec)
179
- spec = Artifact.spec_to_hash(spec) unless Hash === spec
182
+ spec = Artifact.to_hash(spec)
180
183
  File.join(local, spec[:group].split("."), spec[:id], spec[:version], Artifact.hash_to_file_name(spec))
181
184
  end
182
185
 
@@ -210,10 +213,10 @@ module Buildr
210
213
  # and store it in the local repository. Returns the path if downloaded,
211
214
  # otherwise raises an exception.
212
215
  def download(spec)
213
- spec = Artifact.spec_to_hash(spec) unless Hash === spec
216
+ spec = Artifact.to_hash(spec) unless Hash === spec
214
217
  path = locate(spec)
215
218
 
216
- puts "Downloading #{Artifact.hash_to_spec(spec)}" if Rake.application.options.trace
219
+ puts "Downloading #{Artifact.to_spec(spec)}" if Rake.application.options.trace
217
220
  return path if remote.any? do |repo_id, repo_url|
218
221
  begin
219
222
  rel_path = spec[:group].gsub(".", "/") +
@@ -232,13 +235,16 @@ module Buildr
232
235
  false
233
236
  end
234
237
  end
235
- fail "Failed to download #{Artifact.hash_to_spec(spec)}, tried the following repositories:\n#{repositories.remote.values.join("\n")}"
238
+ fail "Failed to download #{Artifact.to_spec(spec)}, tried the following repositories:\n#{repositories.remote.values.join("\n")}"
236
239
  end
237
240
 
238
- # Specifies deployment target for all artifacts generated by this project.
241
+ # Specifies the deployment repository. Accepts a hash with the different
242
+ # repository settings (e.g. url, username, password). Anything else is
243
+ # interepted as the URL.
239
244
  #
240
245
  # For example:
241
- # repositories.deploy_to = "sftp://example.com/var/www/maven/"
246
+ # repositories.deploy_to = { :url=>"sftp://example.com/var/www/maven/",
247
+ # :username="john", :password=>"secret" }
242
248
  # or:
243
249
  # repositories.deploy_to = "sftp://john:secret@example.com/var/www/maven/"
244
250
  def deploy_to=(options)
@@ -246,13 +252,21 @@ module Buildr
246
252
  @deploy_to = options
247
253
  end
248
254
 
255
+ # Returns the current deployment repository configuration. This is a more
256
+ # convenient way to specify deployment in the Rakefile, and override it
257
+ # locally. For example:
258
+ # # Rakefile
259
+ # repositories.deploy_to[:url] ||= "sftp://example.com"
260
+ # # buildr.rb
261
+ # repositories.deploy_to[:url] = "sftp://acme.org"
249
262
  def deploy_to()
250
- @deploy_to || {}
263
+ @deploy_to ||= {}
251
264
  end
252
265
 
253
266
  end
254
267
 
255
- # Returns a global object for setting local and remote repositories.
268
+
269
+ # Returns a global object for setting local, remote and deploy repositories.
256
270
  # See Repositories.
257
271
  def repositories()
258
272
  Repositories.instance
@@ -276,14 +290,13 @@ module Buildr
276
290
  # download(artifact("dojo:dojo-widget:zip:2.0")=>
277
291
  # "http://download.dojotoolkit.org/release-2.0/dojo-2.0-widget.zip")
278
292
  def artifact(spec, &block)
279
- spec = Artifact.spec_to_hash(spec)
293
+ spec = Artifact.to_hash(spec)
280
294
  unless task = Artifact.lookup(spec)
281
295
  task = Artifact.define_task(repositories.locate(spec))
282
296
  Artifact.register(task)
283
297
  end
284
298
  task.apply_spec spec
285
- task.enhance(&block) if block_given?
286
- task
299
+ task.enhance &block
287
300
  end
288
301
 
289
302
  # Creates multiple artifacts from a set of specifications and returns
@@ -320,7 +333,7 @@ module Buildr
320
333
  case spec
321
334
  when Hash
322
335
  set |= [artifact(spec)]
323
- when /:.*:.*:/
336
+ when /:/
324
337
  set |= [artifact(spec)]
325
338
  when String
326
339
  set |= [file(spec)]
@@ -331,7 +344,7 @@ module Buildr
331
344
  when Array
332
345
  set |= artifacts(*spec)
333
346
  else
334
- fail "Invalid artifact specification: #{spec || 'nil'}"
347
+ fail "Invalid artifact specification: #{spec.to_s || 'nil'}"
335
348
  end
336
349
  set
337
350
  end
@@ -1,25 +1,82 @@
1
+ require "open3"
2
+
3
+
1
4
  module Buildr
2
5
 
6
+ BUILD_TASKS = {
7
+ :build =>"Build the project",
8
+ :clean =>"Clean files generated during a build",
9
+ :package =>"Create packages",
10
+ :install =>"Install packages created by the project",
11
+ :uninstall=>"Remove previously installed packages",
12
+ :deploy =>"Deploy packages created by the project"
13
+ }
14
+
15
+ # Handles the build and clean tasks.
16
+ BUILD_TASKS.each { |name, comment| LocalDirectoryTask.define_task(name).add_comment(comment) }
17
+
18
+ Project.on_define do |project|
19
+ BUILD_TASKS.each { |name, comment| project.recursive_task name }
20
+ end
21
+
22
+ class Project
23
+ def build(*args, &block)
24
+ returning(@build_task ||= task("build")) do |task|
25
+ task.enhance args, &block
26
+ end
27
+ end
28
+
29
+ def clean(*args, &block)
30
+ returning(@clean_task ||= task("clean")) do |task|
31
+ task.enhance args, &block
32
+ end
33
+ end
34
+ end
35
+
36
+ Project.on_define do |project|
37
+ project.build
38
+ project.clean
39
+ end
40
+
41
+
3
42
  class ReleaseTask < Rake::Task
4
43
 
5
44
  VERSION_NUMBER_PATTERN = /VERSION_NUMBER\s*=\s*(["'])(.*)\1/
6
45
  NEXT_VERSION_PATTERN = /NEXT_VERSION\s*=\s*(["'])(.*)\1/
7
46
 
47
+ class << self
48
+ def svn_ignores()
49
+ @ignores = (@ignores || []).map { |pat| pat.is_a?(Regexp) ? pat : Regexp.new("^.*\s+#{Regexp.escape pat}$") }
50
+ end
51
+ end
52
+
8
53
  def initialize(*args)
9
54
  super
10
55
  enhance do |task|
11
56
  # Make sure we don't have anything uncommitted in SVN.
12
- fail "Uncommitted SVN files violate the First Principle Of Release!" unless
13
- svn("status").empty?
14
- # Load the Rakefile and find the version numbers.
57
+ check_status
58
+ # Update current version to next version before deploying.
15
59
  next_ver = update_version
16
- # Run the deployment externally using the new version number.
60
+ # Run the deployment externally using the new version number
61
+ # (from the modified Rakefile).
17
62
  sh "rake deploy"
63
+ # Update the next version as well to the next increment and commit.
18
64
  update_next_version next_ver
19
- tag_repository
65
+ # Tag the repository for this release.
66
+ tag_repository next_ver
67
+ # Update the next version to end with -SNAPSHOT.
68
+ update_version_to_snapshot next_ver
20
69
  end
21
70
  end
22
71
 
72
+ def check_status()
73
+ ignores = ReleaseTask.svn_ignores
74
+ status = svn("status", "--ignore-externals", :verbose=>false).
75
+ reject { |line| line =~ /^X\s/ || ignores.any? { |pat| line =~ pat } }
76
+ fail "Uncommitted SVN files violate the First Principle Of Release!\n#{status}" unless
77
+ status.empty?
78
+ end
79
+
23
80
  # Change the Rakefile and update the current version number to the
24
81
  # next version number (VERSION_NUMBER = NEXT_VERSION). We need this
25
82
  # before making a release with the next version. Return the next version.
@@ -54,54 +111,43 @@ module Buildr
54
111
  File.open(Rake.application.rakefile, "w") { |file| file.write rakefile }
55
112
 
56
113
  # Commit new version number.
57
- svn "commit", "-m", "Changed release number to #{version}"
114
+ svn "commit", "-m", "Changed version number to #{version}", Rake.application.rakefile
58
115
  end
59
116
 
60
117
  # Create a tag in the SVN repository.
61
- def tag_repository()
118
+ def tag_repository(version)
62
119
  # Copy to tag.
63
120
  cur_url = svn("info").scan(/URL: (.*)/)[0][0]
64
- new_url = cur_url.sub(/trunk$/, "tags/#{cur_ver}")
65
- svn "copy", cur_url, new_url
121
+ new_url = cur_url.sub(/trunk$/, "tags/#{version}")
122
+ svn "remove", new_url, "-m", "Removing old copy" rescue nil
123
+ svn "copy", cur_url, new_url, "-m", "Release #{version}"
66
124
  end
67
125
 
68
- def svn(*args)
69
- stdin, stdout, stderr = Open3.popen3("svn", *args)
70
- stdin.close
71
- error = stderr.read
72
- fail error unless error.empty?
73
- stdout.read
126
+ def update_version_to_snapshot(version)
127
+ version += "-SNAPSHOT"
128
+ rakefile = File.read(Rake.application.rakefile)
129
+ rakefile.gsub!(VERSION_NUMBER_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{version}"}) }
130
+ File.open(Rake.application.rakefile, "w") { |file| file.write rakefile }
131
+ # Commit new version number.
132
+ svn "commit", "-m", "Changed version number to #{version}", Rake.application.rakefile
74
133
  end
75
- end
76
134
 
77
- # Handles the build and clean tasks.
78
- desc "Clean all projects"
79
- LocalDirectoryTask.define_task("clean")
80
- desc "Build all projects"
81
- LocalDirectoryTask.define_task("build")
82
- desc "Make a release"
83
- ReleaseTask.define_task "release"
84
-
85
- class Project
86
- def build(*args, &block)
87
- returning(@build_task ||= recursive_task("build")) do |task|
88
- task.enhance args, &block
135
+ def svn(*args)
136
+ if Hash === args.last
137
+ options = args.pop
138
+ else
139
+ options = { :verbose=>verbose }
89
140
  end
90
- end
91
-
92
- def clean(*args, &block)
93
- returning(@clean_task ||= recursive_task("clean")) do |task|
94
- task.enhance args, &block
141
+ puts ["svn", *args].join(" ") if options[:verbose]
142
+ Open3.popen3("svn", *args) do |stdin, stdout, stderr|
143
+ stdin.close
144
+ error = stderr.read
145
+ fail error unless error.empty?
146
+ returning(stdout.read) { |output| puts output if Rake.application.options.trace }
95
147
  end
96
148
  end
97
149
  end
98
150
 
99
- Project.on_create do |project|
100
- desc "Clean all files generated during the build process"
101
- project.clean
102
-
103
- desc "Build this project"
104
- project.build
105
- end
106
-
151
+ desc "Make a release"
152
+ ReleaseTask.define_task "release"
107
153
  end