buildr 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ module Buildr
2
+
3
+ # A file task that concatenates all its prerequisites to create a new file.
4
+ #
5
+ # For example:
6
+ # concat("master.sql"=>["users.sql", "orders.sql", reports.sql"]
7
+ #
8
+ # See also Buildr#concat.
9
+ class ConcatTask < Rake::FileTask
10
+ def initialize(*args) #:nodoc:
11
+ super
12
+ enhance do |task|
13
+ content = prerequisites.inject("") do |content, prereq|
14
+ content << File.read(prereq.to_s) if File.exists?(prereq) && !File.directory?(prereq)
15
+ content
16
+ end
17
+ File.open(task.name, "w") { |file| file.write content }
18
+ end
19
+ end
20
+ end
21
+
22
+ # :call-seq:
23
+ # concat(target=>files) => task
24
+ #
25
+ # Creates and returns a file task that concatenates all its prerequisites to create
26
+ # a new file. See #ConcatTask.
27
+ #
28
+ # For example:
29
+ # concat("master.sql"=>["users.sql", "orders.sql", reports.sql"]
30
+ def concat(args)
31
+ file, deps = Rake.application.resolve_args(args)
32
+ ConcatTask.define_task File.expand_path(file)=>deps
33
+ end
34
+ end
@@ -1,7 +1,12 @@
1
1
  require "tempfile"
2
+ require "core/transports"
2
3
 
3
4
  module Buildr
4
5
 
6
+ # :call-seq:
7
+ # download(url_or_uri) => task
8
+ # download(path=>url_or_uri) =>task
9
+ #
5
10
  # Create a task that will download a file from a URL.
6
11
  #
7
12
  # Takes a single argument, a hash with one pair. The key is the file being
data/lib/tasks/filter.rb CHANGED
@@ -1,88 +1,138 @@
1
1
  module Buildr
2
2
 
3
- class FilterTask < Rake::Task
3
+ # A filter knows how to copy a set of source files into a target directory, and apply
4
+ # mapping to these files.
5
+ #
6
+ # You can specify the mapping using a Hash, and it will map ${key} fields found in each
7
+ # source file into the appropriate value. For example:
8
+ # filter.using "version"=>"1.2"
9
+ # will replace all occurrences of "${version}" with "1.2".
10
+ #
11
+ # You can also specify the mapping by passing a proc or a method, that will be called for
12
+ # each source file, with the file name and content, returning the modified content.
13
+ #
14
+ # Without any mapping, the filter simply copies the source files into the target directory.
15
+ #
16
+ # See Buildr#filter.
17
+ class Filter
4
18
 
5
19
  # The target directory.
6
20
  attr_reader :target
7
- # Filter to use.
8
- attr_accessor :filter
21
+ # The mapping. See #using.
22
+ attr_accessor :mapping
23
+ # The source files and directories.
24
+ attr_accessor :sources
9
25
 
10
- def initialize(*args)
11
- super
12
- enhance do |task|
13
- fail "No target directory specified" if !target || (File.exist?(target) && !File.directory?(target))
14
- unless copy_map.empty?
15
- verbose(Rake.application.options.trace || false) do
16
- copy_map do |dest, src|
17
- mkpath File.dirname(dest) rescue nil
18
- case filter
19
- when Proc, Method
20
- filtered = filter.call(File.read(src))
21
- File.open(dest, "w") { |file| file.write filtered }
22
- when Hash
23
- filtered = File.read(src).gsub(/\$\{.*\}/) { |str| filter[str[2..-2]] || str }
24
- File.open(dest, "w") { |file| file.write filtered }
25
- when nil
26
- cp src, dest
27
- else
28
- fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{filter}"
29
- end
30
- end
31
- touch target if File.exist?(target)
32
- end
33
- end
34
- end
26
+ def initialize() #:nodoc:
27
+ @sources = FileList[]
35
28
  end
36
29
 
30
+ # :call-seq:
31
+ # include(*files) => self
32
+ #
33
+ # Specifies files to include and returns self. See FileList#include.
37
34
  def include(*files)
38
- prerequisites.include *files
35
+ @sources.include *files
39
36
  self
40
37
  end
41
38
  alias :add :include
42
39
 
