vic-buildr 1.3.3 → 1.3.4
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 +42 -11
- data/Rakefile +5 -3
- data/_buildr +9 -31
- data/addon/buildr/cobertura.rb +5 -218
- data/addon/buildr/drb.rb +281 -0
- data/addon/buildr/emma.rb +5 -220
- data/addon/buildr/nailgun.rb +94 -686
- data/bin/buildr +0 -9
- data/buildr.gemspec +6 -6
- data/doc/images/favicon.png +0 -0
- data/doc/pages/contributing.textile +6 -4
- data/doc/pages/download.textile +11 -0
- data/doc/pages/extending.textile +2 -2
- data/doc/pages/getting_started.textile +4 -4
- data/doc/pages/index.textile +8 -11
- data/doc/pages/more_stuff.textile +50 -22
- data/doc/pages/packaging.textile +1 -1
- data/doc/pages/projects.textile +2 -2
- data/doc/pages/settings_profiles.textile +2 -2
- data/doc/pages/testing.textile +1 -1
- data/doc/pages/whats_new.textile +12 -0
- data/doc/site.haml +1 -0
- data/lib/buildr.rb +2 -4
- data/lib/buildr/core.rb +2 -0
- data/lib/buildr/core/application.rb +304 -149
- data/lib/buildr/core/checks.rb +3 -131
- data/lib/buildr/core/common.rb +0 -4
- data/lib/buildr/core/compile.rb +1 -7
- data/lib/buildr/core/environment.rb +0 -3
- data/lib/buildr/core/filter.rb +7 -3
- data/lib/buildr/core/generate.rb +50 -52
- data/lib/buildr/core/help.rb +2 -1
- data/lib/buildr/core/osx.rb +49 -0
- data/lib/buildr/core/progressbar.rb +1 -1
- data/lib/buildr/core/project.rb +7 -9
- data/lib/buildr/core/test.rb +4 -4
- data/lib/buildr/core/transports.rb +13 -30
- data/lib/buildr/core/util.rb +8 -3
- data/lib/buildr/groovy/bdd.rb +1 -0
- data/lib/buildr/groovy/compiler.rb +1 -1
- data/lib/buildr/ide/eclipse.rb +30 -20
- data/lib/buildr/ide/idea.rb +3 -2
- data/lib/buildr/ide/idea7x.rb +4 -2
- data/lib/buildr/java/ant.rb +1 -1
- data/lib/buildr/java/bdd.rb +9 -5
- data/lib/buildr/java/cobertura.rb +236 -0
- data/lib/buildr/java/commands.rb +2 -1
- data/lib/buildr/java/emma.rb +238 -0
- data/lib/buildr/java/jtestr_runner.rb.erb +2 -0
- data/lib/buildr/java/packaging.rb +6 -2
- data/lib/buildr/java/pom.rb +0 -4
- data/lib/buildr/java/test_result.rb +45 -15
- data/lib/buildr/java/tests.rb +14 -9
- data/lib/buildr/packaging.rb +5 -2
- data/lib/buildr/packaging/archive.rb +488 -0
- data/lib/buildr/packaging/artifact.rb +36 -7
- data/lib/buildr/packaging/artifact_namespace.rb +2 -2
- data/lib/buildr/packaging/gems.rb +3 -3
- data/lib/buildr/packaging/package.rb +1 -1
- data/lib/buildr/packaging/tar.rb +85 -3
- data/lib/buildr/packaging/version_requirement.rb +172 -0
- data/lib/buildr/packaging/zip.rb +24 -682
- data/lib/buildr/packaging/ziptask.rb +313 -0
- data/lib/buildr/scala/compiler.rb +1 -1
- data/lib/buildr/scala/tests.rb +2 -2
- data/rakelib/apache.rake +58 -8
- data/rakelib/package.rake +4 -1
- data/rakelib/rspec.rake +2 -2
- data/rakelib/rubyforge.rake +6 -3
- data/rakelib/scm.rake +1 -1
- data/rakelib/setup.rake +0 -5
- data/rakelib/stage.rake +4 -1
- data/spec/addon/drb_spec.rb +328 -0
- data/spec/core/application_spec.rb +29 -22
- data/spec/core/build_spec.rb +8 -0
- data/spec/core/checks_spec.rb +293 -311
- data/spec/core/common_spec.rb +8 -2
- data/spec/core/compile_spec.rb +17 -1
- data/spec/core/generate_spec.rb +33 -0
- data/spec/core/project_spec.rb +18 -10
- data/spec/core/test_spec.rb +24 -1
- data/spec/ide/eclipse_spec.rb +96 -28
- data/spec/java/ant.rb +5 -0
- data/spec/java/bdd_spec.rb +4 -4
- data/spec/{addon → java}/cobertura_spec.rb +3 -3
- data/spec/{addon → java}/emma_spec.rb +3 -3
- data/spec/java/java_spec.rb +9 -1
- data/spec/java/packaging_spec.rb +19 -2
- data/spec/{addon → java}/test_coverage_spec.rb +7 -1
- data/spec/java/tests_spec.rb +5 -0
- data/spec/packaging/archive_spec.rb +1 -1
- data/spec/{core → packaging}/artifact_namespace_spec.rb +2 -2
- data/spec/packaging/artifact_spec.rb +46 -5
- data/spec/packaging/packaging_spec.rb +1 -1
- data/spec/sandbox.rb +16 -14
- data/spec/spec_helpers.rb +26 -3
- metadata +20 -11
- data/lib/buildr/core/application_cli.rb +0 -139
@@ -14,7 +14,6 @@
|
|
14
14
|
# the License.
|
15
15
|
|
16
16
|
|
17
|
-
require 'builder'
|
18
17
|
require 'buildr/core/project'
|
19
18
|
require 'buildr/core/transports'
|
20
19
|
require 'buildr/packaging/artifact_namespace'
|
@@ -25,6 +24,9 @@ module Buildr
|
|
25
24
|
desc 'Download all artifacts'
|
26
25
|
task 'artifacts'
|
27
26
|
|
27
|
+
desc "Download all artifacts' sources"
|
28
|
+
task 'artifacts:sources'
|
29
|
+
|
28
30
|
# Mixin with a task to make it behave like an artifact. Implemented by the packaging tasks.
|
29
31
|
#
|
30
32
|
# An artifact has an identifier, group identifier, type, version number and
|
@@ -81,7 +83,7 @@ module Buildr
|
|
81
83
|
# Returns the artifact specification, in the structure:
|
82
84
|
# <group>:<artifact>:<type>:<version>
|
83
85
|
# or
|
84
|
-
# <group>:<artifact>:<type>:<classifier
|
86
|
+
# <group>:<artifact>:<type>:<classifier>:<version>
|
85
87
|
def to_spec
|
86
88
|
classifier ? "#{group}:#{id}:#{type}:#{classifier}:#{version}" : "#{group}:#{id}:#{type}:#{version}"
|
87
89
|
end
|
@@ -95,6 +97,17 @@ module Buildr
|
|
95
97
|
Buildr.artifact(:group=>group, :id=>id, :version=>version, :type=>:pom)
|
96
98
|
end
|
97
99
|
|
100
|
+
# :call-seq:
|
101
|
+
# sources_artifact => Artifact
|
102
|
+
#
|
103
|
+
# Convenience method that returns a sources artifact.
|
104
|
+
def sources_artifact
|
105
|
+
sources_spec = to_spec_hash.merge(:classifier=>'sources')
|
106
|
+
sources_task = OptionalArtifact.define_task(Buildr.repositories.locate(sources_spec))
|
107
|
+
sources_task.send :apply_spec, sources_spec
|
108
|
+
sources_task
|
109
|
+
end
|
110
|
+
|
98
111
|
# :call-seq:
|
99
112
|
# pom_xml => string
|
100
113
|
#
|
@@ -110,7 +123,7 @@ module Buildr
|
|
110
123
|
xml.classifier classifier if classifier
|
111
124
|
end
|
112
125
|
end
|
113
|
-
|
126
|
+
|
114
127
|
def install
|
115
128
|
pom.install if pom && pom != self
|
116
129
|
invoke
|
@@ -312,7 +325,7 @@ module Buildr
|
|
312
325
|
# Use this when you want to install or upload an artifact from a given file, for example:
|
313
326
|
# test = artifact('group:id:jar:1.0').from('test.jar')
|
314
327
|
# install test
|
315
|
-
# See also Buildr#install and Buildr#
|
328
|
+
# See also Buildr#install and Buildr#upload.
|
316
329
|
def from(path)
|
317
330
|
path = File.expand_path(path.to_s)
|
318
331
|
enhance [path] do
|
@@ -340,11 +353,10 @@ module Buildr
|
|
340
353
|
# download
|
341
354
|
#
|
342
355
|
# Downloads an artifact from one of the remote repositories, and stores it in the local
|
343
|
-
# repository.
|
344
|
-
# artifact in the local repository. Raises an exception if the artifact is not found.
|
356
|
+
# repository. Raises an exception if the artifact is not found.
|
345
357
|
#
|
346
358
|
# This method attempts to download the artifact from each repository in the order in
|
347
|
-
# which they are returned from #remote, until successful.
|
359
|
+
# which they are returned from #remote, until successful.
|
348
360
|
def download
|
349
361
|
trace "Downloading #{to_spec}"
|
350
362
|
remote = Buildr.repositories.remote.map { |repo_url| URI === repo_url ? repo_url : URI.parse(repo_url) }
|
@@ -407,6 +419,22 @@ module Buildr
|
|
407
419
|
fail "Failed to download #{to_spec}, tried the following repositories:\n#{remote_uris.join("\n")}"
|
408
420
|
end
|
409
421
|
end
|
422
|
+
|
423
|
+
|
424
|
+
# An artifact that is optional.
|
425
|
+
# If downloading fails, the user will be informed but it will not raise an exception.
|
426
|
+
class OptionalArtifact < Artifact
|
427
|
+
|
428
|
+
protected
|
429
|
+
|
430
|
+
# If downloading fails, the user will be informed but it will not raise an exception.
|
431
|
+
def download
|
432
|
+
super
|
433
|
+
rescue
|
434
|
+
info "Failed to download #{to_spec}. Skipping it."
|
435
|
+
end
|
436
|
+
|
437
|
+
end
|
410
438
|
|
411
439
|
|
412
440
|
# Holds the path to the local repository, URLs for remote repositories, and settings for release server.
|
@@ -594,6 +622,7 @@ module Buildr
|
|
594
622
|
task.send :apply_spec, spec
|
595
623
|
Rake::Task['rake:artifacts'].enhance [task]
|
596
624
|
Artifact.register(task)
|
625
|
+
Rake::Task['artifacts:sources'].enhance [task.sources_artifact] unless spec[:type] == :pom
|
597
626
|
end
|
598
627
|
task.enhance &block
|
599
628
|
end
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# the License.
|
15
15
|
|
16
16
|
|
17
|
-
require 'buildr/
|
17
|
+
require 'buildr/packaging/version_requirement'
|
18
18
|
|
19
19
|
|
20
20
|
module Buildr
|
@@ -883,7 +883,7 @@ module Buildr
|
|
883
883
|
case name.to_s
|
884
884
|
when /!$/ then
|
885
885
|
name = $`.intern
|
886
|
-
if args.size < 1
|
886
|
+
if args.size < 1 || args.size > 2
|
887
887
|
raise ArgumentError.new("wrong number of arguments for #{name}!(spec, version_requirement?)")
|
888
888
|
end
|
889
889
|
need name => args.first
|
@@ -15,9 +15,9 @@
|
|
15
15
|
|
16
16
|
|
17
17
|
require 'buildr/packaging/package'
|
18
|
-
require 'buildr/packaging/
|
19
|
-
|
20
|
-
|
18
|
+
require 'buildr/packaging/archive'
|
19
|
+
gem 'rubyforge' ; autoload :RubyForge, 'rubyforge'
|
20
|
+
Gem.autoload :Package, 'rubygems/package'
|
21
21
|
|
22
22
|
|
23
23
|
module Buildr
|
@@ -35,7 +35,7 @@ module Buildr
|
|
35
35
|
Project.local_task('uninstall') { |name| "Uninstalling packages from #{name}" }
|
36
36
|
desc 'Upload packages created by the project'
|
37
37
|
Project.local_task('upload'=>'package') { |name| "Deploying packages from #{name}" }
|
38
|
-
# Anything that comes after local packaging (install,
|
38
|
+
# Anything that comes after local packaging (install, upload) executes the integration tests,
|
39
39
|
# which do not conflict with integration invoking the project's own packaging (package=>
|
40
40
|
# integration=>foo:package is not circular, just confusing to debug.)
|
41
41
|
task 'package' do
|
data/lib/buildr/packaging/tar.rb
CHANGED
@@ -14,8 +14,8 @@
|
|
14
14
|
# the License.
|
15
15
|
|
16
16
|
|
17
|
-
require 'buildr/packaging/
|
18
|
-
|
17
|
+
require 'buildr/packaging/archive'
|
18
|
+
gem 'archive-tar-minitar' ; autoload :Archive, 'archive/tar/minitar'
|
19
19
|
|
20
20
|
|
21
21
|
module Buildr
|
@@ -45,6 +45,35 @@ module Buildr
|
|
45
45
|
self.mode = '0755'
|
46
46
|
end
|
47
47
|
|
48
|
+
# :call-seq:
|
49
|
+
# entry(name) => Entry
|
50
|
+
#
|
51
|
+
# Returns a Tar file entry. You can use this to check if the entry exists and its contents,
|
52
|
+
# for example:
|
53
|
+
# package(:tar).entry("src/LICENSE").should contain(/Apache Software License/)
|
54
|
+
def entry(entry_name)
|
55
|
+
Buildr::TarEntry.new(self, entry_name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def entries() #:nodoc:
|
59
|
+
tar_entries = nil
|
60
|
+
with_uncompressed_tar { |tar| tar_entries = tar.entries }
|
61
|
+
tar_entries
|
62
|
+
end
|
63
|
+
|
64
|
+
# :call-seq:
|
65
|
+
# with_uncompressed_tar { |tar_entries| ... }
|
66
|
+
#
|
67
|
+
# Yields an Archive::Tar::Minitar::Input object to the provided block.
|
68
|
+
# Opening, closing and Gzip-decompressing is automatically taken care of.
|
69
|
+
def with_uncompressed_tar &block
|
70
|
+
if gzip
|
71
|
+
Zlib::GzipReader.open(name) { |tar| Archive::Tar::Minitar.open(tar, &block) }
|
72
|
+
else
|
73
|
+
Archive::Tar::Minitar.open(name, &block)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
48
77
|
private
|
49
78
|
|
50
79
|
def create_from(file_map)
|
@@ -81,7 +110,60 @@ module Buildr
|
|
81
110
|
end
|
82
111
|
|
83
112
|
end
|
84
|
-
|
113
|
+
|
114
|
+
|
115
|
+
class TarEntry #:nodoc:
|
116
|
+
|
117
|
+
def initialize(tar_task, entry_name)
|
118
|
+
@tar_task = tar_task
|
119
|
+
@entry_name = entry_name
|
120
|
+
end
|
121
|
+
|
122
|
+
# :call-seq:
|
123
|
+
# contain?(*patterns) => boolean
|
124
|
+
#
|
125
|
+
# Returns true if this Tar file entry matches against all the arguments. An argument may be
|
126
|
+
# a string or regular expression.
|
127
|
+
def contain?(*patterns)
|
128
|
+
content = read_content_from_tar
|
129
|
+
patterns.map { |pattern| Regexp === pattern ? pattern : Regexp.new(Regexp.escape(pattern.to_s)) }.
|
130
|
+
all? { |pattern| content =~ pattern }
|
131
|
+
end
|
132
|
+
|
133
|
+
# :call-seq:
|
134
|
+
# empty?() => boolean
|
135
|
+
#
|
136
|
+
# Returns true if this entry is empty.
|
137
|
+
def empty?()
|
138
|
+
read_content_from_tar.nil?
|
139
|
+
end
|
140
|
+
|
141
|
+
# :call-seq:
|
142
|
+
# exist() => boolean
|
143
|
+
#
|
144
|
+
# Returns true if this entry exists.
|
145
|
+
def exist?()
|
146
|
+
exist = false
|
147
|
+
@tar_task.with_uncompressed_tar { |tar| exist = tar.any? { |entry| entry.name == @entry_name } }
|
148
|
+
exist
|
149
|
+
end
|
150
|
+
|
151
|
+
def to_s #:nodoc:
|
152
|
+
@entry_name
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def read_content_from_tar
|
158
|
+
content = Errno::ENOENT.new("No such file or directory - #{@entry_name}")
|
159
|
+
@tar_task.with_uncompressed_tar do |tar|
|
160
|
+
content = tar.inject(content) { |content, entry| entry.name == @entry_name ? entry.read : content }
|
161
|
+
end
|
162
|
+
raise content if Exception === content
|
163
|
+
content
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
85
167
|
end
|
86
168
|
|
87
169
|
|
@@ -0,0 +1,172 @@
|
|
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
|
+
module Buildr
|
18
|
+
|
19
|
+
#
|
20
|
+
# See ArtifactNamespace#need
|
21
|
+
class VersionRequirement
|
22
|
+
|
23
|
+
CMP_PROCS = Gem::Requirement::OPS.dup
|
24
|
+
CMP_REGEX = Gem::Requirement::OP_RE.dup
|
25
|
+
CMP_CHARS = CMP_PROCS.keys.join
|
26
|
+
BOOL_CHARS = '\|\&\!'
|
27
|
+
VER_CHARS = '\w\.\-'
|
28
|
+
|
29
|
+
class << self
|
30
|
+
# is +str+ a version string?
|
31
|
+
def version?(str)
|
32
|
+
/^\s*[#{VER_CHARS}]+\s*$/ === str
|
33
|
+
end
|
34
|
+
|
35
|
+
# is +str+ a version requirement?
|
36
|
+
def requirement?(str)
|
37
|
+
/[#{BOOL_CHARS}#{CMP_CHARS}\(\)]/ === str
|
38
|
+
end
|
39
|
+
|
40
|
+
# :call-seq:
|
41
|
+
# VersionRequirement.create(" >1 <2 !(1.5) ") -> requirement
|
42
|
+
#
|
43
|
+
# parse the +str+ requirement
|
44
|
+
def create(str)
|
45
|
+
instance_eval normalize(str)
|
46
|
+
rescue StandardError => e
|
47
|
+
raise "Failed to parse #{str.inspect} due to: #{e}"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def requirement(req)
|
52
|
+
unless req =~ /^\s*(#{CMP_REGEX})?\s*([#{VER_CHARS}]+)\s*$/
|
53
|
+
raise "Invalid requirement string: #{req}"
|
54
|
+
end
|
55
|
+
comparator, version = $1, $2
|
56
|
+
version = Gem::Version.new(0).tap { |v| v.version = version }
|
57
|
+
VersionRequirement.new(nil, [$1, version])
|
58
|
+
end
|
59
|
+
|
60
|
+
def negate(vreq)
|
61
|
+
vreq.negative = !vreq.negative
|
62
|
+
vreq
|
63
|
+
end
|
64
|
+
|
65
|
+
def normalize(str)
|
66
|
+
str = str.strip
|
67
|
+
if str[/[^\s\(\)#{BOOL_CHARS + VER_CHARS + CMP_CHARS}]/]
|
68
|
+
raise "version string #{str.inspect} contains invalid characters"
|
69
|
+
end
|
70
|
+
str.gsub!(/\s+(and|\&\&)\s+/, ' & ')
|
71
|
+
str.gsub!(/\s+(or|\|\|)\s+/, ' | ')
|
72
|
+
str.gsub!(/(^|\s*)not\s+/, ' ! ')
|
73
|
+
pattern = /(#{CMP_REGEX})?\s*[#{VER_CHARS}]+/
|
74
|
+
left_pattern = /[#{VER_CHARS}\)]$/
|
75
|
+
right_pattern = /^(#{pattern}|\()/
|
76
|
+
str = str.split.inject([]) do |ary, i|
|
77
|
+
ary << '&' if ary.last =~ left_pattern && i =~ right_pattern
|
78
|
+
ary << i
|
79
|
+
end
|
80
|
+
str = str.join(' ')
|
81
|
+
str.gsub!(/!([^=])?/, ' negate \1')
|
82
|
+
str.gsub!(pattern) do |expr|
|
83
|
+
case expr.strip
|
84
|
+
when 'not', 'negate' then 'negate '
|
85
|
+
else 'requirement("' + expr + '")'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
str.gsub!(/negate\s+\(/, 'negate(')
|
89
|
+
str
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def initialize(op, *requirements) #:nodoc:
|
94
|
+
@op, @requirements = op, requirements
|
95
|
+
end
|
96
|
+
|
97
|
+
# Is this object a composed requirement?
|
98
|
+
# VersionRequirement.create('1').composed? -> false
|
99
|
+
# VersionRequirement.create('1 | 2').composed? -> true
|
100
|
+
# VersionRequirement.create('1 & 2').composed? -> true
|
101
|
+
def composed?
|
102
|
+
requirements.size > 1
|
103
|
+
end
|
104
|
+
|
105
|
+
# Return the last requirement on this object having an = operator.
|
106
|
+
def default
|
107
|
+
default = nil
|
108
|
+
requirements.reverse.find do |r|
|
109
|
+
if Array === r
|
110
|
+
if !negative && (r.first.nil? || r.first.include?('='))
|
111
|
+
default = r.last.to_s
|
112
|
+
end
|
113
|
+
else
|
114
|
+
default = r.default
|
115
|
+
end
|
116
|
+
end
|
117
|
+
default
|
118
|
+
end
|
119
|
+
|
120
|
+
# Test if this requirement can be satisfied by +version+
|
121
|
+
def satisfied_by?(version)
|
122
|
+
return false unless version
|
123
|
+
unless version.kind_of?(Gem::Version)
|
124
|
+
raise "Invalid version: #{version.inspect}" unless self.class.version?(version)
|
125
|
+
version = Gem::Version.new(0).tap { |v| v.version = version.strip }
|
126
|
+
end
|
127
|
+
message = op == :| ? :any? : :all?
|
128
|
+
result = requirements.send message do |req|
|
129
|
+
if Array === req
|
130
|
+
cmp, rv = *req
|
131
|
+
CMP_PROCS[cmp || '='].call(version, rv)
|
132
|
+
else
|
133
|
+
req.satisfied_by?(version)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
negative ? !result : result
|
137
|
+
end
|
138
|
+
|
139
|
+
# Either modify the current requirement (if it's already an or operation)
|
140
|
+
# or create a new requirement
|
141
|
+
def |(other)
|
142
|
+
operation(:|, other)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Either modify the current requirement (if it's already an and operation)
|
146
|
+
# or create a new requirement
|
147
|
+
def &(other)
|
148
|
+
operation(:&, other)
|
149
|
+
end
|
150
|
+
|
151
|
+
# return the parsed expression
|
152
|
+
def to_s
|
153
|
+
str = requirements.map(&:to_s).join(" " + @op.to_s + " ").to_s
|
154
|
+
str = "( " + str + " )" if negative || requirements.size > 1
|
155
|
+
str = "!" + str if negative
|
156
|
+
str
|
157
|
+
end
|
158
|
+
|
159
|
+
attr_accessor :negative
|
160
|
+
protected
|
161
|
+
attr_reader :requirements, :op
|
162
|
+
def operation(op, other)
|
163
|
+
@op ||= op
|
164
|
+
if negative == other.negative && @op == op && other.requirements.size == 1
|
165
|
+
@requirements << other.requirements.first
|
166
|
+
self
|
167
|
+
else
|
168
|
+
self.class.new(op, self, other)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end # VersionRequirement
|
172
|
+
end
|
data/lib/buildr/packaging/zip.rb
CHANGED
@@ -14,709 +14,51 @@
|
|
14
14
|
# the License.
|
15
15
|
|
16
16
|
|
17
|
-
$LOADED_FEATURES.unshift 'ftools' if RUBY_VERSION >= '1.9.0'
|
17
|
+
$LOADED_FEATURES.unshift 'ftools' if RUBY_VERSION >= '1.9.0' # Required to properly load RubyZip under Ruby 1.9
|
18
18
|
require 'zip/zip'
|
19
19
|
require 'zip/zipfilesystem'
|
20
20
|
|
21
21
|
|
22
|
-
module
|
23
|
-
|
24
|
-
# Base class for ZipTask, TarTask and other archives.
|
25
|
-
class ArchiveTask < Rake::FileTask
|
26
|
-
|
27
|
-
# Which files go where. All the rules for including, excluding and merging files
|
28
|
-
# are handled by this object.
|
29
|
-
class Path #:nodoc:
|
30
|
-
|
31
|
-
# Returns the archive from this path.
|
32
|
-
attr_reader :root
|
33
|
-
|
34
|
-
def initialize(root, path)
|
35
|
-
@root = root
|
36
|
-
@path = path.empty? ? path : "#{path}/"
|
37
|
-
@includes = FileList[]
|
38
|
-
@excludes = []
|
39
|
-
# Expand source files added to this path.
|
40
|
-
expand_src = proc { @includes.map{ |file| file.to_s }.uniq }
|
41
|
-
@sources = [ expand_src ]
|
42
|
-
# Add files and directories added to this path.
|
43
|
-
@actions = [] << proc do |file_map|
|
44
|
-
expand_src.call.each do |path|
|
45
|
-
unless excluded?(path)
|
46
|
-
if File.directory?(path)
|
47
|
-
in_directory path do |file, rel_path|
|
48
|
-
dest = "#{@path}#{rel_path}"
|
49
|
-
trace "Adding #{dest}"
|
50
|
-
file_map[dest] = file
|
51
|
-
end
|
52
|
-
else
|
53
|
-
trace "Adding #{@path}#{File.basename(path)}"
|
54
|
-
file_map["#{@path}#{File.basename(path)}"] = path
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# :call-seq:
|
62
|
-
# include(*files) => self
|
63
|
-
# include(*files, :path=>path) => self
|
64
|
-
# include(file, :as=>name) => self
|
65
|
-
# include(:from=>path) => self
|
66
|
-
# include(*files, :merge=>true) => self
|
67
|
-
def include(*args)
|
68
|
-
options = args.pop if Hash === args.last
|
69
|
-
files = args.flatten
|
70
|
-
|
71
|
-
if options.nil? || options.empty?
|
72
|
-
@includes.include *files.flatten
|
73
|
-
elsif options[:path]
|
74
|
-
sans_path = options.reject { |k,v| k == :path }
|
75
|
-
path(options[:path]).include *files + [sans_path]
|
76
|
-
elsif options[:as]
|
77
|
-
raise 'You can only use the :as option in combination with the :path option' unless options.size == 1
|
78
|
-
raise 'You can only use one file with the :as option' unless files.size == 1
|
79
|
-
include_as files.first.to_s, options[:as]
|
80
|
-
elsif options[:from]
|
81
|
-
raise 'You can only use the :from option in combination with the :path option' unless options.size == 1
|
82
|
-
raise 'You canont use the :from option with file names' unless files.empty?
|
83
|
-
[options[:from]].flatten.each { |path| include_as path.to_s, '.' }
|
84
|
-
elsif options[:merge]
|
85
|
-
raise 'You can only use the :merge option in combination with the :path option' unless options.size == 1
|
86
|
-
files.each { |file| merge file }
|
87
|
-
else
|
88
|
-
raise "Unrecognized option #{options.keys.join(', ')}"
|
89
|
-
end
|
90
|
-
self
|
91
|
-
end
|
92
|
-
alias :add :include
|
93
|
-
alias :<< :include
|
94
|
-
|
95
|
-
# :call-seq:
|
96
|
-
# exclude(*files) => self
|
97
|
-
def exclude(*files)
|
98
|
-
files = files.flatten.map(&:to_s)
|
99
|
-
@excludes |= files
|
100
|
-
@excludes |= files.reject { |f| f =~ /\*$/ }.map { |f| "#{f}/*" }
|
101
|
-
self
|
102
|
-
end
|
103
|
-
|
104
|
-
# :call-seq:
|
105
|
-
# merge(*files) => Merge
|
106
|
-
# merge(*files, :path=>name) => Merge
|
107
|
-
def merge(*args)
|
108
|
-
options = Hash === args.last ? args.pop : {}
|
109
|
-
files = args.flatten
|
110
|
-
rake_check_options options, :path
|
111
|
-
raise ArgumentError, "Expected at least one file to merge" if files.empty?
|
112
|
-
path = options[:path] || @path
|
113
|
-
expanders = files.collect do |file|
|
114
|
-
@sources << proc { file.to_s }
|
115
|
-
expander = ZipExpander.new(file)
|
116
|
-
@actions << proc { |file_map| expander.expand(file_map, path) }
|
117
|
-
expander
|
118
|
-
end
|
119
|
-
Merge.new(expanders)
|
120
|
-
end
|
121
|
-
|
122
|
-
# Returns a Path relative to this one.
|
123
|
-
def path(path)
|
124
|
-
return self if path.nil?
|
125
|
-
return root.path(path[1..-1]) if path[0] == ?/
|
126
|
-
root.path("#{@path}#{path}")
|
127
|
-
end
|
128
|
-
|
129
|
-
# Returns all the source files.
|
130
|
-
def sources() #:nodoc:
|
131
|
-
@sources.map{ |source| source.call }.flatten
|
132
|
-
end
|
133
|
-
|
134
|
-
def add_files(file_map) #:nodoc:
|
135
|
-
@actions.each { |action| action.call(file_map) }
|
136
|
-
end
|
137
|
-
|
138
|
-
def to_s()
|
139
|
-
@path
|
140
|
-
end
|
141
|
-
|
142
|
-
protected
|
143
|
-
|
144
|
-
def include_as(source, as)
|
145
|
-
@sources << proc { source }
|
146
|
-
@actions << proc do |file_map|
|
147
|
-
file = source.to_s
|
148
|
-
unless excluded?(file)
|
149
|
-
if File.directory?(file)
|
150
|
-
in_directory file do |file, rel_path|
|
151
|
-
path = rel_path.split('/')[1..-1]
|
152
|
-
path.unshift as unless as == '.'
|
153
|
-
dest = "#{@path}#{path.join('/')}"
|
154
|
-
trace "Adding #{dest}"
|
155
|
-
file_map[dest] = file
|
156
|
-
end
|
157
|
-
else
|
158
|
-
trace "Adding #{@path}#{as}"
|
159
|
-
file_map["#{@path}#{as}"] = file
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
def in_directory(dir)
|
166
|
-
prefix = Regexp.new('^' + Regexp.escape(File.dirname(dir) + File::SEPARATOR))
|
167
|
-
Util.recursive_with_dot_files(dir).reject { |file| excluded?(file) }.
|
168
|
-
each { |file| yield file, file.sub(prefix, '') }
|
169
|
-
end
|
170
|
-
|
171
|
-
def excluded?(file)
|
172
|
-
@excludes.any? { |exclude| File.fnmatch(exclude, file, File::FNM_PATHNAME) }
|
173
|
-
end
|
174
|
-
|
175
|
-
end
|
176
|
-
|
177
|
-
class Merge
|
178
|
-
def initialize(expanders)
|
179
|
-
@expanders = expanders
|
180
|
-
end
|
181
|
-
|
182
|
-
def include(*files)
|
183
|
-
@expanders.each { |expander| expander.include(*files) }
|
184
|
-
self
|
185
|
-
end
|
186
|
-
alias :<< :include
|
187
|
-
|
188
|
-
def exclude(*files)
|
189
|
-
@expanders.each { |expander| expander.exclude(*files) }
|
190
|
-
self
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
|
195
|
-
# Extend one Zip file into another.
|
196
|
-
class ZipExpander #:nodoc:
|
197
|
-
|
198
|
-
def initialize(zip_file)
|
199
|
-
@zip_file = zip_file.to_s
|
200
|
-
@includes = []
|
201
|
-
@excludes = []
|
202
|
-
end
|
203
|
-
|
204
|
-
def include(*files)
|
205
|
-
@includes |= files
|
206
|
-
self
|
207
|
-
end
|
208
|
-
alias :<< :include
|
209
|
-
|
210
|
-
def exclude(*files)
|
211
|
-
@excludes |= files
|
212
|
-
self
|
213
|
-
end
|
214
|
-
|
215
|
-
def expand(file_map, path)
|
216
|
-
@includes = ['**/*'] if @includes.empty?
|
217
|
-
Zip::ZipFile.open(@zip_file) do |source|
|
218
|
-
source.entries.reject { |entry| entry.directory? }.each do |entry|
|
219
|
-
if @includes.any? { |pattern| File.fnmatch(pattern, entry.name, File::FNM_PATHNAME) } &&
|
220
|
-
!@excludes.any? { |pattern| File.fnmatch(pattern, entry.name, File::FNM_PATHNAME) }
|
221
|
-
dest = path =~ /^\/?$/ ? entry.name : Util.relative_path(path + "/" + entry.name)
|
222
|
-
trace "Adding #{dest}"
|
223
|
-
file_map[dest] = lambda { |output| output.write source.read(entry) }
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
end
|
230
|
-
|
231
|
-
|
232
|
-
def initialize(*args) #:nodoc:
|
233
|
-
super
|
234
|
-
clean
|
235
|
-
|
236
|
-
# Make sure we're the last enhancements, so other enhancements can add content.
|
237
|
-
enhance do
|
238
|
-
@file_map = {}
|
239
|
-
enhance do
|
240
|
-
send 'create' if respond_to?(:create)
|
241
|
-
# We're here because the archive file does not exist, or one of the files is newer than the archive contents;
|
242
|
-
# we need to make sure the archive doesn't exist (e.g. opening an existing Zip will add instead of create).
|
243
|
-
# We also want to protect against partial updates.
|
244
|
-
rm name, :verbose=>false rescue nil
|
245
|
-
mkpath File.dirname(name), :verbose=>false
|
246
|
-
begin
|
247
|
-
@paths.each do |name, object|
|
248
|
-
@file_map[name] = nil unless name.empty?
|
249
|
-
object.add_files(@file_map)
|
250
|
-
end
|
251
|
-
create_from @file_map
|
252
|
-
rescue
|
253
|
-
rm name, :verbose=>false rescue nil
|
254
|
-
raise
|
255
|
-
end
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
# :call-seq:
|
261
|
-
# clean => self
|
262
|
-
#
|
263
|
-
# Removes all previously added content from this archive.
|
264
|
-
# Use this method if you want to remove default content from a package.
|
265
|
-
# For example, package(:jar) by default includes compiled classes and resources,
|
266
|
-
# using this method, you can create an empty jar and afterwards add the
|
267
|
-
# desired content to it.
|
268
|
-
#
|
269
|
-
# package(:jar).clean.include path_to('desired/content')
|
270
|
-
def clean
|
271
|
-
@paths = { '' => Path.new(self, '') }
|
272
|
-
@prepares = []
|
273
|
-
self
|
274
|
-
end
|
275
|
-
|
276
|
-
# :call-seq:
|
277
|
-
# include(*files) => self
|
278
|
-
# include(*files, :path=>path) => self
|
279
|
-
# include(file, :as=>name) => self
|
280
|
-
# include(:from=>path) => self
|
281
|
-
# include(*files, :merge=>true) => self
|
282
|
-
#
|
283
|
-
# Include files in this archive, or when called on a path, within that path. Returns self.
|
284
|
-
#
|
285
|
-
# The first form accepts a list of files, directories and glob patterns and adds them to the archive.
|
286
|
-
# For example, to include the file foo, directory bar (including all files in there) and all files under baz:
|
287
|
-
# zip(..).include('foo', 'bar', 'baz/*')
|
288
|
-
#
|
289
|
-
# The second form is similar but adds files/directories under the specified path. For example,
|
290
|
-
# to add foo as bar/foo:
|
291
|
-
# zip(..).include('foo', :path=>'bar')
|
292
|
-
# The :path option is the same as using the path method:
|
293
|
-
# zip(..).path('bar').include('foo')
|
294
|
-
# All other options can be used in combination with the :path option.
|
295
|
-
#
|
296
|
-
# The third form adds a file or directory under a different name. For example, to add the file foo under the
|
297
|
-
# name bar:
|
298
|
-
# zip(..).include('foo', :as=>'bar')
|
299
|
-
#
|
300
|
-
# The fourth form adds the contents of a directory using the directory as a prerequisite:
|
301
|
-
# zip(..).include(:from=>'foo')
|
302
|
-
# Unlike <code>include('foo')</code> it includes the contents of the directory, not the directory itself.
|
303
|
-
# Unlike <code>include('foo/*')</code>, it uses the directory timestamp for dependency management.
|
304
|
-
#
|
305
|
-
# The fifth form includes the contents of another archive by expanding it into this archive. For example:
|
306
|
-
# zip(..).include('foo.zip', :merge=>true).include('bar.zip')
|
307
|
-
# You can also use the method #merge.
|
308
|
-
def include(*files)
|
309
|
-
@paths[''].include *files
|
310
|
-
self
|
311
|
-
end
|
312
|
-
alias :add :include
|
313
|
-
alias :<< :include
|
314
|
-
|
315
|
-
# :call-seq:
|
316
|
-
# exclude(*files) => self
|
317
|
-
#
|
318
|
-
# Excludes files and returns self. Can be used in combination with include to prevent some files from being included.
|
319
|
-
def exclude(*files)
|
320
|
-
@paths[''].exclude *files
|
321
|
-
self
|
322
|
-
end
|
323
|
-
|
324
|
-
# :call-seq:
|
325
|
-
# merge(*files) => Merge
|
326
|
-
# merge(*files, :path=>name) => Merge
|
327
|
-
#
|
328
|
-
# Merges another archive into this one by including the individual files from the merged archive.
|
329
|
-
#
|
330
|
-
# Returns an object that supports two methods: include and exclude. You can use these methods to merge
|
331
|
-
# only specific files. For example:
|
332
|
-
# zip(..).merge('src.zip').include('module1/*')
|
333
|
-
def merge(*files)
|
334
|
-
@paths[''].merge *files
|
335
|
-
end
|
336
|
-
|
337
|
-
# :call-seq:
|
338
|
-
# path(name) => Path
|
339
|
-
#
|
340
|
-
# Returns a path object. Use the path object to include files under a path, for example, to include
|
341
|
-
# the file 'foo' as 'bar/foo':
|
342
|
-
# zip(..).path('bar').include('foo')
|
343
|
-
#
|
344
|
-
# Returns a Path object. The Path object implements all the same methods, like include, exclude, merge
|
345
|
-
# and so forth. It also implements path and root, so that:
|
346
|
-
# path('foo').path('bar') == path('foo/bar')
|
347
|
-
# path('foo').root == root
|
348
|
-
def path(name)
|
349
|
-
return @paths[''] if name.nil?
|
350
|
-
normalized = name.split('/').inject([]) do |path, part|
|
351
|
-
case part
|
352
|
-
when '.', nil, ''
|
353
|
-
path
|
354
|
-
when '..'
|
355
|
-
path[0...-1]
|
356
|
-
else
|
357
|
-
path << part
|
358
|
-
end
|
359
|
-
end.join('/')
|
360
|
-
@paths[normalized] ||= Path.new(self, normalized)
|
361
|
-
end
|
362
|
-
|
363
|
-
# :call-seq:
|
364
|
-
# root() => ArchiveTask
|
365
|
-
#
|
366
|
-
# Call this on an archive to return itself, and on a path to return the archive.
|
367
|
-
def root()
|
368
|
-
self
|
369
|
-
end
|
370
|
-
|
371
|
-
# :call-seq:
|
372
|
-
# with(options) => self
|
373
|
-
#
|
374
|
-
# Passes options to the task and returns self. Some tasks support additional options, for example,
|
375
|
-
# the WarTask supports options like :manifest, :libs and :classes.
|
376
|
-
#
|
377
|
-
# For example:
|
378
|
-
# package(:jar).with(:manifest=>'MANIFEST_MF')
|
379
|
-
def with(options)
|
380
|
-
options.each do |key, value|
|
381
|
-
begin
|
382
|
-
send "#{key}=", value
|
383
|
-
rescue NoMethodError
|
384
|
-
raise ArgumentError, "#{self.class.name} does not support the option #{key}"
|
385
|
-
end
|
386
|
-
end
|
387
|
-
self
|
388
|
-
end
|
389
|
-
|
390
|
-
def invoke_prerequisites(args, chain) #:nodoc:
|
391
|
-
@prepares.each { |prepare| prepare.call(self) }
|
392
|
-
@prepares.clear
|
393
|
-
@prerequisites |= @paths.collect { |name, path| path.sources }.flatten
|
394
|
-
super
|
395
|
-
end
|
396
|
-
|
397
|
-
def needed?() #:nodoc:
|
398
|
-
return true unless File.exist?(name)
|
399
|
-
# You can do something like:
|
400
|
-
# include('foo', :path=>'foo').exclude('foo/bar', path=>'foo').
|
401
|
-
# include('foo/bar', :path=>'foo/bar')
|
402
|
-
# This will play havoc if we handled all the prerequisites together
|
403
|
-
# under the task, so instead we handle them individually for each path.
|
404
|
-
#
|
405
|
-
# We need to check that any file we include is not newer than the
|
406
|
-
# contents of the Zip. The file itself but also the directory it's
|
407
|
-
# coming from, since some tasks touch the directory, e.g. when the
|
408
|
-
# content of target/classes is included into a WAR.
|
409
|
-
most_recent = @paths.collect { |name, path| path.sources }.flatten.
|
410
|
-
each { |src| File.directory?(src) ? Util.recursive_with_dot_files(src) | [src] : src }.flatten.
|
411
|
-
select { |file| File.exist?(file) }.collect { |file| File.stat(file).mtime }.max
|
412
|
-
File.stat(name).mtime < (most_recent || Rake::EARLY) || super
|
413
|
-
end
|
414
|
-
|
415
|
-
protected
|
416
|
-
|
417
|
-
# Adds a prepare block. These blocks are called early on for adding more content to
|
418
|
-
# the archive, before invoking prerequsities. Anything you add here will be invoked
|
419
|
-
# as a prerequisite and used to determine whether or not to generate this archive.
|
420
|
-
# In contrast, enhance blocks are evaluated after it was decided to create this archive.
|
421
|
-
def prepare(&block)
|
422
|
-
@prepares << block
|
423
|
-
end
|
424
|
-
|
425
|
-
def []=(key, value) #:nodoc:
|
426
|
-
raise ArgumentError, "This task does not support the option #{key}."
|
427
|
-
end
|
428
|
-
|
429
|
-
end
|
430
|
-
|
431
|
-
# The ZipTask creates a new Zip file. You can include any number of files and and directories,
|
432
|
-
# use exclusion patterns, and include files into specific directories.
|
433
|
-
#
|
434
|
-
# For example:
|
435
|
-
# zip('test.zip').tap do |task|
|
436
|
-
# task.include 'srcs'
|
437
|
-
# task.include 'README', 'LICENSE'
|
438
|
-
# end
|
439
|
-
#
|
440
|
-
# See Buildr#zip and ArchiveTask.
|
441
|
-
class ZipTask < ArchiveTask
|
442
|
-
|
443
|
-
# Compression leve for this Zip.
|
444
|
-
attr_accessor :compression_level
|
445
|
-
|
446
|
-
def initialize(*args) #:nodoc:
|
447
|
-
self.compression_level = Zlib::NO_COMPRESSION
|
448
|
-
super
|
449
|
-
end
|
450
|
-
|
451
|
-
private
|
452
|
-
|
453
|
-
def create_from(file_map)
|
454
|
-
Zip::ZipOutputStream.open name do |zip|
|
455
|
-
seen = {}
|
456
|
-
mkpath = lambda do |dir|
|
457
|
-
unless dir == '.' || seen[dir]
|
458
|
-
mkpath.call File.dirname(dir)
|
459
|
-
zip.put_next_entry(dir + '/', compression_level)
|
460
|
-
seen[dir] = true
|
461
|
-
end
|
462
|
-
end
|
22
|
+
module Zip #:nodoc:
|
463
23
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
mkpath.call path
|
471
|
-
else
|
472
|
-
zip.put_next_entry(path, compression_level)
|
473
|
-
File.open content.to_s, 'rb' do |is|
|
474
|
-
while data = is.read(4096)
|
475
|
-
zip << data
|
476
|
-
end
|
477
|
-
end
|
478
|
-
end
|
479
|
-
end
|
480
|
-
end
|
24
|
+
class ZipCentralDirectory #:nodoc:
|
25
|
+
# Patch to add entries in alphabetical order.
|
26
|
+
def write_to_stream(io)
|
27
|
+
offset = io.tell
|
28
|
+
@entrySet.sort { |a,b| a.name <=> b.name }.each { |entry| entry.write_c_dir_entry(io) }
|
29
|
+
write_e_o_c_d(io, offset)
|
481
30
|
end
|
482
|
-
|
483
|
-
end
|
484
|
-
|
485
|
-
|
486
|
-
# :call-seq:
|
487
|
-
# zip(file) => ZipTask
|
488
|
-
#
|
489
|
-
# The ZipTask creates a new Zip file. You can include any number of files and
|
490
|
-
# and directories, use exclusion patterns, and include files into specific
|
491
|
-
# directories.
|
492
|
-
#
|
493
|
-
# For example:
|
494
|
-
# zip('test.zip').tap do |task|
|
495
|
-
# task.include 'srcs'
|
496
|
-
# task.include 'README', 'LICENSE'
|
497
|
-
# end
|
498
|
-
def zip(file)
|
499
|
-
ZipTask.define_task(file)
|
500
31
|
end
|
501
32
|
|
502
33
|
|
503
|
-
|
504
|
-
# or exclude only specific files and directories, and also to map files from particular
|
505
|
-
# paths inside the zip file into the target directory. Once ready, call #extract.
|
506
|
-
#
|
507
|
-
# Usually it is more convenient to create a file task for extracting the zip file
|
508
|
-
# (see #unzip) and pass this object as a prerequisite to other tasks.
|
509
|
-
#
|
510
|
-
# See Buildr#unzip.
|
511
|
-
class Unzip
|
512
|
-
|
513
|
-
# The zip file to extract.
|
514
|
-
attr_accessor :zip_file
|
515
|
-
# The target directory to extract to.
|
516
|
-
attr_accessor :target
|
517
|
-
|
518
|
-
# Initialize with hash argument of the form target=>zip_file.
|
519
|
-
def initialize(args)
|
520
|
-
@target, arg_names, @zip_file = Buildr.application.resolve_args([args])
|
521
|
-
@paths = {}
|
522
|
-
end
|
34
|
+
class ZipEntry
|
523
35
|
|
524
36
|
# :call-seq:
|
525
|
-
#
|
526
|
-
#
|
527
|
-
# Extract the zip file into the target directory.
|
37
|
+
# exist() => boolean
|
528
38
|
#
|
529
|
-
#
|
530
|
-
|
531
|
-
|
532
|
-
# build unzip(dir=>zip_file)
|
533
|
-
# Or:
|
534
|
-
# unzip(dir=>zip_file).target.invoke
|
535
|
-
def extract()
|
536
|
-
# If no paths specified, then no include/exclude patterns
|
537
|
-
# specified. Nothing will happen unless we include all files.
|
538
|
-
if @paths.empty?
|
539
|
-
@paths[nil] = FromPath.new(self, nil)
|
540
|
-
end
|
541
|
-
|
542
|
-
# Otherwise, empty unzip creates target as a file when touching.
|
543
|
-
mkpath target.to_s, :verbose=>false
|
544
|
-
Zip::ZipFile.open(zip_file.to_s) do |zip|
|
545
|
-
entries = zip.collect
|
546
|
-
@paths.each do |path, patterns|
|
547
|
-
patterns.map(entries).each do |dest, entry|
|
548
|
-
next if entry.directory?
|
549
|
-
dest = File.expand_path(dest, target.to_s)
|
550
|
-
trace "Extracting #{dest}"
|
551
|
-
mkpath File.dirname(dest), :verbose=>false rescue nil
|
552
|
-
entry.extract(dest) { true }
|
553
|
-
end
|
554
|
-
end
|
555
|
-
end
|
556
|
-
# Let other tasks know we updated the target directory.
|
557
|
-
touch target.to_s, :verbose=>false
|
39
|
+
# Returns true if this entry exists.
|
40
|
+
def exist?()
|
41
|
+
Zip::ZipFile.open(zipfile) { |zip| zip.file.exist?(@name) }
|
558
42
|
end
|
559
43
|
|
560
44
|
# :call-seq:
|
561
|
-
#
|
562
|
-
# include(*files, :path=>name) => self
|
563
|
-
#
|
564
|
-
# Include all files that match the patterns and returns self.
|
45
|
+
# empty?() => boolean
|
565
46
|
#
|
566
|
-
#
|
567
|
-
|
568
|
-
|
569
|
-
def include(*files)
|
570
|
-
if Hash === files.last
|
571
|
-
from_path(files.pop[:path]).include *files
|
572
|
-
else
|
573
|
-
from_path(nil).include *files
|
574
|
-
end
|
575
|
-
self
|
47
|
+
# Returns true if this entry is empty.
|
48
|
+
def empty?()
|
49
|
+
Zip::ZipFile.open(zipfile) { |zip| zip.file.read(@name) }.empty?
|
576
50
|
end
|
577
|
-
alias :add :include
|
578
51
|
|
579
52
|
# :call-seq:
|
580
|
-
#
|
53
|
+
# contain(patterns*) => boolean
|
581
54
|
#
|
582
|
-
#
|
583
|
-
#
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
from_path(files.pop[:path]).exclude *files
|
589
|
-
else
|
590
|
-
from_path(nil).exclude *files
|
591
|
-
end
|
592
|
-
self
|
593
|
-
end
|
594
|
-
|
595
|
-
# :call-seq:
|
596
|
-
# from_path(name) => Path
|
597
|
-
#
|
598
|
-
# Allows you to unzip from a path. Returns an object you can use to
|
599
|
-
# specify which files to include/exclude relative to that path.
|
600
|
-
# Expands the file relative to that path.
|
601
|
-
#
|
602
|
-
# For example:
|
603
|
-
# unzip(Dir.pwd=>'test.jar').from_path('etc').include('LICENSE')
|
604
|
-
# will unzip etc/LICENSE into ./LICENSE.
|
605
|
-
#
|
606
|
-
# This is different from:
|
607
|
-
# unzip(Dir.pwd=>'test.jar').include('etc/LICENSE')
|
608
|
-
# which unzips etc/LICENSE into ./etc/LICENSE.
|
609
|
-
def from_path(name)
|
610
|
-
@paths[name] ||= FromPath.new(self, name)
|
611
|
-
end
|
612
|
-
alias :path :from_path
|
613
|
-
|
614
|
-
# :call-seq:
|
615
|
-
# root() => Unzip
|
616
|
-
#
|
617
|
-
# Returns the root path, essentially the Unzip object itself. In case you are wondering
|
618
|
-
# down paths and want to go back.
|
619
|
-
def root()
|
620
|
-
self
|
621
|
-
end
|
622
|
-
|
623
|
-
# Returns the path to the target directory.
|
624
|
-
def to_s()
|
625
|
-
target.to_s
|
626
|
-
end
|
627
|
-
|
628
|
-
class FromPath #:nodoc:
|
629
|
-
|
630
|
-
def initialize(unzip, path)
|
631
|
-
@unzip = unzip
|
632
|
-
if path
|
633
|
-
@path = path[-1] == ?/ ? path : path + '/'
|
634
|
-
else
|
635
|
-
@path = ''
|
636
|
-
end
|
637
|
-
end
|
638
|
-
|
639
|
-
# See UnzipTask#include
|
640
|
-
def include(*files) #:doc:
|
641
|
-
@include ||= []
|
642
|
-
@include |= files
|
643
|
-
self
|
644
|
-
end
|
645
|
-
|
646
|
-
# See UnzipTask#exclude
|
647
|
-
def exclude(*files) #:doc:
|
648
|
-
@exclude ||= []
|
649
|
-
@exclude |= files
|
650
|
-
self
|
651
|
-
end
|
652
|
-
|
653
|
-
def map(entries)
|
654
|
-
includes = @include || ['**/*']
|
655
|
-
excludes = @exclude || []
|
656
|
-
entries.inject({}) do |map, entry|
|
657
|
-
if entry.name =~ /^#{@path}/
|
658
|
-
short = entry.name.sub(@path, '')
|
659
|
-
if includes.any? { |pat| File.fnmatch(pat, short, File::FNM_PATHNAME) } &&
|
660
|
-
!excludes.any? { |pat| File.fnmatch(pat, short, File::FNM_PATHNAME) }
|
661
|
-
map[short] = entry
|
662
|
-
end
|
663
|
-
end
|
664
|
-
map
|
665
|
-
end
|
666
|
-
end
|
667
|
-
|
668
|
-
# Documented in Unzip.
|
669
|
-
def root()
|
670
|
-
@unzip
|
671
|
-
end
|
672
|
-
|
673
|
-
# The target directory to extract to.
|
674
|
-
def target()
|
675
|
-
@unzip.target
|
676
|
-
end
|
677
|
-
|
55
|
+
# Returns true if this ZIP file entry matches against all the arguments. An argument may be
|
56
|
+
# a string or regular expression.
|
57
|
+
def contain?(*patterns)
|
58
|
+
content = Zip::ZipFile.open(zipfile) { |zip| zip.file.read(@name) }
|
59
|
+
patterns.map { |pattern| Regexp === pattern ? pattern : Regexp.new(Regexp.escape(pattern.to_s)) }.
|
60
|
+
all? { |pattern| content =~ pattern }
|
678
61
|
end
|
679
62
|
|
680
63
|
end
|
681
|
-
|
682
|
-
# :call-seq:
|
683
|
-
# unzip(to_dir=>zip_file) => Zip
|
684
|
-
#
|
685
|
-
# Creates a task that will unzip a file into the target directory. The task name
|
686
|
-
# is the target directory, the prerequisite is the file to unzip.
|
687
|
-
#
|
688
|
-
# This method creates a file task to expand the zip file. It returns an Unzip object
|
689
|
-
# that specifies how the file will be extracted. You can include or exclude specific
|
690
|
-
# files from within the zip, and map to different paths.
|
691
|
-
#
|
692
|
-
# The Unzip object's to_s method return the path to the target directory, so you can
|
693
|
-
# use it as a prerequisite. By keeping the Unzip object separate from the file task,
|
694
|
-
# you overlay additional work on top of the file task.
|
695
|
-
#
|
696
|
-
# For example:
|
697
|
-
# unzip('all'=>'test.zip')
|
698
|
-
# unzip('src'=>'test.zip').include('README', 'LICENSE')
|
699
|
-
# unzip('libs'=>'test.zip').from_path('libs')
|
700
|
-
def unzip(args)
|
701
|
-
target, arg_names, zip_file = Buildr.application.resolve_args([args])
|
702
|
-
task = file(File.expand_path(target.to_s)=>zip_file)
|
703
|
-
Unzip.new(task=>zip_file).tap do |setup|
|
704
|
-
task.enhance { setup.extract }
|
705
|
-
end
|
706
|
-
end
|
707
|
-
|
708
64
|
end
|
709
|
-
|
710
|
-
|
711
|
-
module Zip #:nodoc:
|
712
|
-
|
713
|
-
class ZipCentralDirectory #:nodoc:
|
714
|
-
# Patch to add entries in alphabetical order.
|
715
|
-
def write_to_stream(io)
|
716
|
-
offset = io.tell
|
717
|
-
@entrySet.sort { |a,b| a.name <=> b.name }.each { |entry| entry.write_c_dir_entry(io) }
|
718
|
-
write_e_o_c_d(io, offset)
|
719
|
-
end
|
720
|
-
end
|
721
|
-
|
722
|
-
end
|