buildr 1.3.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/CHANGELOG +780 -0
  2. data/DISCLAIMER +7 -0
  3. data/KEYS +151 -0
  4. data/LICENSE +176 -0
  5. data/NOTICE +31 -0
  6. data/README +173 -0
  7. data/Rakefile +63 -0
  8. data/addon/buildr/antlr.rb +65 -0
  9. data/addon/buildr/cobertura.rb +232 -0
  10. data/addon/buildr/hibernate.rb +142 -0
  11. data/addon/buildr/javacc.rb +85 -0
  12. data/addon/buildr/jdepend.rb +60 -0
  13. data/addon/buildr/jetty.rb +248 -0
  14. data/addon/buildr/nailgun.rb +892 -0
  15. data/addon/buildr/openjpa.rb +90 -0
  16. data/addon/buildr/org/apache/buildr/JettyWrapper$1.class +0 -0
  17. data/addon/buildr/org/apache/buildr/JettyWrapper$BuildrHandler.class +0 -0
  18. data/addon/buildr/org/apache/buildr/JettyWrapper.class +0 -0
  19. data/addon/buildr/org/apache/buildr/JettyWrapper.java +144 -0
  20. data/addon/buildr/xmlbeans.rb +93 -0
  21. data/bin/buildr +21 -0
  22. data/buildr.gemspec +50 -0
  23. data/doc/css/default.css +225 -0
  24. data/doc/css/print.css +95 -0
  25. data/doc/css/syntax.css +43 -0
  26. data/doc/images/apache-incubator-logo.png +0 -0
  27. data/doc/images/buildr-hires.png +0 -0
  28. data/doc/images/buildr.png +0 -0
  29. data/doc/images/note.png +0 -0
  30. data/doc/images/tip.png +0 -0
  31. data/doc/images/zbuildr.tif +0 -0
  32. data/doc/pages/artifacts.textile +317 -0
  33. data/doc/pages/building.textile +501 -0
  34. data/doc/pages/contributing.textile +178 -0
  35. data/doc/pages/download.textile +25 -0
  36. data/doc/pages/extending.textile +229 -0
  37. data/doc/pages/getting_started.textile +337 -0
  38. data/doc/pages/index.textile +63 -0
  39. data/doc/pages/mailing_lists.textile +17 -0
  40. data/doc/pages/more_stuff.textile +367 -0
  41. data/doc/pages/packaging.textile +592 -0
  42. data/doc/pages/projects.textile +449 -0
  43. data/doc/pages/recipes.textile +127 -0
  44. data/doc/pages/settings_profiles.textile +339 -0
  45. data/doc/pages/testing.textile +475 -0
  46. data/doc/pages/troubleshooting.textile +121 -0
  47. data/doc/pages/whats_new.textile +389 -0
  48. data/doc/print.haml +52 -0
  49. data/doc/print.toc.yaml +28 -0
  50. data/doc/scripts/buildr-git.rb +411 -0
  51. data/doc/scripts/install-jruby.sh +44 -0
  52. data/doc/scripts/install-linux.sh +64 -0
  53. data/doc/scripts/install-osx.sh +52 -0
  54. data/doc/site.haml +55 -0
  55. data/doc/site.toc.yaml +44 -0
  56. data/lib/buildr.rb +47 -0
  57. data/lib/buildr/core.rb +27 -0
  58. data/lib/buildr/core/application.rb +373 -0
  59. data/lib/buildr/core/application_cli.rb +134 -0
  60. data/lib/buildr/core/build.rb +262 -0
  61. data/lib/buildr/core/checks.rb +382 -0
  62. data/lib/buildr/core/common.rb +155 -0
  63. data/lib/buildr/core/compile.rb +594 -0
  64. data/lib/buildr/core/environment.rb +120 -0
  65. data/lib/buildr/core/filter.rb +258 -0
  66. data/lib/buildr/core/generate.rb +195 -0
  67. data/lib/buildr/core/help.rb +118 -0
  68. data/lib/buildr/core/progressbar.rb +156 -0
  69. data/lib/buildr/core/project.rb +890 -0
  70. data/lib/buildr/core/test.rb +690 -0
  71. data/lib/buildr/core/transports.rb +486 -0
  72. data/lib/buildr/core/util.rb +235 -0
  73. data/lib/buildr/ide.rb +19 -0
  74. data/lib/buildr/ide/eclipse.rb +181 -0
  75. data/lib/buildr/ide/idea.ipr.template +300 -0
  76. data/lib/buildr/ide/idea.rb +194 -0
  77. data/lib/buildr/ide/idea7x.ipr.template +290 -0
  78. data/lib/buildr/ide/idea7x.rb +210 -0
  79. data/lib/buildr/java.rb +26 -0
  80. data/lib/buildr/java/ant.rb +71 -0
  81. data/lib/buildr/java/bdd_frameworks.rb +267 -0
  82. data/lib/buildr/java/commands.rb +210 -0
  83. data/lib/buildr/java/compilers.rb +432 -0
  84. data/lib/buildr/java/deprecated.rb +141 -0
  85. data/lib/buildr/java/groovyc.rb +137 -0
  86. data/lib/buildr/java/jruby.rb +99 -0
  87. data/lib/buildr/java/org/apache/buildr/BuildrNail$Main.class +0 -0
  88. data/lib/buildr/java/org/apache/buildr/BuildrNail.class +0 -0
  89. data/lib/buildr/java/org/apache/buildr/BuildrNail.java +41 -0
  90. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.class +0 -0
  91. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.java +116 -0
  92. data/lib/buildr/java/packaging.rb +706 -0
  93. data/lib/buildr/java/pom.rb +178 -0
  94. data/lib/buildr/java/rjb.rb +142 -0
  95. data/lib/buildr/java/test_frameworks.rb +290 -0
  96. data/lib/buildr/java/version_requirement.rb +172 -0
  97. data/lib/buildr/packaging.rb +21 -0
  98. data/lib/buildr/packaging/artifact.rb +729 -0
  99. data/lib/buildr/packaging/artifact_namespace.rb +957 -0
  100. data/lib/buildr/packaging/artifact_search.rb +140 -0
  101. data/lib/buildr/packaging/gems.rb +102 -0
  102. data/lib/buildr/packaging/package.rb +233 -0
  103. data/lib/buildr/packaging/tar.rb +104 -0
  104. data/lib/buildr/packaging/zip.rb +719 -0
  105. data/rakelib/apache.rake +126 -0
  106. data/rakelib/changelog.rake +56 -0
  107. data/rakelib/doc.rake +103 -0
  108. data/rakelib/package.rake +44 -0
  109. data/rakelib/release.rake +53 -0
  110. data/rakelib/rspec.rake +81 -0
  111. data/rakelib/rubyforge.rake +45 -0
  112. data/rakelib/scm.rake +49 -0
  113. data/rakelib/setup.rake +59 -0
  114. data/rakelib/stage.rake +45 -0
  115. data/spec/application_spec.rb +316 -0
  116. data/spec/archive_spec.rb +494 -0
  117. data/spec/artifact_namespace_spec.rb +635 -0
  118. data/spec/artifact_spec.rb +738 -0
  119. data/spec/build_spec.rb +193 -0
  120. data/spec/checks_spec.rb +537 -0
  121. data/spec/common_spec.rb +579 -0
  122. data/spec/compile_spec.rb +561 -0
  123. data/spec/groovy_compilers_spec.rb +239 -0
  124. data/spec/java_bdd_frameworks_spec.rb +238 -0
  125. data/spec/java_compilers_spec.rb +446 -0
  126. data/spec/java_packaging_spec.rb +1042 -0
  127. data/spec/java_test_frameworks_spec.rb +414 -0
  128. data/spec/packaging_helper.rb +63 -0
  129. data/spec/packaging_spec.rb +589 -0
  130. data/spec/project_spec.rb +739 -0
  131. data/spec/sandbox.rb +116 -0
  132. data/spec/scala_compilers_spec.rb +239 -0
  133. data/spec/spec.opts +6 -0
  134. data/spec/spec_helpers.rb +283 -0
  135. data/spec/test_spec.rb +871 -0
  136. data/spec/transport_spec.rb +300 -0
  137. data/spec/version_requirement_spec.rb +115 -0
  138. metadata +324 -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,719 @@
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 'zip/zip'
18
+ require 'zip/zipfilesystem'
19
+
20
+
21
+ module Buildr
22
+
23
+ # Base class for ZipTask, TarTask and other archives.
24
+ class ArchiveTask < Rake::FileTask
25
+
26
+ # Which files go where. All the rules for including, excluding and merging files
27
+ # are handled by this object.
28
+ class Path #:nodoc:
29
+
30
+ # Returns the archive from this path.
31
+ attr_reader :root
32
+
33
+ def initialize(root, path)
34
+ @root = root
35
+ @path = path.empty? ? path : "#{path}/"
36
+ @includes = FileList[]
37
+ @excludes = []
38
+ # Expand source files added to this path.
39
+ expand_src = proc { @includes.map{ |file| file.to_s }.uniq }
40
+ @sources = [ expand_src ]
41
+ # Add files and directories added to this path.
42
+ @actions = [] << proc do |file_map|
43
+ expand_src.call.each do |path|
44
+ unless excluded?(path)
45
+ if File.directory?(path)
46
+ in_directory path do |file, rel_path|
47
+ dest = "#{@path}#{rel_path}"
48
+ puts "Adding #{dest}" if Buildr.application.options.trace
49
+ file_map[dest] = file
50
+ end
51
+ else
52
+ puts "Adding #{@path}#{File.basename(path)}" if Buildr.application.options.trace
53
+ file_map["#{@path}#{File.basename(path)}"] = path
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ # :call-seq:
61
+ # include(*files) => self
62
+ # include(*files, :path=>path) => self
63
+ # include(file, :as=>name) => self
64
+ # include(:from=>path) => self
65
+ # include(*files, :merge=>true) => self
66
+ def include(*args)
67
+ options = args.pop if Hash === args.last
68
+ files = args.flatten
69
+
70
+ if options.nil? || options.empty?
71
+ @includes.include *files.flatten
72
+ elsif options[:path]
73
+ sans_path = options.reject { |k,v| k == :path }
74
+ path(options[:path]).include *files + [sans_path]
75
+ elsif options[:as]
76
+ raise 'You can only use the :as option in combination with the :path option' unless options.size == 1
77
+ raise 'You can only use one file with the :as option' unless files.size == 1
78
+ include_as files.first.to_s, options[:as]
79
+ elsif options[:from]
80
+ raise 'You can only use the :from option in combination with the :path option' unless options.size == 1
81
+ raise 'You canont use the :from option with file names' unless files.empty?
82
+ [options[:from]].flatten.each { |path| include_as path.to_s, '.' }
83
+ elsif options[:merge]
84
+ raise 'You can only use the :merge option in combination with the :path option' unless options.size == 1
85
+ files.each { |file| merge file }
86
+ else
87
+ raise "Unrecognized option #{options.keys.join(', ')}"
88
+ end
89
+ self
90
+ end
91
+ alias :add :include
92
+ alias :<< :include
93
+
94
+ # :call-seq:
95
+ # exclude(*files) => self
96
+ def exclude(*files)
97
+ files = files.flatten.map(&:to_s)
98
+ @excludes |= files
99
+ @excludes |= files.reject { |f| f =~ /\*$/ }.map { |f| "#{f}/*" }
100
+ self
101
+ end
102
+
103
+ # :call-seq:
104
+ # merge(*files) => Merge
105
+ # merge(*files, :path=>name) => Merge
106
+ def merge(*args)
107
+ options = Hash === args.last ? args.pop : {}
108
+ files = args.flatten
109
+ rake_check_options options, :path
110
+ raise ArgumentError, "Expected at least one file to merge" if files.empty?
111
+ path = options[:path] || @path
112
+ expanders = files.collect do |file|
113
+ @sources << proc { file.to_s }
114
+ expander = ZipExpander.new(file)
115
+ @actions << proc { |file_map| expander.expand(file_map, path) }
116
+ expander
117
+ end
118
+ Merge.new(expanders)
119
+ end
120
+
121
+ # Returns a Path relative to this one.
122
+ def path(path)
123
+ return self if path.nil?
124
+ return root.path(path[1..-1]) if path[0] == ?/
125
+ root.path("#{@path}#{path}")
126
+ end
127
+
128
+ # Returns all the source files.
129
+ def sources() #:nodoc:
130
+ @sources.map{ |source| source.call }.flatten
131
+ end
132
+
133
+ def add_files(file_map) #:nodoc:
134
+ @actions.each { |action| action.call(file_map) }
135
+ end
136
+
137
+ def to_s()
138
+ @path
139
+ end
140
+
141
+ protected
142
+
143
+ def include_as(source, as)
144
+ @sources << proc { source }
145
+ @actions << proc do |file_map|
146
+ file = source.to_s
147
+ unless excluded?(file)
148
+ if File.directory?(file)
149
+ in_directory file do |file, rel_path|
150
+ path = rel_path.split('/')[1..-1]
151
+ path.unshift as unless as == '.'
152
+ dest = "#{@path}#{path.join('/')}"
153
+ puts "Adding #{dest}" if Buildr.application.options.trace
154
+ file_map[dest] = file
155
+ end
156
+ else
157
+ puts "Adding #{@path}#{as}" if Buildr.application.options.trace
158
+ file_map["#{@path}#{as}"] = file
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ def in_directory(dir)
165
+ prefix = Regexp.new('^' + Regexp.escape(File.dirname(dir) + File::SEPARATOR))
166
+ Util.recursive_with_dot_files(dir).reject { |file| excluded?(file) }.
167
+ each { |file| yield file, file.sub(prefix, '') }
168
+ end
169
+
170
+ def excluded?(file)
171
+ @excludes.any? { |exclude| File.fnmatch(exclude, file, File::FNM_PATHNAME) }
172
+ end
173
+
174
+ end
175
+
176
+ class Merge
177
+ def initialize(expanders)
178
+ @expanders = expanders
179
+ end
180
+
181
+ def include(*files)
182
+ @expanders.each { |expander| expander.include(*files) }
183
+ self
184
+ end
185
+ alias :<< :include
186
+
187
+ def exclude(*files)
188
+ @expanders.each { |expander| expander.exclude(*files) }
189
+ self
190
+ end
191
+ end
192
+
193
+
194
+ # Extend one Zip file into another.
195
+ class ZipExpander #:nodoc:
196
+
197
+ def initialize(zip_file)
198
+ @zip_file = zip_file.to_s
199
+ @includes = []
200
+ @excludes = []
201
+ end
202
+
203
+ def include(*files)
204
+ @includes |= files
205
+ self
206
+ end
207
+ alias :<< :include
208
+
209
+ def exclude(*files)
210
+ @excludes |= files
211
+ self
212
+ end
213
+
214
+ def expand(file_map, path)
215
+ @includes = ['**/*'] if @includes.empty?
216
+ Zip::ZipFile.open(@zip_file) do |source|
217
+ source.entries.reject { |entry| entry.directory? }.each do |entry|
218
+ if @includes.any? { |pattern| File.fnmatch(pattern, entry.name, File::FNM_PATHNAME) } &&
219
+ !@excludes.any? { |pattern| File.fnmatch(pattern, entry.name, File::FNM_PATHNAME) }
220
+ dest = path =~ /^\/?$/ ? entry.name : Util.relative_path(path + "/" + entry.name)
221
+ puts "Adding #{dest}" if Buildr.application.options.trace
222
+ file_map[dest] = lambda { |output| output.write source.read(entry) }
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ end
229
+
230
+
231
+ def initialize(*args) #:nodoc:
232
+ super
233
+ clean
234
+
235
+ # Make sure we're the last enhancements, so other enhancements can add content.
236
+ enhance do
237
+ @file_map = {}
238
+ enhance do
239
+ send 'create' if respond_to?(:create)
240
+ # We're here because the archive file does not exist, or one of the files is newer than the archive contents;
241
+ # we need to make sure the archive doesn't exist (e.g. opening an existing Zip will add instead of create).
242
+ # We also want to protect against partial updates.
243
+ rm name, :verbose=>false rescue nil
244
+ mkpath File.dirname(name), :verbose=>false
245
+ begin
246
+ @paths.each do |name, object|
247
+ @file_map[name] = nil unless name.empty?
248
+ object.add_files(@file_map)
249
+ end
250
+ create_from @file_map
251
+ rescue
252
+ rm name, :verbose=>false rescue nil
253
+ raise
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ # :call-seq:
260
+ # clean => self
261
+ #
262
+ # Removes all previously added content from this archive.
263
+ # Use this method if you want to remove default content from a package.
264
+ # For example, package(:jar) by default includes compiled classes and resources,
265
+ # using this method, you can create an empty jar and afterwards add the
266
+ # desired content to it.
267
+ #
268
+ # package(:jar).clean.include path_to('desired/content')
269
+ def clean
270
+ @paths = { '' => Path.new(self, '') }
271
+ @prepares = []
272
+ self
273
+ end
274
+
275
+ # :call-seq:
276
+ # include(*files) => self
277
+ # include(*files, :path=>path) => self
278
+ # include(file, :as=>name) => self
279
+ # include(:from=>path) => self
280
+ # include(*files, :merge=>true) => self
281
+ #
282
+ # Include files in this archive, or when called on a path, within that path. Returns self.
283
+ #
284
+ # The first form accepts a list of files, directories and glob patterns and adds them to the archive.
285
+ # For example, to include the file foo, directory bar (including all files in there) and all files under baz:
286
+ # zip(..).include('foo', 'bar', 'baz/*')
287
+ #
288
+ # The second form is similar but adds files/directories under the specified path. For example,
289
+ # to add foo as bar/foo:
290
+ # zip(..).include('foo', :path=>'bar')
291
+ # The :path option is the same as using the path method:
292
+ # zip(..).path('bar').include('foo')
293
+ # All other options can be used in combination with the :path option.
294
+ #
295
+ # The third form adds a file or directory under a different name. For example, to add the file foo under the
296
+ # name bar:
297
+ # zip(..).include('foo', :as=>'bar')
298
+ #
299
+ # The fourth form adds the contents of a directory using the directory as a prerequisite:
300
+ # zip(..).include(:from=>'foo')
301
+ # Unlike <code>include('foo')</code> it includes the contents of the directory, not the directory itself.
302
+ # Unlike <code>include('foo/*')</code>, it uses the directory timestamp for dependency management.
303
+ #
304
+ # The fifth form includes the contents of another archive by expanding it into this archive. For example:
305
+ # zip(..).include('foo.zip', :merge=>true).include('bar.zip')
306
+ # You can also use the method #merge.
307
+ def include(*files)
308
+ @paths[''].include *files
309
+ self
310
+ end
311
+ alias :add :include
312
+ alias :<< :include
313
+
314
+ # :call-seq:
315
+ # exclude(*files) => self
316
+ #
317
+ # Excludes files and returns self. Can be used in combination with include to prevent some files from being included.
318
+ def exclude(*files)
319
+ @paths[''].exclude *files
320
+ self
321
+ end
322
+
323
+ # :call-seq:
324
+ # merge(*files) => Merge
325
+ # merge(*files, :path=>name) => Merge
326
+ #
327
+ # Merges another archive into this one by including the individual files from the merged archive.
328
+ #
329
+ # Returns an object that supports two methods: include and exclude. You can use these methods to merge
330
+ # only specific files. For example:
331
+ # zip(..).merge('src.zip').include('module1/*')
332
+ def merge(*files)
333
+ @paths[''].merge *files
334
+ end
335
+
336
+ # :call-seq:
337
+ # path(name) => Path
338
+ #
339
+ # Returns a path object. Use the path object to include files under a path, for example, to include
340
+ # the file 'foo' as 'bar/foo':
341
+ # zip(..).path('bar').include('foo')
342
+ #
343
+ # Returns a Path object. The Path object implements all the same methods, like include, exclude, merge
344
+ # and so forth. It also implements path and root, so that:
345
+ # path('foo').path('bar') == path('foo/bar')
346
+ # path('foo').root == root
347
+ def path(name)
348
+ return @paths[''] if name.nil?
349
+ normalized = name.split('/').inject([]) do |path, part|
350
+ case part
351
+ when '.', nil, ''
352
+ path
353
+ when '..'
354
+ path[0...-1]
355
+ else
356
+ path << part
357
+ end
358
+ end.join('/')
359
+ @paths[normalized] ||= Path.new(self, normalized)
360
+ end
361
+
362
+ # :call-seq:
363
+ # root() => ArchiveTask
364
+ #
365
+ # Call this on an archive to return itself, and on a path to return the archive.
366
+ def root()
367
+ self
368
+ end
369
+
370
+ # :call-seq:
371
+ # with(options) => self
372
+ #
373
+ # Passes options to the task and returns self. Some tasks support additional options, for example,
374
+ # the WarTask supports options like :manifest, :libs and :classes.
375
+ #
376
+ # For example:
377
+ # package(:jar).with(:manifest=>'MANIFEST_MF')
378
+ def with(options)
379
+ options.each do |key, value|
380
+ begin
381
+ send "#{key}=", value
382
+ rescue NoMethodError
383
+ raise ArgumentError, "#{self.class.name} does not support the option #{key}"
384
+ end
385
+ end
386
+ self
387
+ end
388
+
389
+ def invoke_prerequisites(args, chain) #:nodoc:
390
+ @prepares.each { |prepare| prepare.call(self) }
391
+ @prepares.clear
392
+ @prerequisites |= @paths.collect { |name, path| path.sources }.flatten
393
+ super
394
+ end
395
+
396
+ def needed?() #:nodoc:
397
+ return true unless File.exist?(name)
398
+ # You can do something like:
399
+ # include('foo', :path=>'foo').exclude('foo/bar', path=>'foo').
400
+ # include('foo/bar', :path=>'foo/bar')
401
+ # This will play havoc if we handled all the prerequisites together
402
+ # under the task, so instead we handle them individually for each path.
403
+ #
404
+ # We need to check that any file we include is not newer than the
405
+ # contents of the Zip. The file itself but also the directory it's
406
+ # coming from, since some tasks touch the directory, e.g. when the
407
+ # content of target/classes is included into a WAR.
408
+ most_recent = @paths.collect { |name, path| path.sources }.flatten.
409
+ each { |src| File.directory?(src) ? Util.recursive_with_dot_files(src) | [src] : src }.flatten.
410
+ select { |file| File.exist?(file) }.collect { |file| File.stat(file).mtime }.max
411
+ File.stat(name).mtime < (most_recent || Rake::EARLY) || super
412
+ end
413
+
414
+ protected
415
+
416
+ # Adds a prepare block. These blocks are called early on for adding more content to
417
+ # the archive, before invoking prerequsities. Anything you add here will be invoked
418
+ # as a prerequisite and used to determine whether or not to generate this archive.
419
+ # In contrast, enhance blocks are evaluated after it was decided to create this archive.
420
+ def prepare(&block)
421
+ @prepares << block
422
+ end
423
+
424
+ def []=(key, value) #:nodoc:
425
+ raise ArgumentError, "This task does not support the option #{key}."
426
+ end
427
+
428
+ end
429
+
430
+ # The ZipTask creates a new Zip file. You can include any number of files and and directories,
431
+ # use exclusion patterns, and include files into specific directories.
432
+ #
433
+ # For example:
434
+ # zip('test.zip').tap do |task|
435
+ # task.include 'srcs'
436
+ # task.include 'README', 'LICENSE'
437
+ # end
438
+ #
439
+ # See Buildr#zip and ArchiveTask.
440
+ class ZipTask < ArchiveTask
441
+
442
+ # Compression leve for this Zip.
443
+ attr_accessor :compression_level
444
+
445
+ def initialize(*args) #:nodoc:
446
+ self.compression_level = Zlib::NO_COMPRESSION
447
+ super
448
+ end
449
+
450
+ private
451
+
452
+ def create_from(file_map)
453
+ Zip::ZipOutputStream.open name do |zip|
454
+ seen = {}
455
+ mkpath = lambda do |dir|
456
+ unless dir == '.' || seen[dir]
457
+ mkpath.call File.dirname(dir)
458
+ zip.put_next_entry(dir + '/', compression_level)
459
+ seen[dir] = true
460
+ end
461
+ end
462
+
463
+ file_map.each do |path, content|
464
+ mkpath.call File.dirname(path)
465
+ if content.respond_to?(:call)
466
+ zip.put_next_entry(path, compression_level)
467
+ content.call zip
468
+ elsif content.nil? || File.directory?(content.to_s)
469
+ mkpath.call path
470
+ else
471
+ zip.put_next_entry(path, compression_level)
472
+ File.open content.to_s, 'rb' do |is|
473
+ while data = is.read(4096)
474
+ zip << data
475
+ end
476
+ end
477
+ end
478
+ end
479
+ end
480
+ end
481
+
482
+ end
483
+
484
+
485
+ # :call-seq:
486
+ # zip(file) => ZipTask
487
+ #
488
+ # The ZipTask creates a new Zip file. You can include any number of files and
489
+ # and directories, use exclusion patterns, and include files into specific
490
+ # directories.
491
+ #
492
+ # For example:
493
+ # zip('test.zip').tap do |task|
494
+ # task.include 'srcs'
495
+ # task.include 'README', 'LICENSE'
496
+ # end
497
+ def zip(file)
498
+ ZipTask.define_task(file)
499
+ end
500
+
501
+
502
+ # An object for unzipping a file into a target directory. You can tell it to include
503
+ # or exclude only specific files and directories, and also to map files from particular
504
+ # paths inside the zip file into the target directory. Once ready, call #extract.
505
+ #
506
+ # Usually it is more convenient to create a file task for extracting the zip file
507
+ # (see #unzip) and pass this object as a prerequisite to other tasks.
508
+ #
509
+ # See Buildr#unzip.
510
+ class Unzip
511
+
512
+ # The zip file to extract.
513
+ attr_accessor :zip_file
514
+ # The target directory to extract to.
515
+ attr_accessor :target
516
+
517
+ # Initialize with hash argument of the form target=>zip_file.
518
+ def initialize(args)
519
+ @target, arg_names, @zip_file = Buildr.application.resolve_args([args])
520
+ @paths = {}
521
+ end
522
+
523
+ # :call-seq:
524
+ # extract()
525
+ #
526
+ # Extract the zip file into the target directory.
527
+ #
528
+ # You can call this method directly. However, if you are using the #unzip method,
529
+ # it creates a file task for the target directory: use that task instead as a
530
+ # prerequisite. For example:
531
+ # build unzip(dir=>zip_file)
532
+ # Or:
533
+ # unzip(dir=>zip_file).target.invoke
534
+ def extract()
535
+ # If no paths specified, then no include/exclude patterns
536
+ # specified. Nothing will happen unless we include all files.
537
+ if @paths.empty?
538
+ @paths[nil] = FromPath.new(self, nil)
539
+ end
540
+
541
+ # Otherwise, empty unzip creates target as a file when touching.
542
+ mkpath target.to_s, :verbose=>false
543
+ Zip::ZipFile.open(zip_file.to_s) do |zip|
544
+ entries = zip.collect
545
+ @paths.each do |path, patterns|
546
+ patterns.map(entries).each do |dest, entry|
547
+ next if entry.directory?
548
+ dest = File.expand_path(dest, target.to_s)
549
+ puts "Extracting #{dest}" if Buildr.application.options.trace
550
+ mkpath File.dirname(dest), :verbose=>false rescue nil
551
+ entry.extract(dest) { true }
552
+ end
553
+ end
554
+ end
555
+ # Let other tasks know we updated the target directory.
556
+ touch target.to_s, :verbose=>false
557
+ end
558
+
559
+ # :call-seq:
560
+ # include(*files) => self
561
+ # include(*files, :path=>name) => self
562
+ #
563
+ # Include all files that match the patterns and returns self.
564
+ #
565
+ # Use include if you only want to unzip some of the files, by specifying
566
+ # them instead of using exclusion. You can use #include in combination
567
+ # with #exclude.
568
+ def include(*files)
569
+ if Hash === files.last
570
+ from_path(files.pop[:path]).include *files
571
+ else
572
+ from_path(nil).include *files
573
+ end
574
+ self
575
+ end
576
+ alias :add :include
577
+
578
+ # :call-seq:
579
+ # exclude(*files) => self
580
+ #
581
+ # Exclude all files that match the patterns and return self.
582
+ #
583
+ # Use exclude to unzip all files except those that match the pattern.
584
+ # You can use #exclude in combination with #include.
585
+ def exclude(*files)
586
+ if Hash === files.last
587
+ from_path(files.pop[:path]).exclude *files
588
+ else
589
+ from_path(nil).exclude *files
590
+ end
591
+ self
592
+ end
593
+
594
+ # :call-seq:
595
+ # from_path(name) => Path
596
+ #
597
+ # Allows you to unzip from a path. Returns an object you can use to
598
+ # specify which files to include/exclude relative to that path.
599
+ # Expands the file relative to that path.
600
+ #
601
+ # For example:
602
+ # unzip(Dir.pwd=>'test.jar').from_path('etc').include('LICENSE')
603
+ # will unzip etc/LICENSE into ./LICENSE.
604
+ #
605
+ # This is different from:
606
+ # unzip(Dir.pwd=>'test.jar').include('etc/LICENSE')
607
+ # which unzips etc/LICENSE into ./etc/LICENSE.
608
+ def from_path(name)
609
+ @paths[name] ||= FromPath.new(self, name)
610
+ end
611
+ alias :path :from_path
612
+
613
+ # :call-seq:
614
+ # root() => Unzip
615
+ #
616
+ # Returns the root path, essentially the Unzip object itself. In case you are wondering
617
+ # down paths and want to go back.
618
+ def root()
619
+ self
620
+ end
621
+
622
+ # Returns the path to the target directory.
623
+ def to_s()
624
+ target.to_s
625
+ end
626
+
627
+ class FromPath #:nodoc:
628
+
629
+ def initialize(unzip, path)
630
+ @unzip = unzip
631
+ if path
632
+ @path = path[-1] == ?/ ? path : path + '/'
633
+ else
634
+ @path = ''
635
+ end
636
+ end
637
+
638
+ # See UnzipTask#include
639
+ def include(*files) #:doc:
640
+ @include ||= []
641
+ @include |= files
642
+ self
643
+ end
644
+
645
+ # See UnzipTask#exclude
646
+ def exclude(*files) #:doc:
647
+ @exclude ||= []
648
+ @exclude |= files
649
+ self
650
+ end
651
+
652
+ def map(entries)
653
+ includes = @include || ['**/*']
654
+ excludes = @exclude || []
655
+ entries.inject({}) do |map, entry|
656
+ short = entry.name.sub(@path, '')
657
+ if includes.any? { |pat| File.fnmatch(pat, short, File::FNM_PATHNAME) } &&
658
+ !excludes.any? { |pat| File.fnmatch(pat, short, File::FNM_PATHNAME) }
659
+ map[short] = entry
660
+ end
661
+ map
662
+ end
663
+ end
664
+
665
+ # Documented in Unzip.
666
+ def root()
667
+ @unzip
668
+ end
669
+
670
+ # The target directory to extract to.
671
+ def target()
672
+ @unzip.target
673
+ end
674
+
675
+ end
676
+
677
+ end
678
+
679
+ # :call-seq:
680
+ # unzip(to_dir=>zip_file) => Zip
681
+ #
682
+ # Creates a task that will unzip a file into the target directory. The task name
683
+ # is the target directory, the prerequisite is the file to unzip.
684
+ #
685
+ # This method creates a file task to expand the zip file. It returns an Unzip object
686
+ # that specifies how the file will be extracted. You can include or exclude specific
687
+ # files from within the zip, and map to different paths.
688
+ #
689
+ # The Unzip object's to_s method return the path to the target directory, so you can
690
+ # use it as a prerequisite. By keeping the Unzip object separate from the file task,
691
+ # you overlay additional work on top of the file task.
692
+ #
693
+ # For example:
694
+ # unzip('all'=>'test.zip')
695
+ # unzip('src'=>'test.zip').include('README', 'LICENSE')
696
+ # unzip('libs'=>'test.zip').from_path('libs')
697
+ def unzip(args)
698
+ target, arg_names, zip_file = Buildr.application.resolve_args([args])
699
+ task = file(File.expand_path(target.to_s)=>zip_file)
700
+ Unzip.new(task=>zip_file).tap do |setup|
701
+ task.enhance { setup.extract }
702
+ end
703
+ end
704
+
705
+ end
706
+
707
+
708
+ module Zip #:nodoc:
709
+
710
+ class ZipCentralDirectory #:nodoc:
711
+ # Patch to add entries in alphabetical order.
712
+ def write_to_stream(io)
713
+ offset = io.tell
714
+ @entrySet.sort { |a,b| a.name <=> b.name }.each { |entry| entry.write_c_dir_entry(io) }
715
+ write_e_o_c_d(io, offset)
716
+ end
717
+ end
718
+
719
+ end