lib/core/common.rb in buildr-1.2.0 vs lib/core/common.rb in buildr-1.2.1
- old
+ new
@@ -5,10 +5,27 @@
require "uri/open-sftp"
class Hash
+ class << self
+
+ # :call-seq:
+ # Hash.from_java_properties(string)
+ #
+ # Returns a hash from a string in the Java properties file format. For example:
+ # str = "foo=bar\nbaz=fab"
+ # Hash.from_properties(str)
+ # => { "foo"=>"bar", "baz"=>"fab" }.to_properties
+ def from_java_properties(string)
+ string.gsub(/\\\n/, "").split("\n").select { |line| line =~ /^[^#].*=.*/ }.
+ map { |line| line.gsub(/\\[trnf\\]/) { |escaped| {?t=>"\t", ?r=>"\r", ?n=>"\n", ?f=>"\f", ?\\=>"\\"}[escaped[1]] } }.
+ inject({}) { |hash, line| name, value = line.split("=") ; hash[name] = value ; hash }
+ end
+
+ end
+
# :call-seq:
# only(keys*) => hash
#
# Returns a new hash with only the specified keys.
#
@@ -30,10 +47,24 @@
# => { :a=>1, :c=>3 }
def except(*keys)
self.inject({}) { |hash, pair| hash[pair[0]] = pair[1] unless keys.include?(pair[0]) ; hash }
end
+ # :call-seq:
+ # to_java_properties() => string
+ #
+ # Convert hash to string format used for Java properties file. For example:
+ # { "foo"=>"bar", "baz"=>"fab" }.to_properties
+ # => foo=bar
+ # baz=fab
+ def to_java_properties()
+ keys.sort.map { |key|
+ value = self[key].gsub(/[\t\r\n\f\\]/) { |escape| "\\" + {"\t"=>"t", "\r"=>"r", "\n"=>"n", "\f"=>"f", "\\"=>"\\"}[escape] }
+ "#{key}=#{value}"
+ }.join("\n")
+ end
+
end
module Buildr
@@ -42,21 +73,79 @@
# for example:
# options.proxy.http = "http://proxy.acme.com:8080"
# options.java_args = "-Xmx512M"
class Options
+ # We use this to present environment variable as arrays.
+ class EnvArray < Array #:nodoc:
+
+ def initialize(name)
+ @name = name.upcase
+ replace((ENV[@name] || ENV[@name.downcase] || "").split(/\s*,\s*/).reject(&:empty?))
+ end
+
+ (Array.instance_methods - Object.instance_methods - Enumerable.instance_methods).sort.each do |method|
+ class_eval %{def #{method}(*args, &block) ; result = super ; write ; result ; end}
+ end
+
+ private
+
+ def write()
+ ENV[@name.downcase] = nil
+ ENV[@name] = map(&:to_s).join(",")
+ end
+
+ end
+
+ # Wraps around the proxy environment variables:
+ # * :http -- HTTP_PROXY
+ # * :exclude -- NO_PROXY
+ class Proxies
+
+ # Returns the HTTP_PROXY URL.
+ def http()
+ ENV["HTTP_PROXY"] || ENV["http_proxy"]
+ end
+
+ # Sets the HTTP_PROXY URL.
+ def http=(url)
+ ENV["http_proxy"] = nil
+ ENV["HTTP_PROXY"] = url
+ end
+
+ # Returns list of hosts to exclude from proxying (NO_PROXY).
+ def exclude()
+ @exclude ||= EnvArray.new("NO_PROXY")
+ end
+
+ # Sets list of hosts to exclude from proxy (NO_PROXY). Accepts host name, array of names,
+ # or nil to clear the list.
+ def exclude=(url)
+ exclude.clear
+ exclude.concat [url].flatten if url
+ exclude
+ end
+
+ end
+
# :call-seq:
# proxy() => options
#
# Returns the proxy options. Currently supported options are:
# * :http -- HTTP proxy for use when downloading.
+ # * :exclude -- Do not use proxy for these hosts/domains.
#
# For example:
# options.proxy.http = "http://proxy.acme.com:8080"
# You can also set it using the environment variable HTTP_PROXY.
+ #
+ # You can exclude individual hosts from being proxied, or entire domains, for example:
+ # options.proxy.exclude = "optimus"
+ # options.proxy.exclude = ["optimus", "prime"]
+ # options.proxy.exclude << "*.internal"
def proxy()
- @proxy ||= Struct.new(:http).new(ENV['HTTP_PROXY'] || ENV['http_proxy'])
+ @proxy ||= Proxies.new
end
end
class << self
@@ -189,30 +278,49 @@
# each source file, with the file name and content, returning the modified content.
#
# Without any mapping, the filter simply copies files from the source directory into the target
# directory.
#
+ # A filter has one target directory, but you can specify any number of source directories,
+ # either when creating the filter or calling #from. Include/exclude patterns are specified
+ # relative to the source directories, so:
+ # filter.include "*.png"
+ # will only include PNG files from any of the source directories.
+ #
# See Buildr#filter.
class Filter
def initialize() #:nodoc:
@include = []
@exclude = []
+ @sources = []
end
- # The source directory as a file task.
- attr_accessor :source
+ # Returns the list of source directories (each being a file task).
+ attr_reader :sources
+ # *Deprecated* Use #sources instead.
+ def source()
+ warn_deprecated "Please use sources instead."
+ @sources.first
+ end
+
+ # *Deprecated* Use #from instead.
+ def source=(dir)
+ warn_deprecated "Please use from instead."
+ from(dir)
+ end
+
# :call-seq:
- # from(dir) => self
+ # from(*sources) => self
#
- # Sets the source directory from which files are copied and returns self.
+ # Adds additional directories from which to copy resources.
#
# For example:
# filter.from("src").into("target").using("build"=>Time.now)
- def from(dir)
- @source = file(File.expand_path(dir.to_s))
+ def from(*sources)
+ @sources |= sources.flatten.map { |dir| file(File.expand_path(dir.to_s)) }
self
end
# The target directory as a file task.
attr_reader :target
@@ -275,39 +383,44 @@
# :call-seq:
# run() => boolean
#
# Runs the filter.
def run()
- raise "No source directory specified, where am I going to find the files to filter?" if source.nil?
- raise "Source directory #{source} doesn't exist" unless File.exist?(source.to_s)
+ raise "No source directory specified, where am I going to find the files to filter?" if sources.empty?
+ sources.each { |source| raise "Source directory #{source} doesn't exist" unless File.exist?(source.to_s) }
raise "No target directory specified, where am I going to copy the files to?" if target.nil?
- includes = @include.empty? ? ["*"] : @include
- src_base = Pathname.new(source.to_s)
- copy_map = Dir[File.join(source.to_s, "**/*")].reject { |file| File.directory?(file) }.
- map { |src| Pathname.new(src).relative_path_from(src_base).to_s }.
- select { |file| includes.any? { |pattern| File.fnmatch(pattern, file) } }.
- reject { |file| @exclude.any? { |pattern| File.fnmatch(pattern, file) } }.
- map { |file| [File.expand_path(file, target.to_s), File.expand_path(file, source.to_s)] }.
- select { |dest, src| !File.exist?(dest) || File.stat(src).mtime > File.stat(dest).mtime }
+ copy_map = sources.flatten.map(&:to_s).inject({}) do |map, source|
+ base = Pathname.new(source)
+ files = FileList[File.join(source, "**/*")].reject { |file| File.directory?(file) }.
+ map { |file| Pathname.new(file).relative_path_from(base).to_s }.
+ select { |file| @include.empty? || @include.any? { |pattern| File.fnmatch(pattern, file) } }.
+ reject { |file| @exclude.any? { |pattern| File.fnmatch(pattern, file) } }
+ files.each do |file|
+ src, dest = File.expand_path(file, source), File.expand_path(file, target.to_s)
+ map[file] = src if !File.exist?(dest) || File.stat(src).mtime > File.stat(dest).mtime
+ end
+ map
+ end
+
return false if copy_map.empty?
verbose(Rake.application.options.trace || false) do
mkpath target.to_s
- copy_map.each do |dest, src|
+ copy_map.each do |path, source|
+ dest = File.expand_path(path, target.to_s)
mkpath File.dirname(dest) rescue nil
case mapping
when Proc, Method # Call on input, accept output.
- relative = Pathname.new(src).relative_path_from(src_base).to_s
- mapped = mapping.call(relative, File.open(src, "rb") { |file| file.read })
+ mapped = mapping.call(path, File.open(source, "rb") { |file| file.read })
File.open(dest, "wb") { |file| file.write mapped }
when Hash # Map ${key} to value
- mapped = File.open(src, "rb") { |file| file.read }.
+ mapped = File.open(source, "rb") { |file| file.read }.
gsub(/\$\{[^}]*\}/) { |str| mapping[str[2..-2]] || str }
File.open(dest, "wb") { |file| file.write mapped }
when nil # No mapping.
- cp src, dest
+ cp source, dest
else
fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{mapping}"
end
end
touch target.to_s
@@ -321,24 +434,24 @@
end
end
# :call-seq:
- # filter(source) => Filter
+ # filter(*source) => Filter
#
- # Creates a filter that will copy files from the source directory into the target directory.
+ # Creates a filter that will copy files from the source directory(ies) into the target directory.
# You can extend the filter to modify files by mapping <tt>${key}</tt> into values in each
# of the copied files, and by including or excluding specific files.
#
# A filter is not a task, you must call the Filter#run method to execute it.
#
# For example, to copy all files from one directory to another:
# filter("src/files").into("target/classes").run
# To include only the text files, and replace each instance of <tt>${build}</tt> with the current
# date/time:
# filter("src/files").into("target/classes").include("*.txt").using("build"=>Time.now).run
- def filter(source)
- Filter.new.from(source)
+ def filter(*sources)
+ Filter.new.from(*sources)
end
end