40
+ # :call-seq:
41
+ # exclude(*files) => self
42
+ #
43
+ # Specifies files to exclude and returns self. See FileList#exclude.
43
44
  def exclude(*files)
44
- prerequisites.exclude *files
45
+ @sources.exclude *files
45
46
  self
46
47
  end
47
48
 
49
+ # :call-seq:
50
+ # into(dir) => self
51
+ #
52
+ # Specifies the target directory and return self. This tells the filter task where
53
+ # to copy the source files to.
54
+ #
55
+ # For example:
56
+ # filter.include("*.HTML").into("docs").run
48
57
  def into(dir)
49
- dir = File.expand_path(dir)
50
- unless @target == dir
51
- @target = dir
52
- file(dir).enhance [self]
53
- end
58
+ @target = File.expand_path(dir.to_s)
54
59
  self
55
60
  end
56
61
 
57
- def using(filter, &block)
58
- self.filter = filter || block
62
+ # :call-seq:
63
+ # using(mapping) => self
64
+ # using() { |file_name, contents| ... } => self
65
+ #
66
+ # Specifies the mapping to use and returns self.
67
+ #
68
+ # The mapping can be a proc or a method called with the file name and content, returning
69
+ # the modified content. Or the mapping can be a Hash for mapping each ${key} into a value.
70
+ # Without any mapping, all files are copied as is.
71
+ #
72
+ # For example:
73
+ # filter.using "version"=>"1.2"
74
+ # will replace all occurrences of "${version}" with "1.2".
75
+ def using(mapping, &block)
76
+ self.mapping = mapping || block
59
77
  self
60
78
  end
61
79
 
62
- def needed?
80
+ # Run the filter.
81
+ def run()
82
+ #@sources.each { |src| Rake.application[src, Rake.application.current_scope].invoke }
83
+ if needed?
84
+ fail "No target directory specified" if !target || (File.exist?(target.to_s) && !File.directory?(target.to_s))
85
+ unless copy_map.empty?
86
+ verbose(Rake.application.options.trace || false) do
87
+ mkpath target.to_s
88
+ copy_map do |dest, src|
89
+ mkpath File.dirname(dest) rescue nil
90
+ case mapping
91
+ when Proc, Method # Call on input, accept output.
92
+ mapped = mapping.call(src, File.read(src))
93
+ File.open(dest, "w") { |file| file.write mapped }
94
+ when Hash # Map ${key} to value
95
+ mapped = File.read(src).gsub(/\$\{.*\}/) { |str| mapping[str[2..-2]] || str }
96
+ File.open(dest, "w") { |file| file.write mapped }
97
+ when nil # No mapping.
98
+ cp src, dest
99
+ else
100
+ fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{mapping}"
101
+ end
102
+ end
103
+ touch target.to_s
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ # Returns the target directory.
110
+ def to_s()
111
+ @target.to_s
112
+ end
113
+
114
+ private
115
+
116
+ def needed?()
63
117
  return false if target.nil? || copy_map.empty?
64
- return true unless File.exist?(target)
118
+ return true unless File.exist?(target.to_s)
65
119
  return true if copy_map.any? { |dest, src| !File.exist?(dest) || File.mtime(src) > File.mtime(dest) }
66
120
  false
67
121
  end
68
-
69
- protected
70
122
 
71
- # Return a copy map of all the files that need copying: the key is
72
- # the file to copy to, the value is the source file. If called with
73
- # a block, yields with each dest/source pair.
123
+ # Return a copy map of all the files that need copying: the key is the file to copy to,
124
+ # the value is the source file. If called with a block, yields with each dest/source pair.
74
125
  def copy_map(&block)
75
- # Create a map between the source file and the similarly named
76
- # file in the target directory, including all files nested inside
77
- # directories.
78
- @copy_map ||= prerequisites.map(&:to_s).inject({}) do |map, path|
126
+ # Create a map between the source file and the similarly named file in the target directory,
127
+ # including all files nested inside directories.
128
+ @copy_map ||= @sources.map(&:to_s).inject({}) do |map, path|
79
129
  if File.directory?(path)
