buildr 1.3.2-java → 1.3.3-java

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.
Files changed (124) hide show
  1. data/CHANGELOG +66 -4
  2. data/{README → README.rdoc} +29 -16
  3. data/Rakefile +16 -20
  4. data/_buildr +38 -0
  5. data/addon/buildr/cobertura.rb +49 -45
  6. data/addon/buildr/emma.rb +238 -0
  7. data/addon/buildr/jetty.rb +1 -1
  8. data/addon/buildr/nailgun.rb +585 -661
  9. data/{lib/buildr/java → addon/buildr}/org/apache/buildr/BuildrNail$Main.class +0 -0
  10. data/{lib/buildr/java → addon/buildr}/org/apache/buildr/BuildrNail.class +0 -0
  11. data/{lib/buildr/java → addon/buildr}/org/apache/buildr/BuildrNail.java +0 -0
  12. data/bin/buildr +9 -2
  13. data/buildr.buildfile +53 -0
  14. data/buildr.gemspec +21 -14
  15. data/doc/css/default.css +51 -48
  16. data/doc/css/print.css +60 -55
  17. data/doc/images/favicon.png +0 -0
  18. data/doc/images/growl-icon.tiff +0 -0
  19. data/doc/images/project-structure.png +0 -0
  20. data/doc/pages/artifacts.textile +46 -156
  21. data/doc/pages/building.textile +63 -323
  22. data/doc/pages/contributing.textile +112 -102
  23. data/doc/pages/download.textile +19 -27
  24. data/doc/pages/extending.textile +27 -81
  25. data/doc/pages/getting_started.textile +44 -119
  26. data/doc/pages/index.textile +26 -47
  27. data/doc/pages/languages.textile +407 -0
  28. data/doc/pages/more_stuff.textile +92 -173
  29. data/doc/pages/packaging.textile +71 -239
  30. data/doc/pages/projects.textile +58 -233
  31. data/doc/pages/recipes.textile +19 -43
  32. data/doc/pages/settings_profiles.textile +39 -104
  33. data/doc/pages/testing.textile +41 -304
  34. data/doc/pages/troubleshooting.textile +29 -47
  35. data/doc/pages/whats_new.textile +69 -167
  36. data/doc/print.haml +0 -1
  37. data/doc/print.toc.yaml +1 -0
  38. data/doc/scripts/buildr-git.rb +1 -1
  39. data/doc/site.haml +1 -0
  40. data/doc/site.toc.yaml +8 -5
  41. data/{KEYS → etc/KEYS} +0 -0
  42. data/etc/git-svn-authors +16 -0
  43. data/lib/buildr.rb +2 -5
  44. data/lib/buildr/core/application.rb +192 -98
  45. data/lib/buildr/core/build.rb +140 -91
  46. data/lib/buildr/core/checks.rb +5 -5
  47. data/lib/buildr/core/common.rb +1 -1
  48. data/lib/buildr/core/compile.rb +12 -10
  49. data/lib/buildr/core/filter.rb +151 -46
  50. data/lib/buildr/core/generate.rb +9 -9
  51. data/lib/buildr/core/progressbar.rb +1 -1
  52. data/lib/buildr/core/project.rb +8 -7
  53. data/lib/buildr/core/test.rb +51 -26
  54. data/lib/buildr/core/transports.rb +22 -38
  55. data/lib/buildr/core/util.rb +78 -26
  56. data/lib/buildr/groovy.rb +18 -0
  57. data/lib/buildr/groovy/bdd.rb +105 -0
  58. data/lib/buildr/groovy/compiler.rb +138 -0
  59. data/lib/buildr/ide/eclipse.rb +102 -71
  60. data/lib/buildr/ide/idea.rb +7 -12
  61. data/lib/buildr/ide/idea7x.rb +7 -8
  62. data/lib/buildr/java.rb +4 -7
  63. data/lib/buildr/java/ant.rb +26 -5
  64. data/lib/buildr/java/bdd.rb +449 -0
  65. data/lib/buildr/java/commands.rb +9 -9
  66. data/lib/buildr/java/{compilers.rb → compiler.rb} +8 -90
  67. data/lib/buildr/java/jruby.rb +29 -11
  68. data/lib/buildr/java/jtestr_runner.rb.erb +116 -0
  69. data/lib/buildr/java/packaging.rb +23 -16
  70. data/lib/buildr/java/pom.rb +1 -1
  71. data/lib/buildr/java/rjb.rb +21 -8
  72. data/lib/buildr/java/test_result.rb +308 -0
  73. data/lib/buildr/java/tests.rb +324 -0
  74. data/lib/buildr/packaging/artifact.rb +12 -11
  75. data/lib/buildr/packaging/artifact_namespace.rb +7 -4
  76. data/lib/buildr/packaging/gems.rb +3 -3
  77. data/lib/buildr/packaging/zip.rb +13 -10
  78. data/lib/buildr/resources/buildr.icns +0 -0
  79. data/lib/buildr/scala.rb +19 -0
  80. data/lib/buildr/scala/compiler.rb +109 -0
  81. data/lib/buildr/scala/tests.rb +203 -0
  82. data/rakelib/apache.rake +71 -45
  83. data/rakelib/doc.rake +2 -2
  84. data/rakelib/package.rake +3 -2
  85. data/rakelib/rspec.rake +23 -21
  86. data/rakelib/setup.rake +34 -9
  87. data/rakelib/stage.rake +4 -1
  88. data/spec/addon/cobertura_spec.rb +77 -0
  89. data/spec/addon/emma_spec.rb +120 -0
  90. data/spec/addon/test_coverage_spec.rb +255 -0
  91. data/spec/{application_spec.rb → core/application_spec.rb} +82 -4
  92. data/spec/{artifact_namespace_spec.rb → core/artifact_namespace_spec.rb} +12 -1
  93. data/spec/core/build_spec.rb +415 -0
  94. data/spec/{checks_spec.rb → core/checks_spec.rb} +2 -2
  95. data/spec/{common_spec.rb → core/common_spec.rb} +119 -30
  96. data/spec/{compile_spec.rb → core/compile_spec.rb} +17 -13
  97. data/spec/core/generate_spec.rb +33 -0
  98. data/spec/{project_spec.rb → core/project_spec.rb} +9 -6
  99. data/spec/{test_spec.rb → core/test_spec.rb} +222 -28
  100. data/spec/{transport_spec.rb → core/transport_spec.rb} +5 -9
  101. data/spec/groovy/bdd_spec.rb +80 -0
  102. data/spec/{groovy_compilers_spec.rb → groovy/compiler_spec.rb} +1 -1
  103. data/spec/ide/eclipse_spec.rb +243 -0
  104. data/spec/{java_spec.rb → java/ant.rb} +7 -17
  105. data/spec/java/bdd_spec.rb +358 -0
  106. data/spec/{java_compilers_spec.rb → java/compiler_spec.rb} +1 -1
  107. data/spec/java/java_spec.rb +88 -0
  108. data/spec/{java_packaging_spec.rb → java/packaging_spec.rb} +65 -4
  109. data/spec/{java_test_frameworks_spec.rb → java/tests_spec.rb} +31 -10
  110. data/spec/{archive_spec.rb → packaging/archive_spec.rb} +12 -2
  111. data/spec/{artifact_spec.rb → packaging/artifact_spec.rb} +12 -5
  112. data/spec/{packaging_helper.rb → packaging/packaging_helper.rb} +0 -0
  113. data/spec/{packaging_spec.rb → packaging/packaging_spec.rb} +1 -1
  114. data/spec/sandbox.rb +22 -5
  115. data/spec/{scala_compilers_spec.rb → scala/compiler_spec.rb} +1 -1
  116. data/spec/{scala_test_frameworks_spec.rb → scala/tests_spec.rb} +11 -12
  117. data/spec/spec_helpers.rb +38 -17
  118. metadata +93 -70
  119. data/lib/buildr/java/bdd_frameworks.rb +0 -265
  120. data/lib/buildr/java/groovyc.rb +0 -137
  121. data/lib/buildr/java/test_frameworks.rb +0 -450
  122. data/spec/build_spec.rb +0 -193
  123. data/spec/java_bdd_frameworks_spec.rb +0 -238
  124. data/spec/spec.opts +0 -6
