buildr 1.3.0-java

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.
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