buildr 1.3.2 → 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
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 +103 -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