@@ -27,7 +27,7 @@ module Buildr
27
27
  # Runs the build in parallel when true (defaults to false). You can force a parallel build by
28
28
  # setting this option directly, or by running the parallel task ahead of the build task.
29
29
  #
30
- # This option only affects recurvise tasks. For example:
30
+ # This option only affects recursive tasks. For example:
31
31
  # buildr parallel package
32
32
  # will run all package tasks (from the sub-projects) in parallel, but each sub-project's package
33
33
  # task runs its child tasks (prepare, compile, resources, etc) in sequence.
@@ -48,7 +48,7 @@ module Buildr
48
48
  desc 'Clean files generated during a build'
49
49
  Project.local_task('clean') { |name| "Cleaning #{name}" }
50
50
 
51
- desc 'The default task it build'
51
+ desc 'The default task is build'
52
52
  task 'default'=>'build'
53
53
  end
54
54
 
@@ -109,146 +109,195 @@ module Buildr
109
109
  end
110
110
 
111
111
 
112
+ class Svn
113
+
114
+ class << self
115
+ def commit(file, message)
116
+ svn 'commit', '-m', message, file
117
+ end
118
+
119
+ def copy(dir, url, message)
120
+ svn 'copy', dir, url, '-m', message
121
+ end
122
+
123
+ # Return the current SVN URL
124
+ def repo_url
125
+ svn('info').scan(/URL: (.*)/)[0][0]
126
+ end
127
+
128
+ def remove(url, message)
129
+ svn 'remove', url, '-m', message
130
+ end
131
+
132
+ # Status check reveals modified files, but also SVN externals which we can safely ignore.
133
+ def uncommitted_files
134
+ svn('status', '--ignore-externals').reject { |line| line =~ /^X\s/ }
135
+ end
136
+
137
+ # :call-seq:
138
+ # svn(*args)
139
+ #
140
+ # Executes SVN command and returns the output.
141
+ def svn(*args)
142
+ cmd = 'svn ' + args.map { |arg| arg[' '] ? %Q{"#{arg}"} : arg }.join(' ')
143
+ trace cmd
144
+ `#{cmd}`.tap { fail 'SVN command failed' unless $?.exitstatus == 0 }
145
+ end
146
+ end
147
+ end
148
+
149
+
112
150
  class Release
