buildr 1.1.3 → 1.2.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 +48 -0
- data/README +1 -1
- data/Rakefile +204 -0
- data/bin/buildr +7 -0
- data/lib/buildr.rb +155 -16
- data/lib/buildr/cobertura.rb +26 -19
- data/lib/buildr/hibernate.rb +8 -6
- data/lib/buildr/javacc.rb +1 -0
- data/lib/buildr/jdepend.rb +31 -4
- data/lib/buildr/jetty.rb +26 -28
- data/lib/buildr/openjpa.rb +8 -6
- data/lib/buildr/xmlbeans.rb +9 -4
- data/lib/core/build.rb +40 -50
- data/lib/core/checks.rb +358 -0
- data/lib/core/common.rb +161 -62
- data/lib/core/generate.rb +65 -0
- data/lib/core/help.rb +72 -0
- data/lib/core/project.rb +32 -37
- data/lib/core/rake_ext.rb +12 -66
- data/lib/core/transports.rb +388 -363
- data/lib/java/ant.rb +33 -36
- data/lib/java/artifact.rb +172 -160
- data/lib/java/compile.rb +13 -21
- data/lib/java/eclipse.rb +5 -5
- data/lib/java/idea.ipr.template +284 -0
- data/lib/java/idea.rb +107 -72
- data/lib/java/java.rb +42 -18
- data/lib/java/packaging.rb +242 -124
- data/lib/java/test.rb +532 -135
- data/lib/tasks/zip.rb +72 -23
- metadata +24 -10
data/lib/core/checks.rb
ADDED
@@ -0,0 +1,358 @@
|
|
1
|
+
require "core/project"
|
2
|
+
require "tasks/zip"
|
3
|
+
require "spec"
|
4
|
+
|
5
|
+
module Buildr
|
6
|
+
|
7
|
+
module BuildChecks #:nodoc:
|
8
|
+
|
9
|
+
module Matchers
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
# Define matchers that operate by calling a method on the tested object.
|
14
|
+
# For example:
|
15
|
+
# foo.should contain(bar)
|
16
|
+
# calls:
|
17
|
+
# foo.contain(bar)
|
18
|
+
def match_using(*names)
|
19
|
+
names.each do |name|
|
20
|
+
matcher = Class.new do
|
21
|
+
# Initialize with expected arguments (i.e. contain(bar) initializes with bar).
|
22
|
+
define_method(:initialize) { |*args| @expects = args }
|
23
|
+
# Matches against actual value (i.e. foo.should exist called with foo).
|
24
|
+
define_method(:matches?) do |actual|
|
25
|
+
@actual = actual
|
26
|
+
return actual.send("#{name}?", *@expects) if actual.respond_to?("#{name}?")
|
27
|
+
return actual.send(name, *@expects) if actual.respond_to?(name)
|
28
|
+
raise "You can't check #{actual}, it doesn't respond to #{name}."
|
29
|
+
end
|
30
|
+
# Some matchers have arguments, others don't, treat appropriately.
|
31
|
+
define_method :failure_message do
|
32
|
+
args = " " + @expects.map{ |arg| "'#{arg}'" }.join(", ") unless @expects.empty?
|
33
|
+
"Expected #{@actual} to #{name}#{args}"
|
34
|
+
end
|
35
|
+
define_method :negative_failure_message do
|
36
|
+
args = " " + @expects.map{ |arg| "'#{arg}'" }.join(", ") unless @expects.empty?
|
37
|
+
"Expected #{@actual} to not #{name}#{args}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
# Define method to create matcher.
|
41
|
+
define_method(name) { |*args| matcher.new(*args) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
# Define delegate matchers for exist and contain methods.
|
48
|
+
match_using :exist, :contain
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# An expectation has subject, description and block. The expectation is validated by running the block,
|
54
|
+
# and can access the subject from the method #it. The description is used for reporting.
|
55
|
+
#
|
56
|
+
# The expectation is run by calling #run_against. You can share expectations by running them against
|
57
|
+
# different projects (or any other context for that matter).
|
58
|
+
#
|
59
|
+
# If the subject is missing, it is set to the argument of #run_against, typically the project itself.
|
60
|
+
# If the description is missing, it is set from the project. If the block is missing, the default behavior
|
61
|
+
# prints "Pending" followed by the description. You can use this to write place holders and fill them later.
|
62
|
+
class Expectation
|
63
|
+
|
64
|
+
attr_reader :description, :subject, :block
|
65
|
+
|
66
|
+
# :call-seq:
|
67
|
+
# initialize(subject, description?) { .... }
|
68
|
+
# initialize(description?) { .... }
|
69
|
+
#
|
70
|
+
# First argument is subject (returned from it method), second argument is description. If you omit the
|
71
|
+
# description, it will be set from the subject. If you omit the subject, it will be set from the object
|
72
|
+
# passed to run_against.
|
73
|
+
def initialize(*args, &block)
|
74
|
+
@description = args.pop if String === args.last
|
75
|
+
@subject = args.shift
|
76
|
+
raise ArgumentError, "Expecting subject followed by description, and either one is optional. Not quite sure what to do with this list of arguments." unless args.empty?
|
77
|
+
@block = block || lambda { puts "Pending: #{description}" if verbose }
|
78
|
+
end
|
79
|
+
|
80
|
+
# :call-seq:
|
81
|
+
# run_against(context)
|
82
|
+
#
|
83
|
+
# Runs this expectation against the context object. The context object is different from the subject,
|
84
|
+
# but used as the subject if no subject specified (i.e. returned from the it method).
|
85
|
+
#
|
86
|
+
# This method creates a new context object modeled after the context argument, but a separate object
|
87
|
+
# used strictly for running this expectation, and used only once. The context object will pass methods
|
88
|
+
# to the context argument, so you can call any method, e.g. package(:jar).
|
89
|
+
#
|
90
|
+
# It also adds all matchers defined in Buildr and RSpec, and two additional methods:
|
91
|
+
# * it() -- Returns the subject.
|
92
|
+
# * description() -- Returns the description.
|
93
|
+
def run_against(context)
|
94
|
+
subject = @subject || context
|
95
|
+
description = @description ? "#{subject} #{@description}" : subject.to_s
|
96
|
+
# Define anonymous class and load it with:
|
97
|
+
# - All instance methods defined in context, so we can pass method calls to the context.
|
98
|
+
# - it() method to return subject, description() method to return description.
|
99
|
+
# - All matchers defined by Buildr and RSpec.
|
100
|
+
klass = Class.new
|
101
|
+
klass.instance_eval do
|
102
|
+
context.class.instance_methods(false).each do |method|
|
103
|
+
define_method(method) { |args| context.send(method, args) }
|
104
|
+
end
|
105
|
+
define_method(:it) { subject }
|
106
|
+
define_method(:description) { description }
|
107
|
+
include Spec::Matchers
|
108
|
+
include Matchers
|
109
|
+
end
|
110
|
+
|
111
|
+
# Run the expectation. We only print the expectation name when tracing (to know they all ran),
|
112
|
+
# or when we get a failure.
|
113
|
+
begin
|
114
|
+
puts description if Rake.application.options.trace
|
115
|
+
klass.new.instance_eval &@block
|
116
|
+
rescue Exception=>error
|
117
|
+
raise error.exception("#{description}\n#{error}").tap { |wrapped| wrapped.set_backtrace(error.backtrace) }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
class Project
|
127
|
+
|
128
|
+
# :call-seq:
|
129
|
+
# check(description) { ... }
|
130
|
+
# check(subject, description) { ... }
|
131
|
+
#
|
132
|
+
# Adds an expectation. The expectation is run against the project by the check task, executed after packaging.
|
133
|
+
# You can access any package created by the project.
|
134
|
+
#
|
135
|
+
# An expectation is written using a subject, description and block to validate the expectation. For example:
|
136
|
+
#
|
137
|
+
# For example:
|
138
|
+
# check package(:jar), "should exist" do
|
139
|
+
# it.should exist
|
140
|
+
# end
|
141
|
+
# check package(:jar), "should contain a manifest" do
|
142
|
+
# it.should contain("META-INF/MANIFEST.MF")
|
143
|
+
# end
|
144
|
+
# check package(:jar).path("com/acme"), "should contain classes" do
|
145
|
+
# it.should_not be_empty
|
146
|
+
# end
|
147
|
+
# check package(:jar).entry("META-INF/MANIFEST"), "should be a recent license" do
|
148
|
+
# it.should contain(/Copyright (C) 2007/)
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# If you omit the subject, the project is used as the subject. If you omit the description, the subject is
|
152
|
+
# used as description.
|
153
|
+
#
|
154
|
+
# During development you can write placeholder expectations by omitting the block. This will simply report
|
155
|
+
# the expectation as pending.
|
156
|
+
def check(*args, &block)
|
157
|
+
expectations << BuildChecks::Expectation.new(*args, &block)
|
158
|
+
end
|
159
|
+
|
160
|
+
# :call-seq:
|
161
|
+
# expectations() => Expectation*
|
162
|
+
#
|
163
|
+
# Returns a list of expectations (see #check).
|
164
|
+
def expectations()
|
165
|
+
@expectations ||= []
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
Project.on_define do |project|
|
171
|
+
# The check task can do any sort of interesting things, but the most important is running expectations.
|
172
|
+
project.task("check") do |task|
|
173
|
+
project.expectations.inject(true) do |passed, expect|
|
174
|
+
begin
|
175
|
+
expect.run_against project
|
176
|
+
passed
|
177
|
+
rescue Exception=>error
|
178
|
+
if verbose
|
179
|
+
puts error.backtrace.detect { |line| line =~ /#{Rake.application.rakefile}/ } || ""
|
180
|
+
puts error
|
181
|
+
end
|
182
|
+
false
|
183
|
+
end
|
184
|
+
end or fail "Checks failed for project #{project.name} (see errors above)."
|
185
|
+
end
|
186
|
+
project.task("package").enhance do |task|
|
187
|
+
# Run all actions before checks.
|
188
|
+
task.enhance { project.task("check").invoke }
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
module Rake #:nodoc:
|
196
|
+
class FileTask
|
197
|
+
|
198
|
+
# :call-seq:
|
199
|
+
# exist?() => boolean
|
200
|
+
#
|
201
|
+
# Returns true if this file exists.
|
202
|
+
def exist?()
|
203
|
+
File.exist?(name)
|
204
|
+
end
|
205
|
+
|
206
|
+
# :call-seq:
|
207
|
+
# empty?() => boolean
|
208
|
+
#
|
209
|
+
# Returns true if file/directory is empty.
|
210
|
+
def empty?()
|
211
|
+
File.directory?(name) ? Dir.glob("#{name}/*").empty? : File.read(name).empty?
|
212
|
+
end
|
213
|
+
|
214
|
+
# :call-seq:
|
215
|
+
# contain(pattern*) => boolean
|
216
|
+
# contain(file*) => boolean
|
217
|
+
#
|
218
|
+
# For a file, returns true if the file content matches against all the arguments. An argument may be
|
219
|
+
# a string or regular expression.
|
220
|
+
#
|
221
|
+
# For a directory, return true if the directory contains the specified files. You can use relative
|
222
|
+
# file names and glob patterns (using *, **, etc).
|
223
|
+
def contain?(*patterns)
|
224
|
+
if File.directory?(name)
|
225
|
+
patterns.map { |pattern| "#{name}/#{pattern}" }.all? { |pattern| !Dir[pattern].empty? }
|
226
|
+
else
|
227
|
+
contents = File.read(name)
|
228
|
+
patterns.map { |pattern| Regexp === pattern ? pattern : Regexp.new(Regexp.escape(pattern.to_s)) }.
|
229
|
+
all? { |pattern| contents =~ pattern }
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
module Zip #:nodoc:
|
238
|
+
class ZipEntry
|
239
|
+
|
240
|
+
# :call-seq:
|
241
|
+
# exist() => boolean
|
242
|
+
#
|
243
|
+
# Returns true if this entry exists.
|
244
|
+
def exist?()
|
245
|
+
Zip::ZipFile.open(zipfile) { |zip| zip.file.exist?(@name) }
|
246
|
+
end
|
247
|
+
|
248
|
+
# :call-seq:
|
249
|
+
# empty?() => boolean
|
250
|
+
#
|
251
|
+
# Returns true if this entry is empty.
|
252
|
+
def empty?()
|
253
|
+
Zip::ZipFile.open(zipfile) { |zip| zip.file.read(@name) }.empty?
|
254
|
+
end
|
255
|
+
|
256
|
+
# :call-seq:
|
257
|
+
# contain(patterns*) => boolean
|
258
|
+
#
|
259
|
+
# Returns true if this ZIP file entry matches against all the arguments. An argument may be
|
260
|
+
# a string or regular expression.
|
261
|
+
def contain?(*patterns)
|
262
|
+
content = Zip::ZipFile.open(zipfile) { |zip| zip.file.read(@name) }
|
263
|
+
patterns.map { |pattern| Regexp === pattern ? pattern : Regexp.new(Regexp.escape(pattern.to_s)) }.
|
264
|
+
all? { |pattern| content =~ pattern }
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
|
271
|
+
module Buildr
|
272
|
+
class ZipTask
|
273
|
+
|
274
|
+
class Path
|
275
|
+
|
276
|
+
# :call-seq:
|
277
|
+
# exist() => boolean
|
278
|
+
#
|
279
|
+
# Returns true if this path exists. This only works if the path has any entries in it,
|
280
|
+
# so exist on path happens to be the opposite of empty.
|
281
|
+
def exist?()
|
282
|
+
!empty?
|
283
|
+
end
|
284
|
+
|
285
|
+
# :call-seq:
|
286
|
+
# empty?() => boolean
|
287
|
+
#
|
288
|
+
# Returns true if this path is empty (has no other entries inside).
|
289
|
+
def empty?()
|
290
|
+
check { |entries| entries.empty? }
|
291
|
+
end
|
292
|
+
|
293
|
+
# :call-seq:
|
294
|
+
# contain(file*) => boolean
|
295
|
+
#
|
296
|
+
# Returns true if this ZIP file path contains all the specified files. You can use relative
|
297
|
+
# file names and glob patterns (using *, **, etc).
|
298
|
+
def contain?(*files)
|
299
|
+
check do |entries|
|
300
|
+
files.all? { |file| entries.detect { |entry| File.fnmatch(file, entry.to_s) } }
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# :call-seq:
|
305
|
+
# entry(name) => ZipEntry
|
306
|
+
#
|
307
|
+
# Returns a ZIP file entry. You can use this to check if the entry exists and its contents,
|
308
|
+
# for example:
|
309
|
+
# package(:jar).path("META-INF").entry("LICENSE").should contain(/Apache Software License/)
|
310
|
+
def entry(name)
|
311
|
+
::Zip::ZipEntry.new(root.name, "#{@path}#{name}")
|
312
|
+
end
|
313
|
+
|
314
|
+
protected
|
315
|
+
|
316
|
+
def check() #:nodoc:
|
317
|
+
unless @cached_entries
|
318
|
+
if @path
|
319
|
+
base = Regexp.new("^" + Regexp.escape(@path || ""))
|
320
|
+
@cached_entries = root.path("").check.map { |name| name.to_s.sub!(base, "") }.reject(&:nil?)
|
321
|
+
else
|
322
|
+
@cached_entries = Zip::ZipFile.open(root.name) { |zip| zip.entries }
|
323
|
+
end
|
324
|
+
end
|
325
|
+
block_given? ? yield(@cached_entries) : @cached_entries
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
# :call-seq:
|
331
|
+
# empty?() => boolean
|
332
|
+
#
|
333
|
+
# Returns true if this ZIP file is empty (has no other entries inside).
|
334
|
+
def empty?()
|
335
|
+
path("").empty
|
336
|
+
end
|
337
|
+
|
338
|
+
# :call-seq:
|
339
|
+
# contain(file*) => boolean
|
340
|
+
#
|
341
|
+
# Returns true if this ZIP file contains all the specified files. You can use absolute
|
342
|
+
# file names and glob patterns (using *, **, etc).
|
343
|
+
def contain?(*files)
|
344
|
+
path("").contain?(*files)
|
345
|
+
end
|
346
|
+
|
347
|
+
# :call-seq:
|
348
|
+
# entry(name) => Entry
|
349
|
+
#
|
350
|
+
# Returns a ZIP file entry. You can use this to check if the entry exists and its contents,
|
351
|
+
# for example:
|
352
|
+
# package(:jar).entry("META-INF/LICENSE").should contain(/Apache Software License/)
|
353
|
+
def entry(name)
|
354
|
+
path("").entry(name)
|
355
|
+
end
|
356
|
+
|
357
|
+
end
|
358
|
+
end
|
data/lib/core/common.rb
CHANGED
@@ -1,9 +1,85 @@
|
|
1
1
|
require "tempfile"
|
2
2
|
require "pathname"
|
3
3
|
require "core/transports"
|
4
|
+
require "open-uri"
|
5
|
+
require "uri/open-sftp"
|
6
|
+
|
7
|
+
|
8
|
+
class Hash
|
9
|
+
|
10
|
+
# :call-seq:
|
11
|
+
# only(keys*) => hash
|
12
|
+
#
|
13
|
+
# Returns a new hash with only the specified keys.
|
14
|
+
#
|
15
|
+
# For example:
|
16
|
+
# { :a=>1, :b=>2, :c=>3, :d=>4 }.only(:a, :c)
|
17
|
+
# => { :b=>2, :d=>4 }
|
18
|
+
def only(*keys)
|
19
|
+
self.inject({}) { |hash, pair| hash[pair[0]] = pair[1] if keys.include?(pair[0]) ; hash }
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# :call-seq:
|
24
|
+
# except(keys*) => hash
|
25
|
+
#
|
26
|
+
# Returns a new hash without the specified keys.
|
27
|
+
#
|
28
|
+
# For example:
|
29
|
+
# { :a=>1, :b=>2, :c=>3, :d=>4 }.except(:a, :c)
|
30
|
+
# => { :a=>1, :c=>3 }
|
31
|
+
def except(*keys)
|
32
|
+
self.inject({}) { |hash, pair| hash[pair[0]] = pair[1] unless keys.include?(pair[0]) ; hash }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
4
37
|
|
5
38
|
module Buildr
|
6
39
|
|
40
|
+
# Collection of options for controlling Buildr. For example for running builds without running
|
41
|
+
# test cases, using a proxy server, JVM arguments, etc. You access this object by calling options,
|
42
|
+
# for example:
|
43
|
+
# options.proxy.http = "http://proxy.acme.com:8080"
|
44
|
+
# options.java_args = "-Xmx512M"
|
45
|
+
class Options
|
46
|
+
|
47
|
+
# :call-seq:
|
48
|
+
# proxy() => options
|
49
|
+
#
|
50
|
+
# Returns the proxy options. Currently supported options are:
|
51
|
+
# * :http -- HTTP proxy for use when downloading.
|
52
|
+
#
|
53
|
+
# For example:
|
54
|
+
# options.proxy.http = "http://proxy.acme.com:8080"
|
55
|
+
# You can also set it using the environment variable HTTP_PROXY.
|
56
|
+
def proxy()
|
57
|
+
@proxy ||= Struct.new(:http).new(ENV['HTTP_PROXY'] || ENV['http_proxy'])
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
class << self
|
63
|
+
|
64
|
+
# :call-seq:
|
65
|
+
# options() => Options
|
66
|
+
#
|
67
|
+
# Returns the Buildr options. See Options.
|
68
|
+
def options()
|
69
|
+
@options ||= Options.new
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# :call-seq:
|
75
|
+
# options() => Options
|
76
|
+
#
|
77
|
+
# Returns the Buildr options. See Options.
|
78
|
+
def options()
|
79
|
+
Buildr.options
|
80
|
+
end
|
81
|
+
|
82
|
+
|
7
83
|
# :call-seq:
|
8
84
|
# struct(hash) => Struct
|
9
85
|
#
|
@@ -76,27 +152,30 @@ module Buildr
|
|
76
152
|
# For example:
|
77
153
|
# download "image.jpg"=>"http://example.com/theme/image.jpg"
|
78
154
|
def download(args)
|
79
|
-
|
155
|
+
args = URI.parse(args) if String === args
|
156
|
+
if URI === args
|
80
157
|
# Given only a download URL, download into a temporary file.
|
81
158
|
# You can infer the file from task name.
|
82
|
-
temp = Tempfile.
|
83
|
-
|
84
|
-
|
159
|
+
temp = Tempfile.open(File.basename(args.to_s))
|
160
|
+
file(temp.path).tap do |task|
|
161
|
+
# Since temporary file exists, force a download.
|
162
|
+
class << task ; def needed?() ; true ; end ; end
|
163
|
+
task.sources << args
|
164
|
+
task.enhance { args.download temp, :proxy=>Buildr.options.proxy }
|
85
165
|
end
|
86
|
-
task.sources << args
|
87
166
|
else
|
88
167
|
# Download to a file created by the task.
|
89
168
|
fail unless args.keys.size == 1
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
169
|
+
uri = URI.parse(args.values.first.to_s)
|
170
|
+
file_create(args.keys.first).tap do |task|
|
171
|
+
task.sources << uri
|
172
|
+
task.enhance { uri.download task.name, :proxy=>Buildr.options.proxy }
|
94
173
|
end
|
95
|
-
task.sources << url
|
96
174
|
end
|
97
|
-
|
175
|
+
|
98
176
|
end
|
99
177
|
|
178
|
+
|
100
179
|
# A filter knows how to copy files from one directory to another, applying mappings to the
|
101
180
|
# contents of these files.
|
102
181
|
#
|
@@ -188,37 +267,52 @@ module Buildr
|
|
188
267
|
# For example:
|
189
268
|
# filter.using "version"=>"1.2"
|
190
269
|
# will replace all occurrences of "${version}" with "1.2".
|
191
|
-
def using(mapping, &block)
|
270
|
+
def using(mapping = nil, &block)
|
192
271
|
self.mapping = mapping || block
|
193
272
|
self
|
194
273
|
end
|
195
274
|
|
275
|
+
# :call-seq:
|
276
|
+
# run() => boolean
|
277
|
+
#
|
196
278
|
# Runs the filter.
|
197
279
|
def run()
|
198
|
-
if
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
280
|
+
raise "No source directory specified, where am I going to find the files to filter?" if source.nil?
|
281
|
+
raise "Source directory #{source} doesn't exist" unless File.exist?(source.to_s)
|
282
|
+
raise "No target directory specified, where am I going to copy the files to?" if target.nil?
|
283
|
+
|
284
|
+
includes = @include.empty? ? ["*"] : @include
|
285
|
+
src_base = Pathname.new(source.to_s)
|
286
|
+
copy_map = Dir[File.join(source.to_s, "**/*")].reject { |file| File.directory?(file) }.
|
287
|
+
map { |src| Pathname.new(src).relative_path_from(src_base).to_s }.
|
288
|
+
select { |file| includes.any? { |pattern| File.fnmatch(pattern, file) } }.
|
289
|
+
reject { |file| @exclude.any? { |pattern| File.fnmatch(pattern, file) } }.
|
290
|
+
map { |file| [File.expand_path(file, target.to_s), File.expand_path(file, source.to_s)] }.
|
291
|
+
select { |dest, src| !File.exist?(dest) || File.stat(src).mtime > File.stat(dest).mtime }
|
292
|
+
return false if copy_map.empty?
|
293
|
+
|
294
|
+
verbose(Rake.application.options.trace || false) do
|
295
|
+
mkpath target.to_s
|
296
|
+
copy_map.each do |dest, src|
|
297
|
+
mkpath File.dirname(dest) rescue nil
|
298
|
+
case mapping
|
299
|
+
when Proc, Method # Call on input, accept output.
|
300
|
+
relative = Pathname.new(src).relative_path_from(src_base).to_s
|
301
|
+
mapped = mapping.call(relative, File.open(src, "rb") { |file| file.read })
|
302
|
+
File.open(dest, "wb") { |file| file.write mapped }
|
303
|
+
when Hash # Map ${key} to value
|
304
|
+
mapped = File.open(src, "rb") { |file| file.read }.
|
305
|
+
gsub(/\$\{[^}]*\}/) { |str| mapping[str[2..-2]] || str }
|
306
|
+
File.open(dest, "wb") { |file| file.write mapped }
|
307
|
+
when nil # No mapping.
|
308
|
+
cp src, dest
|
309
|
+
else
|
310
|
+
fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{mapping}"
|
219
311
|
end
|
220
312
|
end
|
313
|
+
touch target.to_s
|
221
314
|
end
|
315
|
+
true
|
222
316
|
end
|
223
317
|
|
224
318
|
# Returns the target directory.
|
@@ -226,34 +320,6 @@ module Buildr
|
|
226
320
|
@target.to_s
|
227
321
|
end
|
228
322
|
|
229
|
-
private
|
230
|
-
|
231
|
-
def needed?()
|
232
|
-
return false if target.nil? || source.nil? || !File.exist?(source.to_s)
|
233
|
-
return true unless File.exist?(target.to_s)
|
234
|
-
!copy_map.empty?
|
235
|
-
end
|
236
|
-
|
237
|
-
# Return a copy map of all the files that need copying: the key is the file to copy to,
|
238
|
-
# the value is the source file. If called with a block, yields with each dest/source pair.
|
239
|
-
def copy_map(&block)
|
240
|
-
unless @copy_map
|
241
|
-
@include = ["*"] if @include.empty?
|
242
|
-
base = Pathname.new(source.to_s)
|
243
|
-
@copy_map = Dir[File.join(source.to_s, "**/*")].reject { |file| File.directory?(file) }.
|
244
|
-
map { |src| Pathname.new(src).relative_path_from(base).to_s }.
|
245
|
-
select { |file| @include.any? { |pattern| File.fnmatch(pattern, file) } }.
|
246
|
-
reject { |file| @exclude.any? { |pattern| File.fnmatch(pattern, file) } }.
|
247
|
-
map { |file| [File.expand_path(file, target.to_s), File.expand_path(file, source.to_s)] }.
|
248
|
-
select { |dest, src| !File.exist?(dest) || File.stat(src).mtime > File.stat(dest).mtime }
|
249
|
-
end
|
250
|
-
if block_given?
|
251
|
-
@copy_map.each(&block)
|
252
|
-
else
|
253
|
-
@copy_map
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
323
|
end
|
258
324
|
|
259
325
|
# :call-seq:
|
@@ -273,5 +339,38 @@ module Buildr
|
|
273
339
|
def filter(source)
|
274
340
|
Filter.new.from(source)
|
275
341
|
end
|
276
|
-
|
342
|
+
|
277
343
|
end
|
344
|
+
|
345
|
+
|
346
|
+
# Add a touch of colors (red) to warnings.
|
347
|
+
HighLine.use_color = PLATFORM !~ /win32/
|
348
|
+
module Kernel #:nodoc:
|
349
|
+
|
350
|
+
def warn_with_color(message)
|
351
|
+
warn_without_color $terminal.color(message.to_s, :red)
|
352
|
+
end
|
353
|
+
alias_method_chain :warn, :color
|
354
|
+
|
355
|
+
# :call-seq:
|
356
|
+
# warn_deprecated(message)
|
357
|
+
#
|
358
|
+
# Use with deprecated methods and classes. This method automatically adds the file name and line number,
|
359
|
+
# and the text "Deprecated" before the message, and eliminated duplicate warnings. It only warns when
|
360
|
+
# running in verbose mode.
|
361
|
+
#
|
362
|
+
# For example:
|
363
|
+
# warn_deprecated "Please use new_foo instead of foo."
|
364
|
+
def warn_deprecated(message) #:nodoc:
|
365
|
+
return unless verbose
|
366
|
+
"#{caller[1]}: Deprecated: #{message}".tap do |message|
|
367
|
+
@deprecated ||= {}
|
368
|
+
unless @deprecated[message]
|
369
|
+
@deprecated[message] = true
|
370
|
+
warn message
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
end
|
376
|
+
|