80
- Dir[File.join(path, "**", "*")].each do |file|
81
- map[file.sub(File.dirname(path), target)] = file unless
82
- File.directory?(file) || prerequisites.exclude?(file)
130
+ Dir["#{path}/**/*"].each do |file|
131
+ map[file.sub(File.dirname(path), target.to_s)] = file unless
132
+ File.directory?(file) || @sources.exclude?(file)
83
133
  end
84
134
  elsif File.exist?(path)
85
- map[File.join(target, File.basename(path))] = path
135
+ map[File.join(target.to_s, File.basename(path))] = path
86
136
  end
87
137
  map
88
138
  end.reject do |dest, src|
@@ -98,10 +148,12 @@ module Buildr
98
148
 
99
149
  end
100
150
 
151
+ # :call-seq:
152
+ # filter(*files) => Filter
153
+ #
154
+ # Creates a filter task to operate on all the specified files.
101
155
  def filter(*files)
102
- task = nil
103
- namespace { task = FilterTask.define_task("filter").include *files }
104
- task
156
+ Filter.new.include *files
105
157
  end
106
158
 
107
159
  end
data/lib/tasks/zip.rb CHANGED
@@ -7,12 +7,43 @@ module Buildr
7
7
  # The ZipTask creates a new ZIP file. You can include any number of files and
8
8
  # and directories, use exclusion patterns, and include files into specific
9
9
  # directories.
10
+ #
11
+ # For example:
12
+ # zip("test.zip").tap do |task|
13
+ # task.include "srcs"
14
+ # task.include "README", "LICENSE"
15
+ # end
16
+ #
17
+ # See Buildr#zip.
10
18
  class ZipTask < Rake::FileTask
11
19
 
12
- # :nodoc:
13
- module IncludeFiles
20
+ # Which files go where. All the rules for including, excluding and merging files
21
+ # are handled by this object. A zip has at least one path.
22
+ class Path #:nodoc:
23
+
24
+ attr_reader :actions
25
+
26
+ def initialize(zip, path)
27
+ @zip = zip
28
+ @path = "#{path}/" if path
29
+ expand_src = proc { (@files || []).map(&:to_s).uniq }
30
+ @sources = [ expand_src ]
31
+ @actions = [] << proc do |zip|
32
+ expand_src.call.each do |file|
33
+ if File.directory?(file)
34
+ in_directory(file, @files) do |file, rel_path|
35
+ puts "Adding #{@path}#{rel_path}" if Rake.application.options.trace
36
+ zip.add("#{@path}#{rel_path}", file) { true }
37
+ end
38
+ else
39
+ puts "Adding #{@path}#{File.basename(file)}" if Rake.application.options.trace
40
+ zip.add("#{@path}#{File.basename(file)}", file) { true }
41
+ end
42
+ end
43
+ end
44
+ end
14
45
 
15
- # Include the specified files or directories.
46
+ # Documented in ZipTask.
16
47
  def include(*files)
17
48
  if Hash === files.last
18
49
  options = files.pop
@@ -37,14 +68,15 @@ module Buildr
37
68
  end
38
69
  self
39
70
  end
71
+ alias :add :include
40
72
 
41
- # Exclude the specified file or directories.
73
+ # Documented in ZipTask.
42
74
  def exclude(*files)
43
75
  (@files ||= FileList[]).exclude *files
44
76
  self
45
77
  end
46
- alias :add :include
47
78
 
79
+ # Documented in ZipTask.
48
80
  def merge(*files)
49
81
  if Hash === files.last
50
82
  options = files.pop
@@ -56,9 +88,9 @@ module Buildr
56
88
  path(options[:path]).merge *files +[ options.reject { |k,v| k == :path } ]
57
89
  elsif options.keys.empty?
58
90
  files.collect do |file|
59
- @expand_sources << proc { file.to_s }
91
+ @sources << proc { file.to_s }
60
92
  expander = ZipExpander.new(file)
61
- @add_files << proc { |zip| expander.expand(zip, @path) }
93
+ @actions << proc { |zip| expander.expand(zip, @path) }
62
94
  expander
63
95
  end.first
64
96
  else
@@ -66,97 +98,55 @@ module Buildr
66
98
  end
67
99
  end
68
100
 
69
- protected
101
+ # Documented in ZipTask.
102
+ def path(path)
103
+ path.blank? ? self : @zip.path("#{@path}#{path}")
104
+ end
70
105
 
