assaf-buildr 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. data/CHANGELOG +887 -0
  2. data/DISCLAIMER +7 -0
  3. data/LICENSE +176 -0
  4. data/NOTICE +26 -0
  5. data/README.rdoc +146 -0
  6. data/Rakefile +62 -0
  7. data/_buildr +38 -0
  8. data/addon/buildr/antlr.rb +65 -0
  9. data/addon/buildr/cobertura.rb +236 -0
  10. data/addon/buildr/emma.rb +238 -0
  11. data/addon/buildr/hibernate.rb +142 -0
  12. data/addon/buildr/javacc.rb +85 -0
  13. data/addon/buildr/jdepend.rb +60 -0
  14. data/addon/buildr/jetty.rb +248 -0
  15. data/addon/buildr/jibx.rb +86 -0
  16. data/addon/buildr/nailgun.rb +817 -0
  17. data/addon/buildr/openjpa.rb +90 -0
  18. data/addon/buildr/org/apache/buildr/BuildrNail$Main.class +0 -0
  19. data/addon/buildr/org/apache/buildr/BuildrNail.class +0 -0
  20. data/addon/buildr/org/apache/buildr/BuildrNail.java +41 -0
  21. data/addon/buildr/org/apache/buildr/JettyWrapper$1.class +0 -0
  22. data/addon/buildr/org/apache/buildr/JettyWrapper$BuildrHandler.class +0 -0
  23. data/addon/buildr/org/apache/buildr/JettyWrapper.class +0 -0
  24. data/addon/buildr/org/apache/buildr/JettyWrapper.java +144 -0
  25. data/addon/buildr/xmlbeans.rb +93 -0
  26. data/bin/buildr +28 -0
  27. data/buildr.buildfile +53 -0
  28. data/buildr.gemspec +58 -0
  29. data/doc/css/default.css +228 -0
  30. data/doc/css/print.css +100 -0
  31. data/doc/css/syntax.css +52 -0
  32. data/doc/images/apache-incubator-logo.png +0 -0
  33. data/doc/images/buildr-hires.png +0 -0
  34. data/doc/images/buildr.png +0 -0
  35. data/doc/images/favicon.png +0 -0
  36. data/doc/images/growl-icon.tiff +0 -0
  37. data/doc/images/note.png +0 -0
  38. data/doc/images/project-structure.png +0 -0
  39. data/doc/images/tip.png +0 -0
  40. data/doc/images/zbuildr.tif +0 -0
  41. data/doc/pages/artifacts.textile +207 -0
  42. data/doc/pages/building.textile +240 -0
  43. data/doc/pages/contributing.textile +208 -0
  44. data/doc/pages/download.textile +62 -0
  45. data/doc/pages/extending.textile +175 -0
  46. data/doc/pages/getting_started.textile +273 -0
  47. data/doc/pages/index.textile +42 -0
  48. data/doc/pages/languages.textile +407 -0
  49. data/doc/pages/mailing_lists.textile +17 -0
  50. data/doc/pages/more_stuff.textile +286 -0
  51. data/doc/pages/packaging.textile +427 -0
  52. data/doc/pages/projects.textile +274 -0
  53. data/doc/pages/recipes.textile +103 -0
  54. data/doc/pages/settings_profiles.textile +274 -0
  55. data/doc/pages/testing.textile +212 -0
  56. data/doc/pages/troubleshooting.textile +103 -0
  57. data/doc/pages/whats_new.textile +323 -0
  58. data/doc/print.haml +51 -0
  59. data/doc/print.toc.yaml +29 -0
  60. data/doc/scripts/buildr-git.rb +412 -0
  61. data/doc/scripts/install-jruby.sh +44 -0
  62. data/doc/scripts/install-linux.sh +64 -0
  63. data/doc/scripts/install-osx.sh +52 -0
  64. data/doc/site.haml +56 -0
  65. data/doc/site.toc.yaml +47 -0
  66. data/etc/KEYS +151 -0
  67. data/etc/git-svn-authors +16 -0
  68. data/lib/buildr.rb +35 -0
  69. data/lib/buildr/core.rb +27 -0
  70. data/lib/buildr/core/application.rb +489 -0
  71. data/lib/buildr/core/application_cli.rb +139 -0
  72. data/lib/buildr/core/build.rb +311 -0
  73. data/lib/buildr/core/checks.rb +382 -0
  74. data/lib/buildr/core/common.rb +154 -0
  75. data/lib/buildr/core/compile.rb +596 -0
  76. data/lib/buildr/core/environment.rb +120 -0
  77. data/lib/buildr/core/filter.rb +362 -0
  78. data/lib/buildr/core/generate.rb +195 -0
  79. data/lib/buildr/core/help.rb +118 -0
  80. data/lib/buildr/core/progressbar.rb +156 -0
  81. data/lib/buildr/core/project.rb +892 -0
  82. data/lib/buildr/core/test.rb +715 -0
  83. data/lib/buildr/core/transports.rb +558 -0
  84. data/lib/buildr/core/util.rb +289 -0
  85. data/lib/buildr/groovy.rb +18 -0
  86. data/lib/buildr/groovy/bdd.rb +105 -0
  87. data/lib/buildr/groovy/compiler.rb +138 -0
  88. data/lib/buildr/ide.rb +19 -0
  89. data/lib/buildr/ide/eclipse.rb +212 -0
  90. data/lib/buildr/ide/idea.ipr.template +300 -0
  91. data/lib/buildr/ide/idea.rb +189 -0
  92. data/lib/buildr/ide/idea7x.ipr.template +290 -0
  93. data/lib/buildr/ide/idea7x.rb +210 -0
  94. data/lib/buildr/java.rb +23 -0
  95. data/lib/buildr/java/ant.rb +92 -0
  96. data/lib/buildr/java/bdd.rb +449 -0
  97. data/lib/buildr/java/commands.rb +211 -0
  98. data/lib/buildr/java/compiler.rb +348 -0
  99. data/lib/buildr/java/deprecated.rb +141 -0
  100. data/lib/buildr/java/jruby.rb +117 -0
  101. data/lib/buildr/java/jtestr_runner.rb.erb +116 -0
  102. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.class +0 -0
  103. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.java +119 -0
  104. data/lib/buildr/java/packaging.rb +713 -0
  105. data/lib/buildr/java/pom.rb +178 -0
  106. data/lib/buildr/java/rjb.rb +155 -0
  107. data/lib/buildr/java/test_result.rb +308 -0
  108. data/lib/buildr/java/tests.rb +324 -0
  109. data/lib/buildr/java/version_requirement.rb +172 -0
  110. data/lib/buildr/packaging.rb +21 -0
  111. data/lib/buildr/packaging/artifact.rb +730 -0
  112. data/lib/buildr/packaging/artifact_namespace.rb +972 -0
  113. data/lib/buildr/packaging/artifact_search.rb +140 -0
  114. data/lib/buildr/packaging/gems.rb +102 -0
  115. data/lib/buildr/packaging/package.rb +233 -0
  116. data/lib/buildr/packaging/tar.rb +104 -0
  117. data/lib/buildr/packaging/zip.rb +722 -0
  118. data/lib/buildr/resources/buildr.icns +0 -0
  119. data/lib/buildr/scala.rb +19 -0
  120. data/lib/buildr/scala/compiler.rb +109 -0
  121. data/lib/buildr/scala/tests.rb +203 -0
  122. data/rakelib/apache.rake +191 -0
  123. data/rakelib/changelog.rake +57 -0
  124. data/rakelib/doc.rake +103 -0
  125. data/rakelib/package.rake +73 -0
  126. data/rakelib/release.rake +65 -0
  127. data/rakelib/rspec.rake +83 -0
  128. data/rakelib/rubyforge.rake +53 -0
  129. data/rakelib/scm.rake +49 -0
  130. data/rakelib/setup.rake +86 -0
  131. data/rakelib/stage.rake +48 -0
  132. data/spec/addon/cobertura_spec.rb +77 -0
  133. data/spec/addon/emma_spec.rb +120 -0
  134. data/spec/addon/test_coverage_spec.rb +255 -0
  135. data/spec/core/application_spec.rb +412 -0
  136. data/spec/core/artifact_namespace_spec.rb +646 -0
  137. data/spec/core/build_spec.rb +415 -0
  138. data/spec/core/checks_spec.rb +537 -0
  139. data/spec/core/common_spec.rb +664 -0
  140. data/spec/core/compile_spec.rb +566 -0
  141. data/spec/core/generate_spec.rb +33 -0
  142. data/spec/core/project_spec.rb +754 -0
  143. data/spec/core/test_spec.rb +1091 -0
  144. data/spec/core/transport_spec.rb +500 -0
  145. data/spec/groovy/bdd_spec.rb +80 -0
  146. data/spec/groovy/compiler_spec.rb +239 -0
  147. data/spec/ide/eclipse_spec.rb +243 -0
  148. data/spec/java/ant.rb +28 -0
  149. data/spec/java/bdd_spec.rb +358 -0
  150. data/spec/java/compiler_spec.rb +446 -0
  151. data/spec/java/java_spec.rb +88 -0
  152. data/spec/java/packaging_spec.rb +1103 -0
  153. data/spec/java/tests_spec.rb +466 -0
  154. data/spec/packaging/archive_spec.rb +503 -0
  155. data/spec/packaging/artifact_spec.rb +754 -0
  156. data/spec/packaging/packaging_helper.rb +63 -0
  157. data/spec/packaging/packaging_spec.rb +589 -0
  158. data/spec/sandbox.rb +137 -0
  159. data/spec/scala/compiler_spec.rb +228 -0
  160. data/spec/scala/tests_spec.rb +215 -0
  161. data/spec/spec_helpers.rb +304 -0
  162. data/spec/version_requirement_spec.rb +123 -0
  163. metadata +369 -0
