| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with this |
| # work for additional information regarding copyright ownership. The ASF |
| # licenses this file to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| |
| autoload :Archive, 'archive/tar/minitar' |
| autoload :Zlib, 'zlib' |
| |
| module Buildr #:nodoc: |
| |
| # The TarTask creates a new Tar file. You can include any number of files and and directories, |
| # use exclusion patterns, and include files into specific directories. |
| # |
| # To create a GZipped Tar, either set the gzip option to true, or use the .tgz or .gz suffix. |
| # |
| # For example: |
| # tar("test.tgz").tap do |task| |
| # task.include "srcs" |
| # task.include "README", "LICENSE" |
| # end |
| # |
| # See Buildr#tar and ArchiveTask. |
| class TarTask < ArchiveTask |
| |
| # To create a GZipped Tar, either set this option to true, or use the .tgz/.gz suffix. |
| attr_accessor :gzip |
| # Permission mode for files contained in the Tar. Defaults to 0755. |
| attr_accessor :mode |
| |
| def initialize(*args, &block) #:nodoc: |
| super |
| self.gzip = name =~ /\.t?gz$/ |
| self.mode = '0755' |
| end |
| |
| # :call-seq: |
| # entry(name) => Entry |
| # |
| # Returns a Tar file entry. You can use this to check if the entry exists and its contents, |
| # for example: |
| # package(:tar).entry("src/LICENSE").should contain(/Apache Software License/) |
| def entry(entry_name) |
| Buildr::TarEntry.new(self, entry_name) |
| end |
| |
| def entries() #:nodoc: |
| tar_entries = nil |
| with_uncompressed_tar { |tar| tar_entries = tar.entries } |
| tar_entries |
| end |
| |
| # :call-seq: |
| # with_uncompressed_tar { |tar_entries| ... } |
| # |
| # Yields an Archive::Tar::Minitar::Input object to the provided block. |
| # Opening, closing and Gzip-decompressing is automatically taken care of. |
| def with_uncompressed_tar &block |
| if gzip |
| Zlib::GzipReader.open(name) { |tar| Archive::Tar::Minitar.open(tar, &block) } |
| else |
| Archive::Tar::Minitar.open(name, &block) |
| end |
| end |
| |
| private |
| |
| def create_from(file_map, transform_map) |
| if gzip |
| StringIO.new.tap do |io| |
| create_tar io, file_map, transform_map |
| io.seek 0 |
| Zlib::GzipWriter.open(name) { |gzip| gzip.write io.read } |
| end |
| else |
| File.open(name, 'wb') { |file| create_tar file, file_map, transform_map } |
| end |
| end |
| |
| def create_tar(out, file_map, transform_map) |
| Archive::Tar::Minitar::Writer.open(out) do |tar| |
| options = { :mode=>mode || '0755', :mtime=>Time.now } |
| |
| file_map.each do |path, contents| |
| to_transform = [] |
| transform = transform_map.key?(path) |
| if contents.nil? |
| elsif File.directory?(contents.to_s) |
| stat = File.stat(contents.to_s) |
| tar.mkdir(path, options.merge(:mode=>stat.mode, :mtime=>stat.mtime, :uid=>stat.uid, :gid=>stat.gid)) |
| else |
| contents = [contents].flatten |
| |
| combined_options = options |
| if File.exists?(contents.first.to_s) |
| stat = File.stat(contents.first.to_s) |
| combined_options = options.merge(:mode=> stat.mode, :mtime=> stat.mtime, :uid=>stat.uid, :gid=> stat.gid) |
| elsif contents.first.respond_to?(:mode) |
| combined_options = combined_options.merge(:mode => contents.first.mode) |
| end |
| |
| |
| tar.add_file path, combined_options do |os, opts| |
| [contents].flatten.each do |content| |
| if content.respond_to?(:call) |
| if transform |
| output = StringIO.new |
| content.call output |
| to_transform << output.string |
| else |
| content.call os |
| end |
| else |
| File.open content.to_s, 'rb' do |is| |
| if transform |
| output = StringIO.new |
| while data = is.read(4096) |
| output << data |
| end |
| to_transform << output.string |
| else |
| while data = is.read(4096) |
| os.write(data) |
| end |
| end |
| end |
| end |
| end |
| if transform_map.key?(path) |
| os.write(transform_map[path].call(to_transform)) |
| end |
| end |
| end |
| |
| end |
| end |
| end |
| |
| end |
| |
| |
| class TarEntry #:nodoc: |
| |
| def initialize(tar_task, entry_name) |
| @tar_task = tar_task |
| @entry_name = entry_name |
| end |
| |
| # :call-seq: |
| # contain?(*patterns) => boolean |
| # |
| # Returns true if this Tar file entry matches against all the arguments. An argument may be |
| # a string or regular expression. |
| def contain?(*patterns) |
| content = read_content_from_tar |
| patterns.map { |pattern| Regexp === pattern ? pattern : Regexp.new(Regexp.escape(pattern.to_s)) }. |
| all? { |pattern| content =~ pattern } |
| end |
| |
| # :call-seq: |
| # empty?() => boolean |
| # |
| # Returns true if this entry is empty. |
| def empty?() |
| read_content_from_tar.nil? |
| end |
| |
| # :call-seq: |
| # exist() => boolean |
| # |
| # Returns true if this entry exists. |
| def exist?() |
| exist = false |
| @tar_task.with_uncompressed_tar { |tar| exist = tar.any? { |entry| entry.name == @entry_name } } |
| exist |
| end |
| |
| def to_s #:nodoc: |
| @entry_name |
| end |
| |
| private |
| |
| def read_content_from_tar |
| content = Errno::ENOENT.new("No such file or directory - #{@entry_name}") |
| @tar_task.with_uncompressed_tar do |tar| |
| content = tar.inject(content) { |content, entry| entry.name == @entry_name ? entry.read : content } |
| end |
| raise content if Exception === content |
| content |
| end |
| end |
| |
| end |
| |
| |
| # :call-seq: |
| # tar(file) => TarTask |
| # |
| # The TarTask creates a new Tar file. You can include any number of files and |
| # and directories, use exclusion patterns, and include files into specific |
| # directories. |
| # |
| # To create a GZipped Tar, either set the gzip option to true, or use the .tgz or .gz suffix. |
| # |
| # For example: |
| # tar("test.tgz").tap do |tgz| |
| # tgz.include "srcs" |
| # tgz.include "README", "LICENSE" |
| # end |
| def tar(file) |
| TarTask.define_task(file) |
| end |