71
- def setup_path(path = nil)
72
- @path = "#{path}/" if path
73
- expand_src = proc { (@files || []).map(&:to_s).uniq }
74
- @expand_sources = [ expand_src ]
75
- @add_files = [] << proc do |zip|
76
- expand_src.call.each do |file|
77
- if File.directory?(file)
78
- in_directory(file, @files) do |file, rel_path|
79
- puts "Adding #{@path}#{rel_path}" if Rake.application.options.trace
80
- zip.add "#{@path}#{rel_path}", file
81
- end
82
- else
83
- puts "Adding #{@path}#{File.basename(file)}" if Rake.application.options.trace
84
- zip.add "#{@path}#{File.basename(file)}", file
85
- end
86
- end
87
- end
106
+ # Documented in ZipTask.
107
+ def root()
108
+ @zip
109
+ end
110
+
111
+ # Returns all the source files.
112
+ def sources()
113
+ @sources.map(&:call).flatten
88
114
  end
89
115
 
116
+ protected
117
+
90
118
  def include_as(source, as)
91
- @expand_sources << proc { source }
92
- @add_files << proc do |zip|
119
+ @sources << proc { source }
120
+ @actions << proc do |zip|
93
121
  file = source.to_s
94
122
  if File.directory?(file)
95
123
  in_directory(file) do |file, rel_path|
96
- puts "Adding #{@path}#{as}/#{rel_path}" if Rake.application.options.trace
97
- zip.add file, "#{@path}#{as}/#{rel_path}"
124
+ if as == "."
125
+ dest = (@path || "") + rel_path.split("/")[1..-1].join("/")
126
+ else
127
+ dest = "#{@path}#{as}#{rel_path}"
128
+ end
129
+ puts "Adding #{dest}" if Rake.application.options.trace
130
+ zip.add(dest, file) { true }
98
131
  end
99
132
  else
100
133
  puts "Adding #{@path}#{as}" if Rake.application.options.trace
101
- zip.add "#{@path}#{as}", file
134
+ zip.add("#{@path}#{as}", file) { true }
102
135
  end
103
136
  end
104
137
  end
105
138
 
106
139
  def in_directory(dir, excludes = nil)
107
140
  prefix = Regexp.new("^" + Regexp.escape(File.dirname(dir) + File::SEPARATOR))
108
- Dir[File.join(dir, "**", "*")].
141
+ Dir["#{dir}/**/*"].
109
142
  reject { |file| File.directory?(file) || (excludes && excludes.exclude?(file)) }.
110
143
  each { |file| yield file, file.sub(prefix, "") }
111
144
  end
112
145
 
113
- def expand_sources()
114
- @expand_sources.map(&:call).flatten
115
- end
116
-
117
- def add_file(zip)
118
- @add_files.each { |action| action.call zip }
119
- end
120
-
121
- end
122
-
123
-
124
- # Which files go where.
125
- class Path
126
-
127
- include IncludeFiles
128
-
129
- def initialize(path)
130
- setup_path path
131
- end
132
-
133
- end
134
-
135
- include IncludeFiles
136
-
137
- def initialize(*args)
138
- super
139
- @paths = { nil=>self }
140
- setup_path
141
- enhance do |task|
142
- puts "Creating #{task.name}" if verbose
143
- # We're here because the Zip file does not exist, or one of the files is
144
- # newer than the Zip contents; in the later case, opening the Zip file
145
- # will add to its contents instead of replacing it, so we want the Zip
146
- # gone before we change it. We also don't want to see any partial updates.
147
- rm task.name, :verbose=>false rescue nil
148
- mkpath File.dirname(task.name), :verbose=>false
149
- begin
150
- Zip::ZipFile.open(task.name, Zip::ZipFile::CREATE) { |zip| create zip }
151
- rescue
152
- rm task.name, :verbose=>false rescue nil
153
- raise
154
- end
155
- end
156
146
  end
157
147
 
158
-
159
- class ZipExpander
148
+ # Extend one Zip file into another.
149
+ class ZipExpander #:nodoc:
160
150
 
161
151
  def initialize(zip_file)
162
152
  @zip_file = zip_file.to_s
@@ -183,7 +173,6 @@ module Buildr
183
173
  !@excludes.any? { |pattern| File.fnmatch(pattern, entry.name) }