113
151
 
114
- THIS_VERSION_PATTERN = /THIS_VERSION|VERSION_NUMBER\s*=\s*(["'])(.*)\1/
115
- NEXT_VERSION_PATTERN = /NEXT_VERSION\s*=\s*(["'])(.*)\1/
152
+ THIS_VERSION_PATTERN = /(THIS_VERSION|VERSION_NUMBER)\s*=\s*(["'])(.*)\2/
116
153
 
117
154
  class << self
155
+
156
+ # Use this to specify a different tag name for tagging the release in source control.
157
+ # You can set the tag name or a proc that will be called with the version number,
158
+ # for example:
159
+ # Release.tag_name = lambda { |ver| "foo-#{ver}" }
160
+ attr_accessor :tag_name
118
161
 
119
162
  # :call-seq:
120
163
  # make()
121
164
  #
122
165
  # Make a release.
123
- def make()
166
+ def make
124
167
  check
125
- version = with_next_version do |filename, version|
126
- options = ['--buildfile', filename, 'DEBUG=no']
168
+ with_release_candidate_version do |release_candidate_buildfile|
169
+ options = ['--buildfile', release_candidate_buildfile, 'DEBUG=no']
127
170
  options << '--environment' << Buildr.environment unless Buildr.environment.to_s.empty?
128
- sh "#{command} _#{Buildr::VERSION}_ clean upload #{options.join(' ')}"
171
+ buildr %w{clean upload}, options
129
172
  end
130
- tag version
131
- commit version + '-SNAPSHOT'
173
+ tag_release
174
+ commit_new_snapshot
132
175
  end
133
176
 
134
- protected
135
-
136
- def command() #:nodoc:
137
- Config::CONFIG['arch'] =~ /dos|win32/i ? $PROGRAM_NAME.ext('cmd') : $PROGRAM_NAME
177
+ # :call-seq:
178
+ # extract_version() => this_version
179
+ #
180
+ # Extract the current version number from the buildfile.
181
+ # Raise an error if not found.
182
+ def extract_version
183
+ buildfile = File.read(Buildr.application.buildfile.to_s)
184
+ buildfile.scan(THIS_VERSION_PATTERN)[0][2]
185
+ rescue
186
+ fail 'Looking for THIS_VERSION = "..." in your Buildfile, none found'
138
187
  end
139
-
188
+
189
+ # :call-seq:
190
+ # tag_url(svn_url, version) => tag_url
191
+ #
192
+ # Returns the SVN url for the tag.
193
+ # Can tag from the trunk or from branches.
194
+ # Can handle the two standard repository layouts.
195
+ # - http://my.repo/foo/trunk => http://my.repo/foo/tags/1.0.0
196
+ # - http://my.repo/trunk/foo => http://my.repo/tags/foo/1.0.0
197
+ def tag_url(svn_url, version)
198
+ trunk_or_branches = Regexp.union(%r{^(.*)/trunk(.*)$}, %r{^(.*)/branches(.*)/([^/]*)$})
199
+ match = trunk_or_branches.match(svn_url)
200
+ prefix = match[1] || match[3]
201
+ suffix = match[2] || match[4]
202
+ tag = tag_name || version
203
+ tag = tag.call(version) if Proc === tag
204
+ prefix + '/tags' + suffix + '/' + tag
205
+ end
206
+
140
207
  # :call-seq:
141
208
  # check()
142
209
  #
143
210
  # Check that we don't have any local changes in the working copy. Fails if it finds anything
144
211
  # in the working copy that is not checked into source control.
145
- def check()
146
- fail "SVN URL must end with 'trunk' or 'branches/...'" unless svn_url =~ /(trunk)|(branches.*)$/
147
- # Status check reveals modified file, but also SVN externals which we can safely ignore.
148
- status = svn('status', '--ignore-externals').reject { |line| line =~ /^X\s/ }
149
- fail "Uncommitted SVN files violate the First Principle Of Release!\n#{status}" unless
150
- status.empty?
212
+ def check
213
+ fail "SVN URL must contain 'trunk' or 'branches/...'" unless Svn.repo_url =~ /(trunk)|(branches.*)$/
214
+ fail "Uncommitted SVN files violate the First Principle Of Release!\n#{Svn.uncommitted_files}" unless Svn.uncommitted_files.empty?
151
215
  end
152
216
 
217
+ protected
218
+
153
219
  # :call-seq:
154
- # with_next_version() { |filename| ... } => version
220
+ # buildr(tasks, options)
155
221
  #
156
- # Yields to block with upgraded version number, before committing to use it. Returns the *new*
157
- # current version number.
222
+ # Calls another instance of buildr.
223
+ def buildr(tasks, options)
224
+ sh "#{command} _#{Buildr::VERSION}_ #{tasks.join(' ')} #{options.join(' ')}"
225
+ end
226
+
227
+ def command #:nodoc:
228
+ Config::CONFIG['arch'] =~ /dos|win32/i ? $PROGRAM_NAME.ext('cmd') : $PROGRAM_NAME
229
+ end
230
+
231
+ # :call-seq:
232
+ # with_release_candidate_version() { |filename| ... }
233
+ #
234
+ # Yields to block with release candidate buildfile, before committing to use it.
158
235
  #
159
236
  # We need a Buildfile with upgraded version numbers to run the build, but we don't want the
160
- # Buildfile modified unless the build succeeds. So this method updates the version numbers in
237
+ # Buildfile modified unless the build succeeds. So this method updates the version number in
161
238
  # a separate (Buildfile.next) file, yields to the block with that filename, and if successful
162
239
  # copies the new file over the existing one.
163
240
  #
164
- # Version numbers are updated as follows. The next release version becomes the current one,
165
- # and the next version is upgraded by one to become the new next version. So:
166
- # THIS_VERSION = 1.1.0
167
- # NEXT_VERSION = 1.2.0
241
+ # The release version is the current version without '-SNAPSHOT'. So:
242
+ # THIS_VERSION = 1.1.0-SNAPSHOT
168
243
  # becomes:
169
- # THIS_VERSION = 1.2.0
170
- # NEXT_VERSION = 1.2.1
171
- # and the method will return 1.2.0.
172
- def with_next_version()
173
- new_filename = Buildr.application.buildfile + '.next'
174
- modified = change_version do |this_version, next_version|
175
- one_after = next_version.split('.')
176
- one_after[-1] = one_after[-1].to_i + 1
177
- [ next_version, one_after.join('.') ]
178
- end
179
- File.open(new_filename, 'w') { |file| file.write modified }
244
+ # THIS_VERSION = 1.1.0
245
+ # for the release buildfile.
246
+ def with_release_candidate_version
247
+ release_candidate_buildfile = Buildr.application.buildfile.to_s + '.next'
248
+ release_candidate_buildfile_contents = change_version { |version| version[-1] = version[-1].to_i }
249
+ File.open(release_candidate_buildfile, 'w') { |file| file.write release_candidate_buildfile_contents }
180
250
  begin
181
- yield new_filename
182
- mv new_filename, Buildr.application.buildfile
251
+ yield release_candidate_buildfile
252
+ mv release_candidate_buildfile, Buildr.application.buildfile.to_s
183
253
  ensure
184
- rm new_filename rescue nil
254
+ rm release_candidate_buildfile rescue nil
185
255
  end
186
- File.read(Buildr.application.buildfile).scan(THIS_VERSION_PATTERN)[0][1]
187
256
  end
188
257
 
189
258
  # :call-seq:
190
- # change_version() { |this, next| ... } => buildfile
259
+ # change_version() { |this_version| ... } => buildfile
191
260
  #
192
- # Change version numbers in the current Buildfile, but without writing a new file (yet).
193
- # Returns the contents of the Buildfile with the modified version numbers.
261
+ # Change version number in the current Buildfile, but without writing a new file (yet).
262
+ # Returns the contents of the Buildfile with the modified version number.
194
263
  #
195
- # This method yields to the block with the current (this) and next version numbers and expects
196
- # an array with the new this and next version numbers.
197
- def change_version()
198
- buildfile = File.read(Buildr.application.buildfile)
199
- this_version = buildfile.scan(THIS_VERSION_PATTERN)[0][1] or
200
- fail "Looking for THIS_VERSION = \"...\" in your Buildfile, none found"
201
- next_version = buildfile.scan(NEXT_VERSION_PATTERN)[0][1] or
202
- fail "Looking for NEXT_VERSION = \"...\" in your Buildfile, none found"
203
- this_version, next_version = yield(this_version, next_version)
204
- if verbose
205
- puts 'Upgrading version numbers:'
206
- puts " This: #{this_version}"
207
- puts " Next: #{next_version}"
208
- end
209
- buildfile.gsub(THIS_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{this_version}"}) }.
210
- gsub(NEXT_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{next_version}"}) }
264
+ # This method yields to the block with the current (this) version number as an array and expects
265
+ # the block to update it.
266
+ def change_version
267
+ this_version = extract_version
268
+ new_version = this_version.split('.')
269
+ yield(new_version)
270
+ new_version = new_version.join('.')
271
+ buildfile = File.read(Buildr.application.buildfile.to_s)
272
+ buildfile.gsub(THIS_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{new_version}"}) }
211
273
  end
212
274
 
213
275
  # :call-seq:
214
- # tag(version)
276
+ # tag_release()
215
277
  #
216
278
  # Tags the current working copy with the release version number.
217
- def tag(version)
218
- url = svn_url.sub(/(trunk$)|(branches.*)$/, "tags/#{version}")
219
- svn 'remove', url, '-m', 'Removing old copy' rescue nil
220
- svn 'copy', Dir.pwd, url, '-m', "Release #{version}"
279
+ def tag_release
280
+ version = extract_version
281
+ info "Tagging release #{version}"
282
+ url = tag_url Svn.repo_url, version
283
+ Svn.remove url, 'Removing old copy' rescue nil
284
+ Svn.copy Dir.pwd, url, "Release #{version}"
221
285
  end
222
286
 
223
287
  # :call-seq:
224
- # commit(version)
288
+ # commit_new_snapshot()
225
289
  #
226
- # Last, we commit what we currently have in the working copy.
227
- def commit(version)
228
- buildfile = File.read(Buildr.application.buildfile).
229
- gsub(THIS_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{version}"}) }
230
- File.open(Buildr.application.buildfile, 'w') { |file| file.write buildfile }
231
- svn 'commit', '-m', "Changed version number to #{version}", Buildr.application.buildfile
232
- end
233
-
234
- # :call-seq:
235
- # svn(*args)
236
- #
237
- # Executes SVN command and returns the output.
238
- def svn(*args)
239
- cmd = 'svn ' + args.map { |arg| arg[' '] ? %Q{"#{arg}"} : arg }.join(' ')
240
- puts cmd if verbose
241
- `#{cmd}`.tap { fail 'SVN command failed' unless $?.exitstatus == 0 }
242
- end
243
-
244
- # Return the current SVN URL
245
- def svn_url
246
- url = svn('info').scan(/URL: (.*)/)[0][0]
290
+ # Last, we commit what we currently have in the working copy with an upgraded version number.
291
+ def commit_new_snapshot
292
+ buildfile = change_version { |version| version[-1] = (version[-1].to_i + 1).to_s + '-SNAPSHOT' }
293
+ File.open(Buildr.application.buildfile.to_s, 'w') { |file| file.write buildfile }
294
+ Svn.commit Buildr.application.buildfile.to_s, "Changed version number to #{extract_version}"
295
+ info "Current version is now #{extract_version}"
247
296
  end
248
297
  end
249
-
250
298
  end
251
299
 
300
+
252
301
  desc 'Make a release'
253
302
  task 'release' do |task|
254
303
  Release.make
@@ -16,7 +16,7 @@
16
16
 
17
17
  require 'buildr/core/project'
18
18
  require 'buildr/packaging/zip'
19
- require 'test/unit'
19
+ #require 'test/unit'
20
20
  require 'spec/matchers'
21
21
  require 'spec/expectations'
22
22
 
@@ -130,7 +130,7 @@ module Buildr
130
130
  # Run the expectation. We only print the expectation name when tracing (to know they all ran),
131
131
  # or when we get a failure.
132
132
  begin
133
- puts description if Buildr.application.options.trace
133
+ trace description
134
134
  klass.new.instance_eval &@block
135
135
  rescue Exception=>error
136
136
  raise error.exception("#{description}\n#{error}").tap { |wrapped| wrapped.set_backtrace(error.backtrace) }
@@ -149,10 +149,10 @@ module Buildr
149
149
  begin
150
150
  expect.run_against project
151
151
  passed
152
- rescue Exception=>error
152
+ rescue Exception=>ex
153
153
  if verbose
154
- puts error.backtrace.detect { |line| line =~ /#{Buildr.application.buildfile}/ } || ""
155
- puts error
154
+ error ex.backtrace.select { |line| line =~ /#{Buildr.application.buildfile}/ }.join("\n")
155
+ error ex
156
156
  end
157
157
  false
158
158
  end
@@ -111,7 +111,7 @@ module Buildr
111
111
  # Download to a file created by the task.
112
112
  fail unless args.keys.size == 1
113
113
  uri = URI.parse(args.values.first.to_s)
114
- file(args.keys.first).tap do |task|
114
+ file(args.keys.first.to_s).tap do |task|
115
115
  task.sources << uri
116
116
  task.enhance { uri.download task.name }
117
117
  end
@@ -122,6 +122,8 @@ module Buildr
122
122
  map = compile_map(sources, target)
123
123
  return false if map.empty?
124
124
  return true unless File.exist?(target.to_s)
125
+ source_files_not_yet_compiled = map.select { |source, target| !File.exist?(target) }
126
+ trace "Compile needed because source file #{source_files_not_yet_compiled[0][0]} has no corresponding #{source_files_not_yet_compiled[0][1]}" unless source_files_not_yet_compiled.empty?
125
127
  return true if map.any? { |source, target| !File.exist?(target) || File.stat(source).mtime > File.stat(target).mtime }
126
128
  oldest = map.map { |source, target| File.stat(target).mtime }.min
127
129
  return dependencies.any? { |path| file(path).timestamp > oldest }
@@ -173,7 +175,7 @@ module Buildr
173
175
  FileList["#{source}/**/*.{#{ext_glob}}"].reject { |file| File.directory?(file) }.
174
176
  each { |file| map[file] = File.join(target, Util.relative_path(file, source).ext(target_ext)) }
175
177
  else
176
- map[source] = File.join(target, File.basename(source).ext(target_ext))
178
+ map[source] = target # File.join(target, File.basename(source).ext(target_ext))
177
179
  end
178
180
  map
179
181
  end
@@ -223,7 +225,7 @@ module Buildr
223
225
  raise 'No compiler selected and can\'t determine which compiler to use' unless compiler
224
226
  raise 'No target directory specified' unless target
225
227
  mkpath target.to_s, :verbose=>false
226
- puts "Compiling #{task.name.gsub(/:[^:]*$/, '')} into #{target.to_s}" if verbose
228
+ info "Compiling #{task.name.gsub(/:[^:]*$/, '')} into #{target.to_s}"
227
229
  @compiler.compile(sources.map(&:to_s), target.to_s, dependencies.map(&:to_s))
228
230
  # By touching the target we let other tasks know we did something,
229
231
  # and also prevent recompiling again for dependencies.
@@ -244,6 +246,7 @@ module Buildr
244
246
  # compile.from('src/java').into('classes').with('module1.jar')
245
247
  def from(*sources)
246
248
  @sources |= sources.flatten
249
+ guess_compiler if @compiler.nil? && sources.flatten.any? { |source| File.exist?(source) }
247
250
  self
248
251
  end
249
252
 
@@ -316,10 +319,7 @@ module Buildr
316
319
  # based on existing source directories (e.g. src/main/java), or by requesting
317
320
  # a specific compiler (see #using).
318
321
  def compiler
319
- unless @compiler
320
- candidate = Compiler.compilers.detect { |cls| cls.applies_to?(project, self) }
321
- self.compiler = candidate if candidate
322
- end
322
+ guess_compiler unless @compiler
323
323
  @compiler && @compiler.class.to_sym
324
324
  end
325
325
 
@@ -363,7 +363,11 @@ module Buildr
363
363
  # Associates this task with project and particular usage (:main, :test).
364
364
  def associate_with(project, usage) #:nodoc:
365
365
  @project, @usage = project, usage
366
- # Try to guess if we have a compiler to match source files.
366
+ guess_compiler
367
+ end
368
+
369
+ # Try to guess if we have a compiler to match source files.
370
+ def guess_compiler #:nodoc:
367
371
  candidate = Compiler.compilers.detect { |cls| cls.applies_to?(project, self) }
368
372
  self.compiler = candidate if candidate
369
373
  end
@@ -496,9 +500,7 @@ module Buildr
496
500
  # This comes last because the target path is set inside the project definition.
497
501
  project.build project.compile.target
498
502
  project.clean do
499
- verbose(false) do
500
- rm_rf project.compile.target.to_s
501
- end
503
+ rm_rf project.compile.target.to_s, :verbose=>false
502
504
  end
503
505
  end
504
506
  end
@@ -13,7 +13,6 @@
13
13
  # License for the specific language governing permissions and limitations under
14
14
  # the License.
15
15
 
16
-
17
16
  module Buildr
18
17
 
19
18
  # A filter knows how to copy files from one directory to another, applying mappings to the
@@ -56,7 +55,8 @@ module Buildr
56
55
  def clear
57
56
  @include = []
58
57
  @exclude = []
59
- @sources = []
58
+ @sources = FileList[]
59
+ @mapper = Mapper.new
60
60
  self
61
61
  end
62
62
 
@@ -93,7 +93,7 @@ module Buildr
93
93
  # Specifies files to include and returns self. See FileList#include.
94
94
  #
95
95
  # By default all files are included. You can use this method to only include specific
96
- # files form the source directory.
96
+ # files from the source directory.
97
97
  def include(*files)
98
98
  @include += files
99
99
  self
@@ -110,10 +110,14 @@ module Buildr
110
110
  end
111
111
 
112
112
  # The mapping. See #using.
113
- attr_accessor :mapping
113
+ def mapping #:nodoc:
114
+ @mapper.config
115
+ end
114
116
 
115
117
  # The mapper to use. See #using.
116
- attr_accessor :mapper
118
+ def mapper #:nodoc:
119
+ @mapper.mapper_type
120
+ end
117
121
 
118
122
  # :call-seq:
119
123
  # using(mapping) => self
@@ -127,6 +131,7 @@ module Buildr
127
131
  # * :ant -- Map <code>@key@</code>.
128
132
  # * :maven -- Map <code>${key}</code> (default).
129
133
  # * :ruby -- Map <code>#{key}</code>.
134
+ # * :erb -- Map <code><%= key %></code>.
130
135
  # * Regexp -- Maps the matched data (e.g. <code>/=(.*?)=/</code>
131
136
  #
132
137
  # For example:
@@ -138,20 +143,10 @@ module Buildr
138
143
  # to return the mapped content.
139
144
  #
140
145
  # Without any mapping, all files are copied as is.
146
+ #
147
+ # To register new mapping type see the Mapper class.
141
148
  def using(*args, &block)
142
- case args.first
143
- when Hash # Maven hash mapping
144
- using :maven, *args
145
- when Symbol # Mapping from a method
146
- raise ArgumentError, 'Expected mapper type followed by mapping hash' unless args.size == 2 && Hash === args[1]
147
- @mapper, @mapping = *args
148
- when Regexp # Mapping using a regular expression
149
- raise ArgumentError, 'Expected regular expression followed by mapping hash' unless args.size == 2 && Hash === args[1]
150
- @mapper, @mapping = *args
151
- else
152
- raise ArgumentError, 'Expected proc, method or a block' if args.size > 1 || (args.first && block)
153
- @mapping = args.first || block
154
- end
149
+ @mapper.using(*args, &block)
155
150
  self
156
151
  end
157
152
 
@@ -185,24 +180,12 @@ module Buildr
185
180
  mkpath dest
186
181
  else
187
182
  mkpath File.dirname(dest)
188
- case mapping
189
- when Proc, Method # Call on input, accept output.
190
- mapped = mapping.call(path, File.open(source, 'rb') { |file| file.read })
183
+ if @mapper.mapper_type
184
+ mapped = @mapper.transform(File.open(source, 'rb') { |file| file.read }, path)
191
185
  File.open(dest, 'wb') { |file| file.write mapped }
192
- when Hash # Map ${key} to value
193
- content = File.open(source, 'rb') { |file| file.read }
194
- if Symbol === @mapper
195
- mapped = send("#{@mapper}_mapper", content) { |key| mapping[key] }
196
- else
197
- mapped = regexp_mapper(content) { |key| mapping[key] }
198
- end
199
- #gsub(/\$\{[^}]*\}/) { |str| mapping[str[2..-2]] || str }
200
- File.open(dest, 'wb') { |file| file.write mapped }
201
- when nil # No mapping.
186
+ else # no mapping
202
187
  cp source, dest
203
188
  File.chmod(0664, dest)
204
- else
205
- fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{mapping}"
206
189
  end
207
190
  end
208
191
  end
@@ -216,23 +199,145 @@ module Buildr
216
199
  @target.to_s
217
200
  end
218
201
 
219
- private
202
+ # This class implements content replacement logic for Filter.
203
+ #
204
+ # To register a new template engine @:foo@, extend this class with a method like:
205
+ #
206
+ # def foo_transform(content, path = nil)
207
+ # # if this method yields a key, the value comes from the mapping hash
208
+ # content.gsub(/world/) { |str| yield :bar }
209
+ # end
210
+ #
211
+ # Then you can use :foo mapping type on a Filter
212
+ #
213
+ # filter.using :foo, :bar => :baz
214
+ #
215
+ # Or all by your own, simply
216
+ #
217
+ # Mapper.new(:foo, :bar => :baz).transform("Hello world") # => "Hello baz"
218
+ #
219
+ # You can handle configuration arguments by providing a @*_config@ method like:
220
+ #
221
+ # # The return value of this method is available with the :config accessor.
222
+ # def moo_config(*args, &block)
223
+ # raise ArgumentError, "Expected moo block" unless block_given?
224
+ # { :moos => args, :callback => block }
225
+ # end
226
+ #
227
+ # def moo_transform(content, path = nil)
228
+ # content.gsub(/moo+/i) do |str|
229
+ # moos = yield :moos # same than config[:moos]
230
+ # moo = moos[str.size - 3] || str
231
+ # config[:callback].call(moo)
232
+ # end
233
+ # end
234
+ #
235
+ # Usage for the @:moo@ mapper would be something like:
236
+ #
237
+ # mapper = Mapper.new(:moo, 'ooone', 'twoo') do |str|
238
+ # i = nil; str.capitalize.gsub(/\w/) { |s| s.send( (i = !i) ? 'upcase' : 'downcase' ) }
239
+ # end
240
+ # mapper.transform('Moo cow, mooo cows singing mooooo') # => 'OoOnE cow, TwOo cows singing MoOoOo'
241
+ class Mapper
220
242
 
221
- def maven_mapper(content)
222
- content.gsub(/\$\{.*?\}/) { |str| yield(str[2..-2]) || str }
223
- end
243
+ attr_reader :mapper_type, :config
224
244
 
225
- def ant_mapper(content)
226
- content.gsub(/@.*?@/) { |str| yield(str[1..-2]) || str }
227
- end
245
+ def initialize(*args, &block) #:nodoc:
246
+ using(*args, &block)
247
+ end
248
+
249
+ def using(*args, &block)
250
+ case args.first
251
+ when Hash # Maven hash mapping
252
+ using :maven, *args
253
+ when Binding # Erb binding
254
+ using :erb, *args
255
+ when Symbol # Mapping from a method
256
+ raise ArgumentError, "Unknown mapping type: #{args.first}" unless respond_to?("#{args.first}_transform", true)
257
+ configure(*args, &block)
258
+ when Regexp # Mapping using a regular expression
259
+ raise ArgumentError, 'Expected regular expression followed by mapping hash' unless args.size == 2 && Hash === args[1]
260
+ @mapper_type, @config = *args
261
+ else
262
+ unless args.empty? && block.nil?
263
+ raise ArgumentError, 'Expected proc, method or a block' if args.size > 1 || (args.first && block)
264
+ @mapper_type = :callback
265
+ config = args.first || block
266
+ raise ArgumentError, 'Expected proc, method or callable' unless config.respond_to?(:call)
267
+ @config = config
268
+ end
269
+ end
270
+ self
271
+ end
228
272
 
229
- def ruby_mapper(content)
230
- content.gsub(/#\{.*?\}/) { |str| yield(str[2..-2]) || str }
231
- end
273
+ def transform(content, path = nil)
274
+ type = Regexp === mapper_type ? :regexp : mapper_type
275
+ raise ArgumentError, "Invalid mapper type: #{type.inspect}" unless respond_to?("#{type}_transform", true)
276
+ self.__send__("#{type}_transform", content, path) { |key| config[key] || config[key.to_s.to_sym] }
277
+ end
232
278
 
233
- def regexp_mapper(content)
234
- content.gsub(@mapper) { |str| yield(str.scan(@mapper).join) || str }
235
- end
279
+ private
280
+ def configure(mapper_type, *args, &block)
281
+ configurer = method("#{mapper_type}_config") rescue nil
282
+ if configurer
283
+ @config = configurer.call(*args, &block)
284
+ else
285
+ raise ArgumentError, "Missing hash argument after :#{mapper_type}" unless args.size == 1 && Hash === args[0]
286
+ @config = *args
287
+ end
288
+ @mapper_type = mapper_type
289
+ end
290
+
291
+ def maven_transform(content, path = nil)
292
+ content.gsub(/\$\{.*?\}/) { |str| yield(str[2..-2]) || str }
293
+ end
294
+
295
+ def ant_transform(content, path = nil)
296
+ content.gsub(/@.*?@/) { |str| yield(str[1..-2]) || str }
297
+ end
298
+
299
+ def ruby_transform(content, path = nil)
300
+ content.gsub(/#\{.*?\}/) { |str| yield(str[2..-2]) || str }
301
+ end
302
+
303
+ def regexp_transform(content, path = nil)
304
+ content.gsub(mapper_type) { |str| yield(str.scan(mapper_type).join) || str }
305
+ end
306
+
307
+ def callback_transform(content, path = nil)
308
+ config.call(path, content)
309
+ end
310
+
311
+ def erb_transform(content, path = nil)
312
+ case config
313
+ when Binding, Proc
314
+ bnd = config
315
+ when Method
316
+ bnd = config.to_proc
317
+ when Hash
318
+ bnd = OpenStruct.new
319
+ table = config.inject({}) { |h, e| h[e.first.to_sym] = e.last; h }
320
+ bnd.instance_variable_set(:@table, table)
321
+ bnd = bnd.instance_eval { binding }
322
+ else
323
+ bnd = config.instance_eval { binding }
324
+ end
325
+ require 'erb'
326
+ ERB.new(content).result(bnd)
327
+ end
328
+
329
+ def erb_config(*args, &block)
330
+ if block_given?
331
+ raise ArgumentError, "Expected block or single argument, but both given." unless args.empty?
332
+ block
333
+ elsif args.size > 1
334
+ raise ArgumentError, "Expected block or single argument."
335
+ else
336
+ args.first
337
+ end
338
+ end
339
+
340
+ end # class Mapper
236
341
 
237
342
  end
238
343