@@ -0,0 +1,104 @@
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/zip'
18
+ require 'archive/tar/minitar'
19
+
20
+
21
+ module Buildr
22
+
23
+ # The TarTask creates a new Tar file. You can include any number of files and and directories,
24
+ # use exclusion patterns, and include files into specific directories.
25
+ #
26
+ # To create a GZipped Tar, either set the gzip option to true, or use the .tgz or .gz suffix.
27
+ #
28
+ # For example:
29
+ # tar("test.tgz").tap do |task|
30
+ # task.include "srcs"
31
+ # task.include "README", "LICENSE"
32
+ # end
33
+ #
34
+ # See Buildr#tar and ArchiveTask.
35
+ class TarTask < ArchiveTask
36
+
37
+ # To create a GZipped Tar, either set this option to true, or use the .tgz/.gz suffix.
38
+ attr_accessor :gzip
39
+ # Permission mode for files contained in the Tar. Defaults to 0755.
40
+ attr_accessor :mode
41
+
42
+ def initialize(*args, &block) #:nodoc:
43
+ super
44
+ self.gzip = name =~ /\.[t?]gz$/
45
+ self.mode = '0755'
46
+ end
47
+
48
+ private
49
+
50
+ def create_from(file_map)
51
+ if gzip
52
+ StringIO.new.tap do |io|
53
+ create_tar io, file_map
54
+ io.seek 0
55
+ Zlib::GzipWriter.open(name) { |gzip| gzip.write io.read }
56
+ end
57
+ else
58
+ File.open(name, 'wb') { |file| create_tar file, file_map }
59
+ end
60
+ end
61
+
62
+ def create_tar(out, file_map)
63
+ Archive::Tar::Minitar::Writer.open(out) do |tar|
64
+ options = { :mode=>mode || '0755', :mtime=>Time.now }
65
+
66
+ file_map.each do |path, content|
67
+ if content.respond_to?(:call)
68
+ tar.add_file(path, options) { |os, opts| content.call os }
69
+ elsif content.nil? || File.directory?(content.to_s)
70
+ else
71
+ File.open content.to_s, 'rb' do |is|
72
+ tar.add_file path, options.merge(:mode=>is.stat.mode, :mtime=>is.stat.mtime, :uid=>is.stat.uid, :gid=>is.stat.gid) do |os, opts|
73
+ while data = is.read(4096)
74
+ os.write(data)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+
87
+
88
+ # :call-seq:
89
+ # tar(file) => TarTask
90
+ #
91
+ # The TarTask creates a new Tar file. You can include any number of files and
92
+ # and directories, use exclusion patterns, and include files into specific
93
+ # directories.
94
+ #
95
+ # To create a GZipped Tar, either set the gzip option to true, or use the .tgz or .gz suffix.
96
+ #
97
+ # For example:
98
+ # tar("test.tgz").tap do |tgz|
99
+ # tgz.include "srcs"
100
+ # tgz.include "README", "LICENSE"
101
+ # end
102
+ def tar(file)
103
+ TarTask.define_task(file)
104
+ end
@@ -0,0 +1,722 @@
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
+ $LOADED_FEATURES.unshift 'ftools' if RUBY_VERSION >= '1.9.0'
18
+ require 'zip/zip'
19
+ require 'zip/zipfilesystem'
20
+
21
+
22
+ module Buildr
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
463
+
464
+ file_map.each do |path, content|
465
+ mkpath.call File.dirname(path)
466
+ if content.respond_to?(:call)
467
+ zip.put_next_entry(path, compression_level)
468
+ content.call zip
469
+ elsif content.nil? || File.directory?(content.to_s)
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
481
+ 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
+ end
501
+
502
+
503
+ # An object for unzipping a file into a target directory. You can tell it to include
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
523
+
524
+ # :call-seq:
525
+ # extract()
526
+ #
527
+ # Extract the zip file into the target directory.
528
+ #
529
+ # You can call this method directly. However, if you are using the #unzip method,
530
+ # it creates a file task for the target directory: use that task instead as a
531
+ # prerequisite. For example:
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
558
+ end
559
+
560
+ # :call-seq:
561
+ # include(*files) => self
562
+ # include(*files, :path=>name) => self
563
+ #
564
+ # Include all files that match the patterns and returns self.
565
+ #
566
+ # Use include if you only want to unzip some of the files, by specifying
567
+ # them instead of using exclusion. You can use #include in combination
568
+ # with #exclude.
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
576
+ end
577
+ alias :add :include
578
+
579
+ # :call-seq:
580
+ # exclude(*files) => self
581
+ #
582
+ # Exclude all files that match the patterns and return self.
583
+ #
584
+ # Use exclude to unzip all files except those that match the pattern.
585
+ # You can use #exclude in combination with #include.
586
+ def exclude(*files)
587
+ if Hash === files.last
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
+
678
+ end
679
+
680
+ 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
+ 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