184
174
  puts "Adding #{path}#{entry.name}" if Rake.application.options.trace
185
175
  zip.get_output_stream("#{path}#{entry.name}") { |output| output.write source.read(entry) }
186
- # TODO: read and write file
187
176
  end
188
177
  end
189
178
  end
@@ -191,17 +180,123 @@ module Buildr
191
180
 
192
181
  end
193
182
 
183
+ def initialize(*args) #:nodoc:
184
+ super
185
+ @paths = { nil=>Path.new(self, nil) }
186
+ enhance do |task|
187
+ puts "Creating #{task.name}" if verbose
188
+ # We're here because the Zip file does not exist, or one of the files is
189
+ # newer than the Zip contents; in the later case, opening the Zip file
190
+ # will add to its contents instead of replacing it, so we want the Zip
191
+ # gone before we change it. We also don't want to see any partial updates.
192
+ rm task.name, :verbose=>false rescue nil
193
+ mkpath File.dirname(task.name), :verbose=>false
194
+ begin
195
+ Zip::ZipFile.open(task.name, Zip::ZipFile::CREATE) do |zip|
196
+ zip.restore_permissions = true
197
+ create zip
198
+ end
199
+ rescue
200
+ rm task.name, :verbose=>false rescue nil
201
+ raise
202
+ end
203
+ end
204
+ end
194
205
 
195
- # Returns a path to which you can include/exclude files.
206
+ # :call-seq:
207
+ # include(*files) => self
208
+ # include(*files, :path=>path) => self
209
+ # include(file, :as=>name) => self
210
+ # include(*zips, :merge=>true) => self
211
+ #
212
+ # Include files in the ZIP (or current path) and returns self.
196
213
  #
214
+ # This method accepts three options. You can use :path to include files under
215
+ # a specific path, for example:
197
216
  # zip(..).include("foo", :path=>"bar")
198
- # is equivalen to:
217
+ # includes the file bar as foo/bar. See also #path.
218
+ #
219
+ # You can use :as to include a file under a different name, for example:
220
+ # zip(..).include("foo", :as=>"bar")
221
+ # You can use the :as option in combination with the :path option, but only with
222
+ # a single file at a time.
223
+ #
224
+ # As a special case, you can include the entire contents of a directory by including
225
+ # the directory and using :as=>".". This:
226
+ # zip(..).include("srcs", :as=>".")
227
+ # will include all the source files, using the directory as a prerequisite. This:
228
+ # zip(..).include("srcs/*")
229
+ # includes all the same source files, using the source files as a prerequisite.
230
+ #
231
+ # You can use :merge option to include the contents of another ZIP file, for example:
232
+ # zip(..).include("foo.zip", :merge=>true)
233
+ # You can use the :merge option in combination with the :path option. See also #merge.
234
+ def include(*files)
235
+ @paths[nil].include *files
236
+ self
237
+ end
238
+ alias :add :include
239
+
240
+ # :call-seq:
241
+ # exclude(*files) => self
242
+ #
243
+ # Excludes files and returns self. Can be used in combination with include to
244
+ # prevent some files from being included.
245
+ def exclude(*files)
246
+ @paths[nil].exclude *files
247
+ self
248
+ end
249
+
250
+ # :call-seq:
251
+ # merge(*files) => Merge
252
+ # merge(*files, :path=>name) => Merge
253
+ #
254
+ # Merges ZIP files and returns a merge object. The contents of the merged ZIP file is
255
+ # extracted into this ZIP file (or current path).
256
+ #
257
+ # The returned object supports two methods: include and exclude. You can use these to
258
+ # merge only specific files from the ZIP. For example:
259
+ # zip(..).merge("src.zip").include("module1/*")
260
+ #
261
+ # This differs from include with the :merge option, which returns self.
262
+ def merge(*files)
263
+ @paths[nil].merge *files
264
+ end
265
+
266
+ # :call-seq:
267
+ # path(name) => Path
268
+ #
269
+ # Returns a path object. You can use the path object to include files in a given
270
+ # path inside the ZIP file. The path object implements the include, exclude, merge,
271
+ # path and root methods.
272
+ #
273
+ # For example:
199
274
  # zip(..).path("bar").include("foo")
