lib/tasks/zip.rb in buildr-0.15.0 vs lib/tasks/zip.rb in buildr-0.16.0

- old
+ new

@@ -7,15 +7,140 @@ # The ZipTask creates a new ZIP file. You can include any number of files and # and directories, use exclusion patterns, and include files into specific # directories. class ZipTask < Rake::FileTask + # :nodoc: + module IncludeFiles + + # Include the specified files or directories. + def include(*files) + if Hash === files.last + options = files.pop + else + options = {} + end + + if options[:path] + path(options[:path]).include *files +[ options.reject { |k,v| k == :path } ] + elsif options[:as] + raise "You can only use the :as option in combination with the :path option" unless options.keys.size == 1 + raise "You can only use one file with the :as option" unless files.size == 1 + include_as(files.first, options[:as]) + elsif options[:merge] + raise "You can only use the :merge option in combination with the :path option" unless options.keys.size == 1 + files.each { |file| merge file } + elsif options.keys.empty? + (@files ||= FileList[]).include *files + else + raise "Unrecognized option #{options.keys.join(", ")}" + end + self + end + + # Exclude the specified file or directories. + def exclude(*files) + (@files ||= FileList[]).exclude *files + self + end + alias :add :include + + def merge(*files) + if Hash === files.last + options = files.pop + else + options = {} + end + + if options[:path] + path(options[:path]).merge *files +[ options.reject { |k,v| k == :path } ] + elsif options.keys.empty? + files.collect do |file| + @expand_sources << proc { artifacts(file).map(&:to_s) } + expander = ZipExpander.new(file) + @add_files << proc { |zip| expander.expand(zip, @path) } + expander + end.first + else + raise "Unrecognized option #{options.keys.join(", ")}" + end + end + + protected + + def setup_path(path = nil) + @path = "#{path}/" if path + expand_src = proc { artifacts(*@files || []).map(&:to_s) } + @expand_sources = [ expand_src ] + @add_files = [] << proc do |zip| + expand_src.call.each do |file| + if File.directory?(file) + in_directory(file, @files) do |file, rel_path| + puts "Adding #{@path}#{rel_path}" if Rake.application.options.trace + zip.add "#{@path}#{rel_path}", file + end + else + puts "Adding #{@path}#{File.basename(file)}" if Rake.application.options.trace + zip.add "#{@path}#{File.basename(file)}", file + end + end + end + end + + def include_as(source, as) + @expand_sources << proc { artifacts(source).map(&:to_s) } + @add_files << proc do |zip| + file = artifacts(source).first.to_s + if File.directory?(file) + in_directory(file) do |file, rel_path| + puts "Adding #{@path}#{as}/#{rel_path}" if Rake.application.options.trace + zip.add file, "#{@path}#{as}/#{rel_path}" + end + else + puts "Adding #{@path}#{as}" if Rake.application.options.trace + zip.add "#{@path}#{as}", file + end + end + end + + def in_directory(dir, excludes = nil) + prefix = Regexp.new("^" + Regexp.escape(File.dirname(dir) + File::SEPARATOR)) + Dir[File.join(dir, "**", "*")]. + reject { |file| File.directory?(file) || (excludes && excludes.exclude?(file)) }. + each { |file| yield file, file.sub(prefix, "") } + end + + def expand_sources() + @expand_sources.map(&:call).flatten + end + + def add_file(zip) + @add_files.each { |action| action.call zip } + end + + end + + + # Which files go where. + class Path + + include IncludeFiles + + def initialize(path) + setup_path path + end + + end + + include IncludeFiles + def initialize(*args) super - @paths = {} + @paths = { nil=>self } + setup_path enhance do |task| - verbose { puts "Creating #{task.name}" } + puts "Creating #{task.name}" if verbose # We're here because the Zip file does not exist, or one of the files is # newer than the Zip contents; in the later case, opening the Zip file # will add to its contents instead of replacing it, so we want the Zip # gone before we change it. We also don't want to see any partial updates. rm task.name, :verbose=>false rescue nil @@ -27,42 +152,54 @@ raise end end end - # Include the specified files and directories. Returns self. - # - # You can specify a path for inclusion by passing :path=> as the - # last argument. For example: - # task.include "src" # Include the src directory - # task.include foobar, :path=>"libs" # Include under /libs/ - def include(*files) - if Hash === files.last - path = files.pop[:path] + + class ZipExpander + + def initialize(zip_file) + @zip_file = zip_file end - (@paths[path] ||= FileList[]).include *files - self - end - alias :add :include - # Excludes the specified files and directories. Returns self. Use this - # in combination with include; make sure to use the same path. - def exclude(*files) - if Hash === files.last - path = files.pop[:path] + def include(*files) + (@includes ||= []) + @includes |= files + self end - (@paths[path] ||= FileList[]).exclude *files - self + + def exclude(*files) + (@excludes ||= []) + @excludes |= files + self + end + + def expand(zip, path) + @includes ||= ["*"] + @excludes ||= [] + Zip::ZipFile.open(artifacts(@zip_file).map(&:to_s).first) do |source| + source.entries.reject { |entry| entry.directory? }.each do |entry| + if @includes.any? { |pattern| File.fnmatch(pattern, entry.name) } && + !@excludes.any? { |pattern| File.fnmatch(pattern, entry.name) } + puts "Adding #{path}#{entry.name}" if Rake.application.options.trace + zip.get_output_stream("#{path}#{entry.name}") { |output| output.write source.read(entry) } + # TODO: read and write file + end + end + end + end + end + # Returns a path to which you can include/exclude files. # # zip(..).include("foo", :path=>"bar") # is equivalen to: # zip(..).path("bar").include("foo") def path(path) - @paths[path] ||= FileList[] + path.blank? ? @paths[nil] : (@paths[path] ||= Path.new(path)) end # Pass options to the task. Returns self. def with(options) options.each do |key, value| @@ -75,38 +212,35 @@ fail "#{self.class} does not support the attribute #{key}" end def invoke_prerequisites() super - @paths.each { |p, files| artifacts(*files).each { |f| file(f).invoke } } + @paths.collect { |name, path| path.expand_sources }.flatten.each { |src| file(src).invoke } end def needed?() + return true unless File.exist?(name) # You can do something like: # include("foo", :path=>"foo").exclude("foo/bar", path=>"foo"). # include("foo/bar", :path=>"foo/bar") # This will play havoc if we handled all the prerequisites together # under the task, so instead we handle them individually for each path. - #@paths.collect{ |p, files| files }.flatten.each { |f| file(f).invoke } - return true unless File.exist?(name) + # # We need to check that any file we include is not newer than the # contents of the ZIP. The file itself but also the directory it's # coming from, since some tasks touch the directory, e.g. when the # content of target/classes is included into a WAR. - files = @paths.collect { |path, files| files.map(&:to_s) }.flatten - files = files.collect { |f| File.directory?(f) ? FileList[File.join(f, "**", "*")].collect | [f] : f }.flatten - most_recent = files.select { |f| File.exist?(f) }.collect { |f| File.stat(f).mtime }.max + most_recent = @paths.collect { |name, path| path.expand_sources }.flatten. + each { |src| File.directory?(src) ? FileList[File.join(src, "**", "*")] | [src] : src }.flatten. + select { |file| File.exist?(file) }.collect { |file| File.stat(file).mtime }.max File.stat(name).mtime < (most_recent || Rake::EARLY) end protected def create(zip) - zip_map.keys.sort.each do |path| - puts "Adding #{path}" if Rake.application.options.trace - zip.add path, zip_map[path] - end + @paths.each { |name, obj| obj.add_file zip } end def zip_map() unless @zip_map args = @paths.collect do |path, files| @@ -242,12 +376,13 @@ def from_path(path) @paths[path] ||= FromPath.new(path) end def needed?() - return true unless target && File.exist?(target) - return true if prerequisites.any? { |prereq| File.stat(prereq).mtime > File.stat(target).mtime } - false + #return true unless target && File.exist?(target) + #return true if prerequisites.any? { |prereq| File.stat(prereq).mtime > File.stat(target).mtime } + #false + true end # :nodoc: class FromPath