| Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
|---|---|---|---|---|
| lib/buildr/packaging/ziptask.rb | 356 | 185 | 12.64%
|
15.68%
|
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
1 # Licensed to the Apache Software Foundation (ASF) under one or more |
2 # contributor license agreements. See the NOTICE file distributed with this |
3 # work for additional information regarding copyright ownership. The ASF |
4 # licenses this file to you under the Apache License, Version 2.0 (the |
5 # "License"); you may not use this file except in compliance with the License. |
6 # You may obtain a copy of the License at |
7 # |
8 # http://www.apache.org/licenses/LICENSE-2.0 |
9 # |
10 # Unless required by applicable law or agreed to in writing, software |
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
13 # License for the specific language governing permissions and limitations under |
14 # the License. |
15 |
16 |
17 require 'buildr/packaging/archive' |
18 |
19 |
20 module Buildr |
21 |
22 # The ZipTask creates a new Zip file. You can include any number of files and and directories, |
23 # use exclusion patterns, and include files into specific directories. |
24 # |
25 # For example: |
26 # zip('test.zip').tap do |task| |
27 # task.include 'srcs' |
28 # task.include 'README', 'LICENSE' |
29 # end |
30 # |
31 # See Buildr#zip and ArchiveTask. |
32 class ZipTask < ArchiveTask |
33 |
34 # Compression leve for this Zip. |
35 attr_accessor :compression_level |
36 |
37 def initialize(*args) #:nodoc: |
38 self.compression_level = Zlib::DEFAULT_COMPRESSION |
39 super |
40 end |
41 |
42 # :call-seq: |
43 # entry(name) => Entry |
44 # |
45 # Returns a ZIP file entry. You can use this to check if the entry exists and its contents, |
46 # for example: |
47 # package(:jar).entry("META-INF/LICENSE").should contain(/Apache Software License/) |
48 def entry(entry_name) |
49 ::Zip::ZipEntry.new(name, entry_name) |
50 end |
51 |
52 def entries #:nodoc: |
53 @entries ||= Zip::ZipFile.open(name) { |zip| zip.entries } |
54 end |
55 |
56 private |
57 |
58 def create_from(file_map) |
59 Zip::ZipOutputStream.open name do |zip| |
60 seen = {} |
61 mkpath = lambda do |dir| |
62 dirname = (dir[-1..-1] =~ /\/$/) ? dir : dir + '/' |
63 unless dir == '.' || seen[dirname] |
64 mkpath.call File.dirname(dirname) |
65 zip.put_next_entry(dirname, compression_level) |
66 seen[dirname] = true |
67 end |
68 end |
69 |
70 file_map.each do |path, content| |
71 warn "Warning: Path in zipfile #{name} contains backslash: #{path}" if path =~ /\\/ |
72 mkpath.call File.dirname(path) |
73 if content.respond_to?(:call) |
74 zip.put_next_entry(path, compression_level) |
75 content.call zip |
76 elsif content.nil? || File.directory?(content.to_s) |
77 mkpath.call path |
78 else |
79 entry = zip.put_next_entry(path, compression_level) |
80 File.open content.to_s, 'rb' do |is| |
81 entry.unix_perms = is.stat.mode & 07777 |
82 while data = is.read(4096) |
83 zip << data |
84 end |
85 end |
86 end |
87 end |
88 end |
89 end |
90 |
91 end |
92 |
93 |
94 # :call-seq: |
95 # zip(file) => ZipTask |
96 # |
97 # The ZipTask creates a new Zip file. You can include any number of files and |
98 # and directories, use exclusion patterns, and include files into specific |
99 # directories. |
100 # |
101 # For example: |
102 # zip('test.zip').tap do |task| |
103 # task.include 'srcs' |
104 # task.include 'README', 'LICENSE' |
105 # end |
106 def zip(file) |
107 ZipTask.define_task(file) |
108 end |
109 |
110 |
111 # An object for unzipping/untarring a file into a target directory. You can tell it to include |
112 # or exclude only specific files and directories, and also to map files from particular |
113 # paths inside the zip file into the target directory. Once ready, call #extract. |
114 # |
115 # Usually it is more convenient to create a file task for extracting the zip file |
116 # (see #unzip) and pass this object as a prerequisite to other tasks. |
117 # |
118 # See Buildr#unzip. |
119 class Unzip |
120 |
121 # The zip file to extract. |
122 attr_accessor :zip_file |
123 # The target directory to extract to. |
124 attr_accessor :target |
125 |
126 # Initialize with hash argument of the form target=>zip_file. |
127 def initialize(args) |
128 @target, arg_names, zip_file = Buildr.application.resolve_args([args]) |
129 @zip_file = zip_file.first |
130 @paths = {} |
131 end |
132 |
133 # :call-seq: |
134 # extract |
135 # |
136 # Extract the zip/tgz file into the target directory. |
137 # |
138 # You can call this method directly. However, if you are using the #unzip method, |
139 # it creates a file task for the target directory: use that task instead as a |
140 # prerequisite. For example: |
141 # build unzip(dir=>zip_file) |
142 # Or: |
143 # unzip(dir=>zip_file).target.invoke |
144 def extract |
145 # If no paths specified, then no include/exclude patterns |
146 # specified. Nothing will happen unless we include all files. |
147 if @paths.empty? |
148 @paths[nil] = FromPath.new(self, nil) |
149 end |
150 |
151 # Otherwise, empty unzip creates target as a file when touching. |
152 mkpath target.to_s |
153 if zip_file.to_s.match /\.t?gz$/ |
154 #un-tar.gz |
155 Zlib::GzipReader.open(zip_file.to_s) { |tar| |
156 Archive::Tar::Minitar::Input.open(tar) do |inp| |
157 inp.each do |tar_entry| |
158 @paths.each do |path, patterns| |
159 patterns.map([tar_entry]).each do |dest, entry| |
160 next if entry.directory? |
161 dest = File.expand_path(dest, target.to_s) |
162 trace "Extracting #{dest}" |
163 mkpath File.dirname(dest) rescue nil |
164 #entry.restore_permissions = true |
165 File.open(dest, 'wb') {|f| f.write entry.read} |
166 end |
167 end |
168 end |
169 end |
170 } |
171 else |
172 Zip::ZipFile.open(zip_file.to_s) do |zip| |
173 entries = zip.collect |
174 @paths.each do |path, patterns| |
175 patterns.map(entries).each do |dest, entry| |
176 next if entry.directory? |
177 dest = File.expand_path(dest, target.to_s) |
178 trace "Extracting #{dest}" |
179 mkpath File.dirname(dest) rescue nil |
180 entry.restore_permissions = true |
181 entry.extract(dest) { true } |
182 end |
183 end |
184 end |
185 end |
186 # Let other tasks know we updated the target directory. |
187 touch target.to_s |
188 end |
189 |
190 #reads the includes/excludes and apply them to the entry_name |
191 def included?(entry_name) |
192 @paths.each do |path, patterns| |
193 return true if path.nil? |
194 if entry_name =~ /^#{path}/ |
195 short = entry_name.sub(path, '') |
196 if patterns.include.any? { |pattern| File.fnmatch(pattern, entry_name) } && |
197 !patterns.exclude.any? { |pattern| File.fnmatch(pattern, entry_name) } |
198 # trace "tar_entry.full_name " + entry_name + " is included" |
199 return true |
200 end |
201 end |
202 end |
203 # trace "tar_entry.full_name " + entry_name + " is excluded" |
204 return false |
205 end |
206 |
207 |
208 # :call-seq: |
209 # include(*files) => self |
210 # include(*files, :path=>name) => self |
211 # |
212 # Include all files that match the patterns and returns self. |
213 # |
214 # Use include if you only want to unzip some of the files, by specifying |
215 # them instead of using exclusion. You can use #include in combination |
216 # with #exclude. |
217 def include(*files) |
218 if Hash === files.last |
219 from_path(files.pop[:path]).include *files |
220 else |
221 from_path(nil).include *files |
222 end |
223 self |
224 end |
225 alias :add :include |
226 |
227 # :call-seq: |
228 # exclude(*files) => self |
229 # |
230 # Exclude all files that match the patterns and return self. |
231 # |
232 # Use exclude to unzip all files except those that match the pattern. |
233 # You can use #exclude in combination with #include. |
234 def exclude(*files) |
235 if Hash === files.last |
236 from_path(files.pop[:path]).exclude *files |
237 else |
238 from_path(nil).exclude *files |
239 end |
240 self |
241 end |
242 |
243 # :call-seq: |
244 # from_path(name) => Path |
245 # |
246 # Allows you to unzip from a path. Returns an object you can use to |
247 # specify which files to include/exclude relative to that path. |
248 # Expands the file relative to that path. |
249 # |
250 # For example: |
251 # unzip(Dir.pwd=>'test.jar').from_path('etc').include('LICENSE') |
252 # will unzip etc/LICENSE into ./LICENSE. |
253 # |
254 # This is different from: |
255 # unzip(Dir.pwd=>'test.jar').include('etc/LICENSE') |
256 # which unzips etc/LICENSE into ./etc/LICENSE. |
257 def from_path(name) |
258 @paths[name] ||= FromPath.new(self, name) |
259 end |
260 alias :path :from_path |
261 |
262 # :call-seq: |
263 # root => Unzip |
264 # |
265 # Returns the root path, essentially the Unzip object itself. In case you are wondering |
266 # down paths and want to go back. |
267 def root |
268 self |
269 end |
270 |
271 # Returns the path to the target directory. |
272 def to_s |
273 target.to_s |
274 end |
275 |
276 class FromPath #:nodoc: |
277 |
278 def initialize(unzip, path) |
279 @unzip = unzip |
280 if path |
281 @path = path[-1] == ?/ ? path : path + '/' |
282 else |
283 @path = '' |
284 end |
285 end |
286 |
287 # See UnzipTask#include |
288 def include(*files) #:doc: |
289 @include ||= [] |
290 @include |= files |
291 self |
292 end |
293 |
294 # See UnzipTask#exclude |
295 def exclude(*files) #:doc: |
296 @exclude ||= [] |
297 @exclude |= files |
298 self |
299 end |
300 |
301 def map(entries) |
302 includes = @include || ['*'] |
303 excludes = @exclude || [] |
304 entries.inject({}) do |map, entry| |
305 if entry.name =~ /^#{@path}/ |
306 short = entry.name.sub(@path, '') |
307 if includes.any? { |pat| File.fnmatch(pat, short) } && |
308 !excludes.any? { |pat| File.fnmatch(pat, short) } |
309 map[short] = entry |
310 end |
311 end |
312 map |
313 end |
314 end |
315 |
316 # Documented in Unzip. |
317 def root |
318 @unzip |
319 end |
320 |
321 # The target directory to extract to. |
322 def target |
323 @unzip.target |
324 end |
325 |
326 end |
327 |
328 end |
329 |
330 # :call-seq: |
331 # unzip(to_dir=>zip_file) => Zip |
332 # |
333 # Creates a task that will unzip a file into the target directory. The task name |
334 # is the target directory, the prerequisite is the file to unzip. |
335 # |
336 # This method creates a file task to expand the zip file. It returns an Unzip object |
337 # that specifies how the file will be extracted. You can include or exclude specific |
338 # files from within the zip, and map to different paths. |
339 # |
340 # The Unzip object's to_s method return the path to the target directory, so you can |
341 # use it as a prerequisite. By keeping the Unzip object separate from the file task, |
342 # you overlay additional work on top of the file task. |
343 # |
344 # For example: |
345 # unzip('all'=>'test.zip') |
346 # unzip('src'=>'test.zip').include('README', 'LICENSE') |
347 # unzip('libs'=>'test.zip').from_path('libs') |
348 def unzip(args) |
349 target, arg_names, zip_file = Buildr.application.resolve_args([args]) |
350 task = file(File.expand_path(target.to_s)=>zip_file) |
351 Unzip.new(task=>zip_file).tap do |setup| |
352 task.enhance { setup.extract } |
353 end |
354 end |
355 |
356 end |
Generated on 2011-07-06 23:35:38 -0700 with rcov 0.9.8