200
- def path(path)
201
- path.blank? ? @paths[nil] : (@paths[path] ||= Path.new(path))
275
+ # Will add the file foo under the name bar/foo.
276
+ #
277
+ # As a shorthand, you can also use the :path option:
278
+ # zip(..).include("foo", :path=>"bar")
279
+ def path(name)
280
+ name.blank? ? @paths[nil] : (@paths[name] ||= Path.new(self, name))
202
281
  end
203
282
 
204
- # Pass options to the task. Returns self.
283
+ # :call-seq:
284
+ # root() => ZipTask
285
+ #
286
+ # Returns the root path, essentially the ZipTask object itself. In case you are wondering
287
+ # down paths and want to go back.
288
+ def root()
289
+ self
290
+ end
291
+
292
+ # :call-seq:
293
+ # with(options) => self
294
+ #
295
+ # Pass options to the task. Returns self. ZipTask itself does not support any options,
296
+ # but other tasks (e.g. JarTask, WarTask) do.
297
+ #
298
+ # For example:
299
+ # package(:jar).with(:manifest=>"MANIFEST_MF")
205
300
  def with(options)
206
301
  options.each do |key, value|
207
302
  self[key] = value
@@ -209,16 +304,22 @@ module Buildr
209
304
  self
210
305
  end
211
306
 
307
+ # :call-seq:
308
+ # [name] = value
309
+ #
310
+ # Used by with method to set specific options. For example:
311
+ # package(:jar).with(:manifest=>"MANIFEST_MF")
312
+ # Or:
313
+ # package(:jar)[:manifest] = "MANIFEST_MF"
212
314
  def []=(key, value)
213
315
  fail "#{self.class} does not support the attribute #{key}"
214
316
  end
215
-
216
- def invoke_prerequisites()
217
- super
218
- @paths.collect { |name, path| path.expand_sources }.flatten.each { |src| file(src).invoke }
219
- end
220
317
 
221
- def needed?()
318
+ def prerequisites() #:nodoc:
319
+ super + @paths.collect { |name, path| path.sources }.flatten.each { |src| file(src) }
320
+ end
321
+
322
+ def needed?() #:nodoc:
222
323
  return true unless File.exist?(name)
223
324
  # You can do something like:
224
325
  # include("foo", :path=>"foo").exclude("foo/bar", path=>"foo").
@@ -230,26 +331,31 @@ module Buildr
230
331
  # contents of the ZIP. The file itself but also the directory it's
231
332
  # coming from, since some tasks touch the directory, e.g. when the
232
333
  # content of target/classes is included into a WAR.
233
- most_recent = @paths.collect { |name, path| path.expand_sources }.flatten.
234
- each { |src| File.directory?(src) ? FileList[File.join(src, "**", "*")] | [src] : src }.flatten.
334
+ most_recent = @paths.collect { |name, path| path.sources }.flatten.
335
+ each { |src| File.directory?(src) ? FileList["#{src}/**/*"] | [src] : src }.flatten.
235
336
  select { |file| File.exist?(file) }.collect { |file| File.stat(file).mtime }.max
236
337
  File.stat(name).mtime < (most_recent || Rake::EARLY) || super
237
338
  end
238
339
 
239
340
  protected
240
341
 
342
+ # Sub-classes override this method to perform additional creation tasks,
343
+ # e.g. creating a manifest file in a JAR.
241
344
  def create(zip)
242
- @paths.each { |name, obj| obj.add_file zip }
345
+ @paths.each { |name, obj| obj.actions.each { |action| action[zip] } }
243
346
  end
244
347
 
245
348
  end
246
349
 
350
+ # :call-seq:
351
+ # zip(file) => ZipTask
352
+ #
247
353
  # The ZipTask creates a new ZIP file. You can include any number of files and
248
354
  # and directories, use exclusion patterns, and include files into specific
249
355
  # directories.
250
356
  #
251
357
  # For example:
252
- # returning(zip("test.zip")) { |task|
358
+ # zip("test.zip").tap do |task|
253
359
  # task.include "srcs"
254
360
  # task.include "README", "LICENSE"
255
361
  # end
@@ -258,56 +364,68 @@ module Buildr
258
364
  end
259
365
 
260
366
 
