buildr 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,4 +1,16 @@
1
- 1.2 (6/6/2007)
1
+ 1.2.1 (7/12/2007)
2
+ * Added: Proxy exclusion, use environment variable NO_PROXY, or options.proxy.exclude = <url> || [<url>] (http://groups.google.com/group/buildr-talk/t/9f1e988e0dbeea9f).
3
+ * Added: You can now copy resources from multiple source directories, using resources.from (http://groups.google.com/group/buildr-talk/browse_thread/thread/4f2867a6dbbc19d4).
4
+ * Added: Hash.from_java_properties(string) and hash.to_java_properties.
5
+ * Changed: Buildr.options now wrap various environment variables instead of duplicating them (HTTP_PROXY, NO_PROXY, TEST, DEBUG).
6
+ * Changed: No longer passing proxies to transports, instead they obtain them from environment variables.
7
+ * Changed: Buildr now uses XJavaDoc 1.1 instead of 1.1-j5. If you need the 1.1-j5 fix, see here http://groups.google.com/group/buildr-talk/browse_thread/thread/49f3226810466c94/1f0d25d002433fe2.
8
+ * Fixed: One RubyForge release for all packages, instead of one per package (Anatol Pomozov).
9
+ * Fixed: buildr command does not recognize project tasks (foo:compile) or default task (http://groups.google.com/group/buildr-talk/t/660061a0bc81989a).
10
+ * Fixed: Upload fails on SFTP permissions.
11
+ * Fixed: Hibernate.schema_export not passing Ant task when yielding.
12
+
13
+ 1.2.0 (6/6/2007)
2
14
  * Added: Artifact.list returns specs for all registered artifacts (those created with artifact or package).
3
15
  * Added: Buildr.option.java_args are used when creating the RJB JVM, when running a Java process (unless you override directly), and when running JUnit tests (again, unless override).
4
16
  * Added: TestNG support (test.using :testng).
data/Rakefile CHANGED
@@ -141,10 +141,12 @@ end
141
141
 
142
142
  namespace :upload do
143
143
  task :docs=>"rake:docs" do |task|
144
- `rsync -r --del --progress html/* rubyforge.org:/var/www/gforge-projects/#{spec.rubyforge_project.downcase}`
144
+ sh "rsync -r --del --progress html/* rubyforge.org:/var/www/gforge-projects/#{spec.rubyforge_project.downcase}"
145
145
  end
146
146
 
147
147
  task :packages=>["rake:docs", "rake:package"] do |task|
148
+ require 'rubyforge'
149
+
148
150
  # Read the changes for this release.
149
151
  pattern = /(^(\d+\.\d+(?:\.\d+)?)\s+\(\d+\/\d+\/\d+\)\s*((:?^[^\n]+\n)*))/
150
152
  changelog = File.read(__FILE__.pathmap("%d/CHANGELOG"))
@@ -156,12 +158,13 @@ namespace :upload do
156
158
  fail "No changeset found for version #{spec.version}" unless current
157
159
 
158
160
  puts "Uploading #{spec.name} #{spec.version}"
159
- files = ["gem", "tgz", "zip"].map { |ext| "pkg/#{spec.name}-#{spec.version}.#{ext}" }
160
- system "rubyforge", "login"
161
- files.each do |file|
162
- system "rubyforge", "add_release", spec.rubyforge_project.downcase, spec.name.downcase,
163
- spec.version.to_s, file, "-a", current
164
- end
161
+ files = %w( gem tgz zip ).map { |ext| "pkg/#{spec.name}-#{spec.version}.#{ext}" }
162
+ rubyforge = RubyForge.new
163
+ rubyforge.login
164
+ File.open(".changes", 'w'){|f| f.write(current)}
165
+ rubyforge.userconfig.merge!("release_changes" => ".changes", "preformatted" => true)
166
+ rubyforge.add_release spec.rubyforge_project.downcase, spec.name.downcase, spec.version, *files
167
+ rm ".changes"
165
168
  puts "Release #{spec.version} uploaded"
166
169
  end
167
170
  end
@@ -19,10 +19,11 @@ require "facet/string/starts_with"
19
19
  require "facet/openobject"
20
20
  require "facets/core/kernel/tap"
21
21
  # A different kind of buildr, one we use to create XML.
22
+ require "builder"
22
23
 
23
24
 
24
25
  module Buildr
25
- VERSION = "1.2.0".freeze
26
+ VERSION = "1.2.1".freeze
26
27
  end
27
28
 
28
29
 
@@ -32,141 +33,143 @@ end
32
33
  unless defined?(Rake)
33
34
  require "rake"
34
35
 
35
- class Application < Rake::Application #:nodoc:
36
-
37
- DEFAULT_BUILDFILES = ["buildfile", "Buildfile"] + DEFAULT_RAKEFILES
38
-
39
- OPTIONS = [ # :nodoc:
40
- ['--help', '-H', GetoptLong::NO_ARGUMENT,
41
- "Display this help message."],
42
- ['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
43
- "Do not search parent directories for the buildfile."],
44
- ['--quiet', '-q', GetoptLong::NO_ARGUMENT,
45
- "Do not log messages to standard output."],
46
- ['--buildfile', '-f', GetoptLong::OPTIONAL_ARGUMENT,
47
- "Use FILE as the buildfile."],
48
- ['--require', '-r', GetoptLong::REQUIRED_ARGUMENT,
49
- "Require MODULE before executing buildfile."],
50
- ['--trace', '-t', GetoptLong::NO_ARGUMENT,
51
- "Turn on invoke/execute tracing, enable full backtrace."],
52
- ['--verbose', '-v', GetoptLong::NO_ARGUMENT,
53
- "Log message to standard output (default)."],
54
- ['--version', '-V', GetoptLong::NO_ARGUMENT,
55
- "Display the program version."],
56
- ['--freeze', "-F", GetoptLong::NO_ARGUMENT,
57
- "Freezes the Buildfile so it always uses Buildr version #{Buildr::VERSION}"],
58
- ['--unfreeze', "-U", GetoptLong::NO_ARGUMENT,
59
- "Unfreezes the Buildfile to use the latest version of Buildr"]
60
- ]
61
-
62
- def initialize()
63
- super
64
- @rakefiles = DEFAULT_BUILDFILES
65
- end
66
-
67
- def run()
68
- standard_exception_handling do
36
+ module Buildr
37
+ class Application < Rake::Application #:nodoc:
38
+
39
+ DEFAULT_BUILDFILES = ["buildfile", "Buildfile"] + DEFAULT_RAKEFILES
40
+
41
+ OPTIONS = [ # :nodoc:
42
+ ['--help', '-H', GetoptLong::NO_ARGUMENT,
43
+ "Display this help message."],
44
+ ['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
45
+ "Do not search parent directories for the buildfile."],
46
+ ['--quiet', '-q', GetoptLong::NO_ARGUMENT,
47
+ "Do not log messages to standard output."],
48
+ ['--buildfile', '-f', GetoptLong::OPTIONAL_ARGUMENT,
49
+ "Use FILE as the buildfile."],
50
+ ['--require', '-r', GetoptLong::REQUIRED_ARGUMENT,
51
+ "Require MODULE before executing buildfile."],
52
+ ['--trace', '-t', GetoptLong::NO_ARGUMENT,
53
+ "Turn on invoke/execute tracing, enable full backtrace."],
54
+ ['--verbose', '-v', GetoptLong::NO_ARGUMENT,
55
+ "Log message to standard output (default)."],
56
+ ['--version', '-V', GetoptLong::NO_ARGUMENT,
57
+ "Display the program version."],
58
+ ['--freeze', "-F", GetoptLong::NO_ARGUMENT,
59
+ "Freezes the Buildfile so it always uses Buildr version #{Buildr::VERSION}"],
60
+ ['--unfreeze', "-U", GetoptLong::NO_ARGUMENT,
61
+ "Unfreezes the Buildfile to use the latest version of Buildr"]
62
+ ]
63
+
64
+ def initialize()
65
+ super
66
+ @rakefiles = DEFAULT_BUILDFILES
69
67
  @name = "Buildr"
70
68
  opts = GetoptLong.new(*command_line_options)
71
69
  opts.each { |opt, value| do_option(opt, value) }
72
70
  collect_tasks
73
- load_buildfile
74
- top_level
75
71
  end
76
- end
77
72
 
78
- def do_option(opt, value)
79
- case opt
80
- when '--help'
81
- help
82
- exit
83
- when "--buildfile"
84
- @rakefiles.clear
85
- @rakefiles << value
86
- when '--version'
87
- puts "Buildr, version #{Buildr::VERSION}"
88
- exit
89
- when "--freeze"
90
- find_buildfile
91
- puts "Freezing the Buildfile so it always uses Buildr version #{Buildr::VERSION}"
92
- gem =
93
- original = File.read(rakefile)
94
- if original =~ /gem\s*(["'])buildr\1/
95
- modified = original.sub(/gem\s*(["'])buildr\1\s*,\s*(["']).*\2/, %{gem "buildr", "#{Buildr::VERSION}"})
96
- else
97
- modified = %{gem "buildr", "#{Buildr::VERSION}"\n} + original
73
+ def run()
74
+ standard_exception_handling do
75
+ load_buildfile
76
+ top_level
98
77
  end
99
- File.open(rakefile, "w") { |file| file.write modified }
100
- exit
101
- when "--unfreeze"
102
- find_buildfile
103
- puts "Unfreezing the Buildfile to use the latest version of Buildr from your Gems repository."
104
- modified = File.read(rakefile).sub(/^\s*gem\s*(["'])buildr\1.*\n/, "")
105
- File.open(rakefile, "w") { |file| file.write modified }
106
- exit
107
- when '--require', "--nosearch", "--quiet", "--trace", "--verbose"
108
- super
109
78
  end
110
- end
111
79
 
112
- def find_buildfile()
113
- here = Dir.pwd
114
- while ! have_rakefile
115
- Dir.chdir("..")
116
- if Dir.pwd == here || options.nosearch
117
- error = "No Buildfile found (looking for: #{@rakefiles.join(', ')})"
118
- if STDIN.isatty
119
- if $terminal.agree("To use Buildr you need a buildfile. Do you want me to create one? (yes/no)")
120
- chdir(original_dir) { task("generate").invoke }
121
- end
122
- exit 1
80
+ def do_option(opt, value)
81
+ case opt
82
+ when '--help'
83
+ help
84
+ exit
85
+ when "--buildfile"
86
+ @rakefiles.clear
87
+ @rakefiles << value
88
+ when '--version'
89
+ puts "Buildr, version #{Buildr::VERSION}"
90
+ exit
91
+ when "--freeze"
92
+ find_buildfile
93
+ puts "Freezing the Buildfile so it always uses Buildr version #{Buildr::VERSION}"
94
+ gem =
95
+ original = File.read(rakefile)
96
+ if original =~ /gem\s*(["'])buildr\1/
97
+ modified = original.sub(/gem\s*(["'])buildr\1\s*,\s*(["']).*\2/, %{gem "buildr", "#{Buildr::VERSION}"})
123
98
  else
124
- raise error
99
+ modified = %{gem "buildr", "#{Buildr::VERSION}"\n} + original
125
100
  end
101
+ File.open(rakefile, "w") { |file| file.write modified }
102
+ exit
103
+ when "--unfreeze"
104
+ find_buildfile
105
+ puts "Unfreezing the Buildfile to use the latest version of Buildr from your Gems repository."
106
+ modified = File.read(rakefile).sub(/^\s*gem\s*(["'])buildr\1.*\n/, "")
107
+ File.open(rakefile, "w") { |file| file.write modified }
108
+ exit
109
+ when '--require', "--nosearch", "--quiet", "--trace", "--verbose"
110
+ super
126
111
  end
112
+ end
113
+
114
+ def find_buildfile()
127
115
  here = Dir.pwd
116
+ while ! have_rakefile
117
+ Dir.chdir("..")
118
+ if Dir.pwd == here || options.nosearch
119
+ error = "No Buildfile found (looking for: #{@rakefiles.join(', ')})"
120
+ if STDIN.isatty
121
+ if $terminal.agree("To use Buildr you need a buildfile. Do you want me to create one? (yes/no)")
122
+ chdir(original_dir) { task("generate").invoke }
123
+ end
124
+ exit 1
125
+ else
126
+ raise error
127
+ end
128
+ end
129
+ here = Dir.pwd
130
+ end
128
131
  end
129
- end
130
132
 
131
- def load_buildfile()
132
- find_buildfile
133
- puts "(in #{Dir.pwd})"
134
- load File.expand_path(@rakefile) if @rakefile != ''
135
- load_imports
136
- end
133
+ def load_buildfile()
134
+ find_buildfile
135
+ puts "(in #{Dir.pwd})"
136
+ load File.expand_path(@rakefile) if @rakefile != ''
137
+ load_imports
138
+ end
137
139
 
138
- def usage()
139
- puts "Buildr #{Buildr::VERSION}"
140
- puts
141
- puts "Usage:"
142
- puts " buildr [-f buildfile] {options} targets..."
143
- end
140
+ def usage()
141
+ puts "Buildr #{Buildr::VERSION}"
142
+ puts
143
+ puts "Usage:"
144
+ puts " buildr [-f buildfile] {options} targets..."
145
+ end
144
146
 
145
- def help()
146
- usage
147
- puts
148
- puts "Options:"
149
- OPTIONS.sort.each do |long, short, mode, desc|
150
- if mode == GetoptLong::REQUIRED_ARGUMENT
151
- if desc =~ /\b([A-Z]{2,})\b/
152
- long = long + "=#{$1}"
147
+ def help()
148
+ usage
149
+ puts
150
+ puts "Options:"
151
+ OPTIONS.sort.each do |long, short, mode, desc|
152
+ if mode == GetoptLong::REQUIRED_ARGUMENT
153
+ if desc =~ /\b([A-Z]{2,})\b/
154
+ long = long + "=#{$1}"
155
+ end
153
156
  end
157
+ printf " %-20s (%s)\n", long, short
158
+ printf " %s\n", desc
154
159
  end
155
- printf " %-20s (%s)\n", long, short
156
- printf " %s\n", desc
160
+ puts
161
+ puts "For help with your buildfile:"
162
+ puts " buildr help"
163
+ end
164
+
165
+ def command_line_options
166
+ OPTIONS.collect { |lst| lst[0..-2] }
157
167
  end
158
- puts
159
- puts "For help with your buildfile:"
160
- puts " buildr help"
161
- end
162
168
 
163
- def command_line_options
164
- OPTIONS.collect { |lst| lst[0..-2] }
165
169
  end
166
170
 
171
+ Rake.application = Application.new
167
172
  end
168
-
169
- Rake.application = Application.new
170
173
  end
171
174
 
172
175
 
@@ -12,7 +12,8 @@ module Buildr
12
12
  :dom4j => "dom4j:dom4j:jar:1.6.1",
13
13
  :hibernate => "org.hibernate:hibernate:jar:3.1.2",
14
14
  :xdoclet => Buildr.group("xdoclet", "xdoclet-xdoclet-module", "xdoclet-hibernate-module",
15
- :under=>"xdoclet", :version=>"1.2.3") + ["xdoclet:xjavadoc:jar:1.1-j5"]
15
+ # :under=>"xdoclet", :version=>"1.2.3") + ["xdoclet:xjavadoc:jar:1.1-j5"]
16
+ :under=>"xdoclet", :version=>"1.2.3") + ["xdoclet:xjavadoc:jar:1.1"]
16
17
  )
17
18
 
18
19
  class << self
@@ -50,10 +51,10 @@ module Buildr
50
51
  # :drop=>"no", :create=>"yes", :output=>target) do
51
52
  # fileset :dir=>source.to_s, :includes=>"**/*.hbm.xml"
52
53
  # end
53
- def schemaexport(options = nil, &block)
54
+ def schemaexport(options = nil)
54
55
  ant "schemaexport" do |ant|
55
56
  ant.taskdef :name=>"schemaexport", :classname=>"org.hibernate.tool.hbm2ddl.SchemaExportTask", :classpath=>requires
56
- ant.schemaexport options, &block if options
57
+ ant.schemaexport(options) { yield ant if block_given? } if options
57
58
  end
58
59
  end
59
60
 
@@ -7,6 +7,23 @@ require "uri/open-sftp"
7
7
 
8
8
  class Hash
9
9
 
10
+ class << self
11
+
12
+ # :call-seq:
13
+ # Hash.from_java_properties(string)
14
+ #
15
+ # Returns a hash from a string in the Java properties file format. For example:
16
+ # str = "foo=bar\nbaz=fab"
17
+ # Hash.from_properties(str)
18
+ # => { "foo"=>"bar", "baz"=>"fab" }.to_properties
19
+ def from_java_properties(string)
20
+ string.gsub(/\\\n/, "").split("\n").select { |line| line =~ /^[^#].*=.*/ }.
21
+ map { |line| line.gsub(/\\[trnf\\]/) { |escaped| {?t=>"\t", ?r=>"\r", ?n=>"\n", ?f=>"\f", ?\\=>"\\"}[escaped[1]] } }.
22
+ inject({}) { |hash, line| name, value = line.split("=") ; hash[name] = value ; hash }
23
+ end
24
+
25
+ end
26
+
10
27
  # :call-seq:
11
28
  # only(keys*) => hash
12
29
  #
@@ -32,6 +49,20 @@ class Hash
32
49
  self.inject({}) { |hash, pair| hash[pair[0]] = pair[1] unless keys.include?(pair[0]) ; hash }
33
50
  end
34
51
 
52
+ # :call-seq:
53
+ # to_java_properties() => string
54
+ #
55
+ # Convert hash to string format used for Java properties file. For example:
56
+ # { "foo"=>"bar", "baz"=>"fab" }.to_properties
57
+ # => foo=bar
58
+ # baz=fab
59
+ def to_java_properties()
60
+ keys.sort.map { |key|
61
+ value = self[key].gsub(/[\t\r\n\f\\]/) { |escape| "\\" + {"\t"=>"t", "\r"=>"r", "\n"=>"n", "\f"=>"f", "\\"=>"\\"}[escape] }
62
+ "#{key}=#{value}"
63
+ }.join("\n")
64
+ end
65
+
35
66
  end
36
67
 
37
68
 
@@ -44,17 +75,75 @@ module Buildr
44
75
  # options.java_args = "-Xmx512M"
45
76
  class Options
46
77
 
78
+ # We use this to present environment variable as arrays.
79
+ class EnvArray < Array #:nodoc:
80
+
81
+ def initialize(name)
82
+ @name = name.upcase
83
+ replace((ENV[@name] || ENV[@name.downcase] || "").split(/\s*,\s*/).reject(&:empty?))
84
+ end
85
+
86
+ (Array.instance_methods - Object.instance_methods - Enumerable.instance_methods).sort.each do |method|
87
+ class_eval %{def #{method}(*args, &block) ; result = super ; write ; result ; end}
88
+ end
89
+
90
+ private
91
+
92
+ def write()
93
+ ENV[@name.downcase] = nil
94
+ ENV[@name] = map(&:to_s).join(",")
95
+ end
96
+
97
+ end
98
+
99
+ # Wraps around the proxy environment variables:
100
+ # * :http -- HTTP_PROXY
101
+ # * :exclude -- NO_PROXY
102
+ class Proxies
103
+
104
+ # Returns the HTTP_PROXY URL.
105
+ def http()
106
+ ENV["HTTP_PROXY"] || ENV["http_proxy"]
107
+ end
108
+
109
+ # Sets the HTTP_PROXY URL.
110
+ def http=(url)
111
+ ENV["http_proxy"] = nil
112
+ ENV["HTTP_PROXY"] = url
113
+ end
114
+
115
+ # Returns list of hosts to exclude from proxying (NO_PROXY).
116
+ def exclude()
117
+ @exclude ||= EnvArray.new("NO_PROXY")
118
+ end
119
+
120
+ # Sets list of hosts to exclude from proxy (NO_PROXY). Accepts host name, array of names,
121
+ # or nil to clear the list.
122
+ def exclude=(url)
123
+ exclude.clear
124
+ exclude.concat [url].flatten if url
125
+ exclude
126
+ end
127
+
128
+ end
129
+
47
130
  # :call-seq:
48
131
  # proxy() => options
49
132
  #
50
133
  # Returns the proxy options. Currently supported options are:
51
134
  # * :http -- HTTP proxy for use when downloading.
135
+ # * :exclude -- Do not use proxy for these hosts/domains.
52
136
  #
53
137
  # For example:
54
138
  # options.proxy.http = "http://proxy.acme.com:8080"
55
139
  # You can also set it using the environment variable HTTP_PROXY.
140
+ #
141
+ # You can exclude individual hosts from being proxied, or entire domains, for example:
142
+ # options.proxy.exclude = "optimus"
143
+ # options.proxy.exclude = ["optimus", "prime"]
144
+ # options.proxy.exclude << "*.internal"
56
145
  def proxy()
57
- @proxy ||= Struct.new(:http).new(ENV['HTTP_PROXY'] || ENV['http_proxy'])
146
+ @proxy ||= Proxies.new
58
147
  end
59
148
 
60
149
  end
@@ -191,26 +280,45 @@ module Buildr
191
280
  # Without any mapping, the filter simply copies files from the source directory into the target
192
281
  # directory.
193
282
  #
283
+ # A filter has one target directory, but you can specify any number of source directories,
284
+ # either when creating the filter or calling #from. Include/exclude patterns are specified
285
+ # relative to the source directories, so:
286
+ # filter.include "*.png"
287
+ # will only include PNG files from any of the source directories.
288
+ #
194
289
  # See Buildr#filter.
195
290
  class Filter
196
291
 
197
292
  def initialize() #:nodoc:
198
293
  @include = []
199
294
  @exclude = []
295
+ @sources = []
200
296
  end
201
297
 
202
- # The source directory as a file task.
203
- attr_accessor :source
298
+ # Returns the list of source directories (each being a file task).
299
+ attr_reader :sources
300
+
301
+ # *Deprecated* Use #sources instead.
302
+ def source()
303
+ warn_deprecated "Please use sources instead."
304
+ @sources.first
305
+ end
204
306
 
307
+ # *Deprecated* Use #from instead.
308
+ def source=(dir)
309
+ warn_deprecated "Please use from instead."
310
+ from(dir)
311
+ end
312
+
205
313
  # :call-seq:
206
- # from(dir) => self
314
+ # from(*sources) => self
207
315
  #
208
- # Sets the source directory from which files are copied and returns self.
316
+ # Adds additional directories from which to copy resources.
209
317
  #
210
318
  # For example:
211
319
  # filter.from("src").into("target").using("build"=>Time.now)
212
- def from(dir)
213
- @source = file(File.expand_path(dir.to_s))
320
+ def from(*sources)
321
+ @sources |= sources.flatten.map { |dir| file(File.expand_path(dir.to_s)) }
214
322
  self
215
323
  end
216
324
 
@@ -277,35 +385,40 @@ module Buildr
277
385
  #
278
386
  # Runs the filter.
279
387
  def run()
280
- raise "No source directory specified, where am I going to find the files to filter?" if source.nil?
281
- raise "Source directory #{source} doesn't exist" unless File.exist?(source.to_s)
388
+ raise "No source directory specified, where am I going to find the files to filter?" if sources.empty?
389
+ sources.each { |source| raise "Source directory #{source} doesn't exist" unless File.exist?(source.to_s) }
282
390
  raise "No target directory specified, where am I going to copy the files to?" if target.nil?
283
391
 
284
- includes = @include.empty? ? ["*"] : @include
285
- src_base = Pathname.new(source.to_s)
286
- copy_map = Dir[File.join(source.to_s, "**/*")].reject { |file| File.directory?(file) }.
287
- map { |src| Pathname.new(src).relative_path_from(src_base).to_s }.
288
- select { |file| includes.any? { |pattern| File.fnmatch(pattern, file) } }.
289
- reject { |file| @exclude.any? { |pattern| File.fnmatch(pattern, file) } }.
290
- map { |file| [File.expand_path(file, target.to_s), File.expand_path(file, source.to_s)] }.
291
- select { |dest, src| !File.exist?(dest) || File.stat(src).mtime > File.stat(dest).mtime }
392
+ copy_map = sources.flatten.map(&:to_s).inject({}) do |map, source|
393
+ base = Pathname.new(source)
394
+ files = FileList[File.join(source, "**/*")].reject { |file| File.directory?(file) }.
395
+ map { |file| Pathname.new(file).relative_path_from(base).to_s }.
396
+ select { |file| @include.empty? || @include.any? { |pattern| File.fnmatch(pattern, file) } }.
397
+ reject { |file| @exclude.any? { |pattern| File.fnmatch(pattern, file) } }
398
+ files.each do |file|
399
+ src, dest = File.expand_path(file, source), File.expand_path(file, target.to_s)
400
+ map[file] = src if !File.exist?(dest) || File.stat(src).mtime > File.stat(dest).mtime
401
+ end
402
+ map
403
+ end
404
+
292
405
  return false if copy_map.empty?
293
406
 
294
407
  verbose(Rake.application.options.trace || false) do
295
408
  mkpath target.to_s
296
- copy_map.each do |dest, src|
409
+ copy_map.each do |path, source|
410
+ dest = File.expand_path(path, target.to_s)
297
411
  mkpath File.dirname(dest) rescue nil
298
412
  case mapping
299
413
  when Proc, Method # Call on input, accept output.
300
- relative = Pathname.new(src).relative_path_from(src_base).to_s
301
- mapped = mapping.call(relative, File.open(src, "rb") { |file| file.read })
414
+ mapped = mapping.call(path, File.open(source, "rb") { |file| file.read })
302
415
  File.open(dest, "wb") { |file| file.write mapped }
303
416
  when Hash # Map ${key} to value
304
- mapped = File.open(src, "rb") { |file| file.read }.
417
+ mapped = File.open(source, "rb") { |file| file.read }.
305
418
  gsub(/\$\{[^}]*\}/) { |str| mapping[str[2..-2]] || str }
306
419
  File.open(dest, "wb") { |file| file.write mapped }
307
420
  when nil # No mapping.
308
- cp src, dest
421
+ cp source, dest
309
422
  else
310
423
  fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{mapping}"
311
424
  end
@@ -323,9 +436,9 @@ module Buildr
323
436
  end
324
437
 
325
438
  # :call-seq:
326
- # filter(source) => Filter
439
+ # filter(*source) => Filter
327
440
  #
328
- # Creates a filter that will copy files from the source directory into the target directory.
441
+ # Creates a filter that will copy files from the source directory(ies) into the target directory.
329
442
  # You can extend the filter to modify files by mapping <tt>${key}</tt> into values in each
330
443
  # of the copied files, and by including or excluding specific files.
331
444
  #
@@ -336,8 +449,8 @@ module Buildr
336
449
  # To include only the text files, and replace each instance of <tt>${build}</tt> with the current
337
450
  # date/time:
338
451
  # filter("src/files").into("target/classes").include("*.txt").using("build"=>Time.now).run
339
- def filter(source)
340
- Filter.new.from(source)
452
+ def filter(*sources)
453
+ Filter.new.from(*sources)
341
454
  end
342
455
 
343
456
  end
@@ -56,7 +56,6 @@ module URI
56
56
  # File.open("image.jpg", "w") { |file| file.write URI.read("http://example.com/image.jpg") }
57
57
  #
58
58
  # Supported options:
59
- # * :proxy -- Collection of proxy settings, accessed by scheme.
60
59
  # * :modified -- Only download if file modified since this timestamp. Returns nil if not modified.
61
60
  # * :progress -- Show the progress bar while reading.
62
61
  def read(uri, options = nil, &block)
@@ -94,7 +93,6 @@ module URI
94
93
  # write "sftp://localhost/jars/killer-app.jar", File.read("killer-app.jar")
95
94
  #
96
95
  # Supported options:
97
- # * :proxy -- Collection of proxy settings, accessed by scheme.
98
96
  # * :progress -- Show the progress bar while reading.
99
97
  def write(uri, *args, &block)
100
98
  uri = URI.parse(uri.to_s) unless URI === uri
@@ -176,7 +174,19 @@ module URI
176
174
  #
177
175
  # For options, see URI::write.
178
176
  def write(*args, &block)
179
- fail "This protocol doesn't support writing (yet, how about helping by implementing it?)"
177
+ options = args.pop if Hash === args.last
178
+ options ||= {}
179
+ if String === args.first
180
+ ios = StringIO.new(args.first, "r")
181
+ write(options.merge(:size=>args.first.size)) { |bytes| ios.read(bytes) }
182
+ elsif args.first.respond_to?(:read)
183
+ size = args.first.size rescue nil
184
+ write({:size=>size}.merge(options)) { |bytes| args.first.read(bytes) }
185
+ elsif args.empty? && block
186
+ write_internal options, &block
187
+ else
188
+ raise ArgumentError, "Either give me the content, or pass me a block, otherwise what would I upload?"
189
+ end
180
190
  end
181
191
 
182
192
  # :call-seq:
@@ -274,8 +284,26 @@ module URI
274
284
  end
275
285
  end
276
286
 
287
+ # :call-seq:
288
+ # proxy_uri() => URI?
289
+ #
290
+ # Returns the proxy server to use. Obtains the proxy from the relevant environment variable (e.g. HTTP_PROXY).
291
+ # Supports exclusions based on host name and port number from environment variable NO_PROXY.
292
+ def proxy_uri()
293
+ proxy = ENV["#{scheme.upcase}_PROXY"]
294
+ proxy = URI.parse(proxy) if String === proxy
295
+ excludes = (ENV["NO_PROXY"] || "").split(/\s*,\s*/).compact
296
+ excludes = excludes.map { |exclude| exclude =~ /:\d+$/ ? exclude : "#{exclude}:*" }
297
+ return proxy unless excludes.any? { |exclude| File.fnmatch(exclude, "#{host}:#{port}") }
298
+ end
299
+
300
+ def write_internal(options, &block) #:nodoc:
301
+ fail "This protocol doesn't support writing (yet, how about helping by implementing it?)"
302
+ end
303
+
277
304
  end
278
305
 
306
+
279
307
  class HTTP #:nodoc:
280
308
 
281
309
  # See URI::Generic#read
@@ -322,8 +350,7 @@ module URI
322
350
  end
323
351
  end
324
352
 
325
- proxy = options[:proxy] && options[:proxy].http
326
- if proxy
353
+ if proxy = proxy_uri
327
354
  proxy = URI.parse(proxy) if String === proxy
328
355
  Net::HTTP.start(host, port, proxy.host, proxy.port, proxy.user, proxy.password) { |http| request[http] }
329
356
  else
@@ -334,6 +361,7 @@ module URI
334
361
 
335
362
  end
336
363
 
364
+
337
365
  class SFTP #:nodoc:
338
366
 
339
367
  class << self
@@ -343,65 +371,53 @@ module URI
343
371
  end
344
372
  end
345
373
 
346
- # See URI::Generic#write
347
- def write(*args, &block)
348
- options = args.pop if Hash === args.last
349
- options ||= {}
350
- if String === args.first
351
- ios = StringIO.new(args.first, "r")
352
- write(options.merge(:size=>args.first.size)) { |bytes| ios.read(bytes) }
353
- elsif args.first.respond_to?(:read)
354
- size = args.first.size rescue nil
355
- write({:size=>size}.merge(options)) { |bytes| args.first.read(bytes) }
356
- elsif args.empty? && block
374
+ protected
357
375
 
358
- # SSH options are based on the username/password from the URI.
359
- ssh_options = { :port=>port, :username=>user }.merge(options[:ssh_options] || {})
360
- ssh_options[:password] ||= SFTP.passwords[host]
361
- begin
362
- puts "Connecting to #{host}" if Rake.application.options.trace
363
- session = Net::SSH.start(host, ssh_options)
364
- SFTP.passwords[host] = ssh_options[:password]
365
- rescue Net::SSH::AuthenticationFailed=>ex
366
- # Only if running with console, prompt for password.
367
- if !ssh_options[:password] && $stdout.isatty
368
- password = HighLine.new.ask("Password for #{host}:") { |q| q.echo = "*" }
369
- ssh_options[:password] = password
370
- retry
371
- end
372
- raise
376
+ def write_internal(options, &block) #:nodoc:
377
+ # SSH options are based on the username/password from the URI.
378
+ ssh_options = { :port=>port, :username=>user }.merge(options[:ssh_options] || {})
379
+ ssh_options[:password] ||= SFTP.passwords[host]
380
+ begin
381
+ puts "Connecting to #{host}" if Rake.application.options.trace
382
+ session = Net::SSH.start(host, ssh_options)
383
+ SFTP.passwords[host] = ssh_options[:password]
384
+ rescue Net::SSH::AuthenticationFailed=>ex
385
+ # Only if running with console, prompt for password.
386
+ if !ssh_options[:password] && $stdout.isatty
387
+ password = HighLine.new.ask("Password for #{host}:") { |q| q.echo = "*" }
388
+ ssh_options[:password] = password
389
+ retry
373
390
  end
391
+ raise
392
+ end
374
393
 
375
- session.sftp.connect do |sftp|
376
- puts "connected" if Rake.application.options.trace
394
+ session.sftp.connect do |sftp|
395
+ puts "connected" if Rake.application.options.trace
377
396
 
378
- # To create a path, we need to create all its parent. We use realpath to determine if
379
- # the path already exists, otherwise mkdir fails.
380
- puts "Creating path #{@base_path}" if Rake.application.options.trace
381
- path.split("/").inject("") do |base, part|
382
- combined = base + part
383
- sftp.realpath combined rescue sftp.mkdir combined, {}
384
- "#{combined}/"
385
- end
397
+ # To create a path, we need to create all its parent. We use realpath to determine if
398
+ # the path already exists, otherwise mkdir fails.
399
+ puts "Creating path #{@base_path}" if Rake.application.options.trace
400
+ path.split("/").inject("") do |base, part|
401
+ combined = base + part
402
+ sftp.realpath combined rescue sftp.mkdir combined, {}
403
+ "#{combined}/"
404
+ end
386
405
 
387
- with_progress_bar options[:progress] && options[:size], path.split("/"), options[:size] || 0 do |progress|
388
- puts "Uploading to #{path}" if Rake.application.options.trace
389
- sftp.open_handle(path, "w") do |handle|
390
- # Writing in chunks gives us the benefit of a progress bar,
391
- # but also require that we maintain a position in the file,
392
- # since write() with two arguments always writes at position 0.
393
- pos = 0
394
- while chunk = yield(32 * 4096)
395
- sftp.write(handle, chunk, pos)
396
- pos += chunk.size
397
- progress << chunk
398
- end
399
- sftp.setstat(target_path, :permissions => options[:permissions]) if options[:permissions]
406
+ with_progress_bar options[:progress] && options[:size], path.split("/"), options[:size] || 0 do |progress|
407
+ puts "Uploading to #{path}" if Rake.application.options.trace
408
+ sftp.open_handle(path, "w") do |handle|
409
+ # Writing in chunks gives us the benefit of a progress bar,
410
+ # but also require that we maintain a position in the file,
411
+ # since write() with two arguments always writes at position 0.
412
+ pos = 0
413
+ while chunk = yield(32 * 4096)
414
+ sftp.write(handle, chunk, pos)
415
+ pos += chunk.size
416
+ progress << chunk
400
417
  end
418
+ sftp.setstat(path, :permissions => options[:permissions]) if options[:permissions]
401
419
  end
402
420
  end
403
- else
404
- raise ArgumentError, "Either give me the content, or pass me a block, otherwise what would I upload?"
405
421
  end
406
422
  end
407
423
 
@@ -452,38 +468,6 @@ module URI
452
468
  end
453
469
  end
454
470
 
455
- # See URI::Generic#write
456
- def write(*args, &block)
457
- options = args.pop if Hash === args.last
458
- options ||= {}
459
- raise ArgumentError, "Either you're attempting to write a file to another host (which we don't support), or you used two slashes by mistake, where you should have file:///<path>." unless host.blank?
460
-
461
- if String === args.first
462
- ios = StringIO.new(args.first, "r")
463
- write(options.merge(:size=>args.first.size)) { |bytes| ios.read(bytes) }
464
- elsif args.first.respond_to?(:read)
465
- size = args.first.size rescue nil
466
- write({:size=>size}.merge(options)) { |bytes| args.first.read(bytes) }
467
- elsif args.empty? && block
468
- temp = nil
469
- Tempfile.open File.basename(path) do |temp|
470
- temp.binmode
471
- with_progress_bar options[:progress] && options[:size], path.split("/"), options[:size] || 0 do |progress|
472
- while chunk = yield(32 * 4096)
473
- temp.write chunk
474
- progress << chunk
475
- end
476
- end
477
- end
478
- real_path.tap do |path|
479
- mkpath File.dirname(path)
480
- File.move temp.path, path
481
- end
482
- else
483
- raise ArgumentError, "Either give me the content, or pass me a block, otherwise what would I upload?"
484
- end
485
- end
486
-
487
471
  def to_s()
488
472
  "file://#{host}#{path}"
489
473
  end
@@ -495,6 +479,26 @@ module URI
495
479
  RUBY_PLATFORM =~ /win32/ && path =~ /^\/[a-zA-Z]:\// ? path[1..-1] : path
496
480
  end
497
481
 
482
+ protected
483
+
484
+ def write_internal(options, &block) #:nodoc:
485
+ raise ArgumentError, "Either you're attempting to write a file to another host (which we don't support), or you used two slashes by mistake, where you should have file:///<path>." unless host.blank?
486
+ temp = nil
487
+ Tempfile.open File.basename(path) do |temp|
488
+ temp.binmode
489
+ with_progress_bar options[:progress] && options[:size], path.split("/"), options[:size] || 0 do |progress|
490
+ while chunk = yield(32 * 4096)
491
+ temp.write chunk
492
+ progress << chunk
493
+ end
494
+ end
495
+ end
496
+ real_path.tap do |path|
497
+ mkpath File.dirname(path)
498
+ File.move temp.path, path
499
+ end
500
+ end
501
+
498
502
  @@schemes["FILE"] = FILE
499
503
 
500
504
  end
@@ -100,7 +100,7 @@ module Buildr
100
100
  # In the first form, uses the upload options specified by repositories.release_to.
101
101
  # In the second form, uses a URL that includes all the relevant information.
102
102
  # In the third form, uses a hash with the options :url, :username, :password,
103
- # :proxy and :permissions. All but :url are optional.
103
+ # and :permissions. All but :url are optional.
104
104
  def upload(upload_to = nil)
105
105
  # Where do we release to?
106
106
  upload_to ||= Buildr.repositories.release_to
@@ -117,7 +117,7 @@ module Buildr
117
117
  # Upload artifact relative to base URL, need to create path before uploading.
118
118
  puts "Deploying #{to_spec}" if verbose
119
119
  path = group.gsub(".", "/") + "/#{id}/#{version}/#{File.basename(name)}"
120
- URI.upload uri + path, name, :proxy=>Buildr.options.proxy, :permissions=>upload_to[:permissions]
120
+ URI.upload uri + path, name, :permissions=>upload_to[:permissions]
121
121
  end
122
122
 
123
123
  protected
@@ -275,7 +275,7 @@ module Buildr
275
275
  begin
276
276
  path = group.gsub(".", "/") + "/#{id}/#{version}/#{File.basename(name)}"
277
277
  mkpath File.dirname(name), :verbose=>false
278
- URI.download repo_url + path, name, :proxy=>Buildr.options.proxy
278
+ URI.download repo_url + path, name
279
279
  true
280
280
  rescue URI::NotFoundError
281
281
  false
@@ -396,8 +396,8 @@ module Buildr
396
396
  mkpath File.dirname(filename), :verbose=>false
397
397
  # We absolutely need the POM, so make sure we download it before the artifact
398
398
  # (unless the artifact is a POM).
399
- URI.download repo_url + path.ext("pom"), name.ext("pom"), :proxy=>Buildr.options.proxy unless type == :pom
400
- URI.download repo_url + path, filename, :proxy=>Buildr.options.proxy
399
+ URI.download repo_url + path.ext("pom"), filename.ext("pom") unless type == :pom
400
+ URI.download repo_url + path, filename
401
401
  true
402
402
  rescue URI::NotFoundError
403
403
  false
@@ -584,13 +584,13 @@ module Buildr
584
584
  puts "Deploying #{arg.to_spec}" if verbose
585
585
  spec = arg.to_spec_hash
586
586
  path = spec[:group].gsub(".", "/") + "/#{spec[:id]}/#{spec[:version]}/" + Artifact.hash_to_file_name(spec)
587
- URI.upload uri + path, arg.to_s, :proxy=>Buildr.options.proxy, :permissions=>deploy_to[:permissions]
587
+ URI.upload uri + path, arg.to_s, :permissions=>deploy_to[:permissions]
588
588
  else
589
589
  # Upload file to URL.
590
590
  puts "Deploying #{arg}" if verbose
591
591
  path = File.basename(args.to_s)
592
592
  path = File.join(options[:path], path) if options[:path]
593
- URI.upload uri + path, arg.to_s, :proxy=>Buildr.options.proxy, :permissions=>deploy_to[:permissions]
593
+ URI.upload uri + path, arg.to_s, :permissions=>deploy_to[:permissions]
594
594
  end
595
595
  end
596
596
  end
@@ -255,7 +255,7 @@ module Buildr
255
255
  def initialize(*args) #:nodoc:
256
256
  super
257
257
  @filter = Buildr::Filter.new
258
- enhance { filter.run if filter.source && filter.target }
258
+ enhance { filter.run unless filter.sources.empty? }
259
259
  end
260
260
 
261
261
  # :call-seq:
@@ -277,13 +277,28 @@ module Buildr
277
277
  end
278
278
 
279
279
  # :call-seq:
280
- # source() => task
280
+ # from(*sources) => self
281
+ #
282
+ # Adds additional directories from which to copy resources.
281
283
  #
282
- # Returns the filter's source directory as a file task.
284
+ # For example:
285
+ # resources.from _("src/etc")
286
+ def from(*sources)
287
+ filter.from *sources
288
+ self
289
+ end
290
+
291
+ # *Deprecated* Use #sources instead.
283
292
  def source()
293
+ warn_deprecated "Please use sources instead."
284
294
  filter.source
285
295
  end
286
296
 
297
+ # Returns the list of source directories (each being a file task).
298
+ def sources()
299
+ filter.sources
300
+ end
301
+
287
302
  # :call-seq:
288
303
  # target() => task
289
304
  #
@@ -293,7 +308,7 @@ module Buildr
293
308
  end
294
309
 
295
310
  def prerequisites() #:nodoc:
296
- super + [filter.source].compact
311
+ super + filter.sources.flatten
297
312
  end
298
313
 
299
314
  end
@@ -499,7 +514,7 @@ module Buildr
499
514
  # can access by calling resources.filter (see Buildr::Filter).
500
515
  #
501
516
  # For example:
502
- # resources.filter.include "config.xml"
517
+ # resources.from _("src/etc")
503
518
  # resources.filter.using "Copyright"=>"Acme Inc, 2007"
504
519
  def resources(*prereqs, &block)
505
520
  task("resources").enhance prereqs, &block
@@ -528,7 +543,7 @@ module Buildr
528
543
  prepare = task("prepare")
529
544
  # Resources task is a filter.
530
545
  resources = Java::ResourcesTask.define_task("resources")
531
- project.path_to("src/main/resources").tap { |dir| resources.filter.from dir if File.exist?(dir) }
546
+ project.path_to("src/main/resources").tap { |dir| resources.from dir if File.exist?(dir) }
532
547
  # Compile task requires prepare and performs resources, if anything compiled.
533
548
  compile = Java::CompileTask.define_task("compile"=>[prepare, resources])
534
549
  project.path_to("src/main/java").tap { |dir| compile.from dir if File.exist?(dir) }
@@ -552,18 +567,21 @@ module Buildr
552
567
 
553
568
  class Options
554
569
 
555
- # Runs the compile in debugging mode when true (default).
570
+ # Returns the debug option (environment variable DEBUG).
571
+ def debug()
572
+ (ENV["DEBUG"] || ENV["debug"]) !~ /(no|off|false)/
573
+ end
574
+
575
+ # Sets the debug option (environment variable DEBUG).
556
576
  #
557
577
  # You can turn this option off directly, or by setting the environment variable
558
578
  # DEBUG to "no". For example:
559
579
  # buildr build DEBUG=no
560
580
  #
561
581
  # The release tasks runs a build with <tt>DEBUG=no</tt>.
562
- attr_accessor :debug
563
-
564
- def debug() #:nodoc:
565
- @debug = (ENV["DEBUG"] || ENV["debug"]) !~ /(no|off|false)/ if @debug.nil?
566
- @debug
582
+ def debug=(flag)
583
+ ENV["debug"] = nil
584
+ ENV["DEBUG"] = flag.to_s
567
585
  end
568
586
 
569
587
  end
@@ -36,7 +36,7 @@ module Buildr
36
36
  excludes = [ '**/.svn/', '**/CVS/' ].join('|')
37
37
 
38
38
  # Only for projects that are packageable.
39
- task_name = project.path_to("#{project.name.sub(':', '-')}.iml")
39
+ task_name = project.path_to("#{project.name.gsub(':', '-')}.iml")
40
40
  idea.enhance [ file(task_name) ]
41
41
 
42
42
  # The only thing we need to look for is a change in the Buildfile.
@@ -123,7 +123,7 @@ module Buildr
123
123
 
124
124
  # Root project aggregates all the subprojects.
125
125
  if project.parent == nil
126
- task_name = project.path_to("#{project.name.sub(':', '-')}.ipr")
126
+ task_name = project.path_to("#{project.name.gsub(':', '-')}.ipr")
127
127
  idea.enhance [ file(task_name) ]
128
128
 
129
129
  file(task_name=>sources) do |task|
@@ -136,7 +136,8 @@ module Buildr
136
136
  xml.modules do
137
137
  project.projects.each do |subp|
138
138
  module_name = subp.name.gsub(":", "-")
139
- module_path = subp.name[/:(.*)/][1..-1]
139
+ module_path = subp.name.split(":"); module_path.shift
140
+ module_path = module_path.join("/")
140
141
  path = "#{module_path}/#{module_name}.iml"
141
142
  xml.module :fileurl=>"file://$PROJECT_DIR$/#{path}", :filepath=>"$PROJECT_DIR$/#{path}"
142
143
  end
@@ -586,7 +586,7 @@ module Buildr
586
586
  #project.recursive_task("test")
587
587
  # Similar to the regular resources task but using different paths.
588
588
  resources = Java::ResourcesTask.define_task("test:resources")
589
- project.path_to("src/test/resources").tap { |dir| resources.filter.from dir if File.exist?(dir) }
589
+ project.path_to("src/test/resources").tap { |dir| resources.from dir if File.exist?(dir) }
590
590
  # Similar to the regular compile task but using different paths.
591
591
  compile = Java::CompileTask.define_task("test:compile"=>[project.compile, task("test:prepare"), project.test.resources])
592
592
  project.path_to("src/test/java").tap { |dir| compile.from dir if File.exist?(dir) }
@@ -620,26 +620,37 @@ module Buildr
620
620
  # Set to false to not run any test cases. Set to :all to run all test cases, ignoring failures.
621
621
  #
622
622
  # This option is set from the environment variable "test", so you can also do:
623
- # buildr # With tests
624
- # buildr test=no # Without tests
625
- # buildr test=all # Ignore failures
626
- attr_accessor :test
627
-
628
- def test() #:nodoc:
629
- if @test.nil?
630
- case value = ENV["TEST"] || ENV["test"]
631
- when /^(no|off|false|skip)$/i
632
- @test = false
633
- when /^all$/i
634
- @test = :all
635
- when /^(yes|on|true)$/i, nil
636
- @test = true
637
- else
638
- warn "Expecting the environment variable test to be 'no' or 'all', not sure what to do with #{value}, so I'm just going to run all the test cases and stop at failure."
639
- @test = true
640
- end
623
+
624
+ # Returns the test option (environment variable TEST). Possible values are:
625
+ # * :false -- Do not run any test cases (also accepts "no" and "skip").
626
+ # * :true -- Run all test cases, stop on failure (default if not set).
627
+ # * :all -- Run all test cases, ignore failures.
628
+ def test()
629
+ case value = ENV["TEST"] || ENV["test"]
630
+ when /^(no|off|false|skip)$/i
631
+ false
632
+ when /^all$/i
633
+ :all
634
+ when /^(yes|on|true)$/i, nil
635
+ true
636
+ else
637
+ warn "Expecting the environment variable test to be 'no' or 'all', not sure what to do with #{value}, so I'm just going to run all the test cases and stop at failure."
638
+ true
641
639
  end
642
- @test
640
+ end
641
+
642
+ # Sets the test option (environment variable TEST). Possible values are true, false or :all.
643
+ #
644
+ # You can also set this from the environment variable, e.g.:
645
+ #
646
+ # buildr # With tests
647
+ # buildr test=no # Without tests
648
+ # buildr test=all # Ignore failures
649
+ # set TEST=no
650
+ # buildr # Without tests
651
+ def test=(flag)
652
+ ENV["test"] = nil
653
+ ENV["TEST"] = flag.to_s
643
654
  end
644
655
 
645
656
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: buildr
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.2.0
7
- date: 2007-07-06 00:00:00 -07:00
6
+ version: 1.2.1
7
+ date: 2007-07-12 00:00:00 -07:00
8
8
  summary: A build system that doesn't suck
9
9
  require_paths:
10
10
  - lib