261
- # The UnzipTask expands the contents of a ZIP file into a target directory.
262
- # You can include any number of files and directories, use exclusion patterns,
263
- # and expand files from relative paths.
367
+ # An object for unzipping a file into a target directory. You can tell it to include
368
+ # or exclude only specific files and directories, and also to map files from particular
369
+ # paths inside the zip file into the target directory. Once ready, call #extract.
264
370
  #
265
- # The file(s) to unzip is the first prerequisite.
266
- class UnzipTask < Rake::Task
371
+ # Usually it is more convenient to create a file task for extracting the zip file
372
+ # (see #unzip) and pass this object as a prerequisite to other tasks.
373
+ #
374
+ # See Buildr#unzip.
375
+ class Unzip
267
376
 
268
- # The target directory.
377
+ # The zip file to extract.
378
+ attr_accessor :zip_file
379
+ # The target directory to extract to.
269
380
  attr_accessor :target
270
381
 
271
- def initialize(*args)
272
- super
382
+ # Initialize with hash argument of the form target=>zip_file.
383
+ def initialize(args)
384
+ @target, @zip_file = Rake.application.resolve_args(args)
273
385
  @paths = {}
274
- enhance do |task|
275
- fail "Where do you want the file unzipped" unless target
386
+ end
276
387
 
277
- # If no paths specified, then no include/exclude patterns
278
- # specified. Nothing will happen unless we include all files.
279
- if @paths.empty?
280
- @paths[nil] = FromPath.new(nil)
281
- @paths[nil].include "*"
282
- end
388
+ # :call-seq:
389
+ # extract()
390
+ #
391
+ # Extract the zip file into the target directory.
392
+ #
393
+ # You can call this method directly. However, if you are using the #unzip method,
394
+ # it creates a file task for the target directory: use that task instead as a
395
+ # prerequisite. For example:
396
+ # build unzip(dir=>zip_file)
397
+ # Or:
398
+ # unzip(dir=>zip_file).target.invoke
399
+ def extract()
400
+ # If no paths specified, then no include/exclude patterns
401
+ # specified. Nothing will happen unless we include all files.
402
+ if @paths.empty?
403
+ @paths[nil] = FromPath.new(nil)
404
+ @paths[nil].include "*"
405
+ end
283
406
 
284
- # Otherwise, empty unzip creates target as a file when touching.
285
- mkpath target, :verbose=>false
286
- prerequisites.each do |file|
287
- Zip::ZipFile.open(file) do |zip|
288
- entries = zip.collect
289
- @paths.each do |path, patterns|
290
- patterns.map(entries).each do |dest, entry|
291
- next if entry.directory?
292
- dest = File.expand_path(dest, target)
293
- puts "Extracting #{dest}" if Rake.application.options.trace
294
- mkpath File.dirname(dest), :verbose=>false rescue nil
295
- entry.extract(dest) { true }
296
- end
297
- end
407
+ # Otherwise, empty unzip creates target as a file when touching.
408
+ mkpath target.to_s, :verbose=>false
409
+ Zip::ZipFile.open(zip_file.to_s) do |zip|
410
+ entries = zip.collect
411
+ @paths.each do |path, patterns|
412
+ patterns.map(entries).each do |dest, entry|
413
+ next if entry.directory?
414
+ dest = File.expand_path(dest, target.to_s)
415
+ puts "Extracting #{dest}" if Rake.application.options.trace
416
+ mkpath File.dirname(dest), :verbose=>false rescue nil
417
+ entry.extract(dest) { true }
298
418
  end
299
419
  end
300
- # Let other tasks know we updated the target directory.
301
- touch target, :verbose=>false
302
420
  end
421
+ # Let other tasks know we updated the target directory.
422
+ touch target.to_s, :verbose=>false
303
423
  end
304
424
 
305
- # Specifies directory to unzip to and return self.
306
- def into(target)
307
- self.target = target
308
- self
309
- end
310
-
425
+ # :call-seq:
426
+ # include(*files) => self
427
+ # include(*files, :path=>name) => self
428
+ #
311
429
  # Include all files that match the patterns and returns self.
312
430
  #
313
431
  # Use include if you only want to unzip some of the files, by specifying
@@ -323,6 +441,9 @@ module Buildr
323
441
  end
324
442
  alias :add :include
325
443
 
444
+ # :call-seq:
445
+ # exclude(*files) => self
446
+ #
326
447
  # Exclude all files that match the patterns and return self.
327
448
  #
328
449
  # Use exclude to unzip all files except those that match the pattern.
@@ -336,6 +457,9 @@ module Buildr
336
457
  self
337
458
  end
338
459
 
460
+ # :call-seq:
461
+ # from_path(name) => Path
462
+ #
339
463
  # Allows you to unzip from a path. Returns an object you can use to
340
464
  # specify which files to include/exclude relative to that path.
341
465
  # Expands the file relative to that path.
@@ -347,19 +471,16 @@ module Buildr
347
471
  # This is different from:
348
472
  # unzip("test.jar").into(Dir.pwd).include("etc/LICENSE")
349
473
  # which unzips etc/LICENSE into ./etc/LICENSE.
350
- def from_path(path)
351
- @paths[path] ||= FromPath.new(path)
474
+ def from_path(name)
475
+ @paths[name] ||= FromPath.new(name)
352
476
  end
353
477
 
354
- def needed?()
355
- #return true unless target && File.exist?(target)
356
- #return true if prerequisites.any? { |prereq| File.stat(prereq).mtime > File.stat(target).mtime }
357
- #false
358
- true
478
+ # Returns the path to the target directory.
479
+ def to_s()
480
+ target.to_s
359
481
  end
360
482
 
361
- # :nodoc:
362
- class FromPath
483
+ class FromPath #:nodoc:
363
484
 
364
485
  def initialize(path)
365
486
  if path
@@ -370,20 +491,19 @@ module Buildr
370
491
  end
371
492
 
372
493
  # See UnzipTask#include
373
- def include(*files)
494
+ def include(*files) #:doc:
374
495
  @include ||= []
375
496
  @include |= files
376
497
  self
377
498
  end
378
499
 
379
500
  # See UnzipTask#exclude
380
- def exclude(*files)
501
+ def exclude(*files) #:doc:
381
502
  @exclude ||= []
382
503
  @exclude |= files
383
504
  self
384
505
  end
385
506
 
386
- # :nodoc:
387
507
  def map(entries)
388
508
  includes = @include || ["*"]
389
509
  excludes = @exclude || []
@@ -401,22 +521,30 @@ module Buildr
401
521
 
402
522
  end
403
523
 
404
- # Defines a task that will unzip the specified file, into the directory
405
- # specified by calling #into. It is the second call to into that creates
406
- # and returns the task.
524
+ # :call-seq:
525
+ # unzip(to_dir=>zip_file) => Zip
526
+ #
527
+ # Creates a task that will unzip a file into the target directory. The task name
528
+ # is the target directory, the prerequisite is the file to unzip.
407
529
  #
408
- # You can unzip only some files by specifying an inclusion or exclusion
409
- # pattern, and unzip files from a path in the ZIP file. See UnzipTask
410
- # for more information.
530
+ # This method creates a file task to expand the zip file. It returns an Unzip object
531
+ # that specifies how the file will be extracted. You can include or exclude specific
532
+ # files from within the zip, and map to different paths.
533
+ #
534
+ # The Unzip object's to_s method return the path to the target directory, so you can
535
+ # use it as a prerequisite. By keeping the Unzip object separate from the file task,
536
+ # you overlay additional work on top of the file task.
411
537
  #
412
538
  # For example:
413
- # unzip("test.zip").into("test")
414
- # unzip("test.zip").into("etc").include("README", "LICENSE")
415
- # unzip("test.zip").into("src").from_path("srcs")
416
- def unzip(file)
417
- task = nil
418
- namespace { task = UnzipTask.define_task("unzip"=>file) }
419
- task
539
+ # unzip("all"=>"test.zip")
540
+ # unzip("src"=>"test.zip").include("README", "LICENSE")
541
+ # unzip("libs"=>"test.zip").from_path("libs")
542
+ def unzip(args)
543
+ target, zip_file = Rake.application.resolve_args(args)
544
+ task = file(File.expand_path(target.to_s)=>zip_file)
545
+ Unzip.new(task=>zip_file).tap do |setup|
546
+ task.enhance { setup.extract }
547
+ end
420
548
  end
421
549
 
422
550
  end