| Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
|---|---|---|---|---|
| lib/buildr/core/util.rb | 537 | 353 | 12.10%
|
13.03%
|
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
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 'rbconfig' |
18 require 'pathname' |
19 autoload :Tempfile, 'tempfile' |
20 autoload :YAML, 'yaml' |
21 autoload :REXML, 'rexml/document' |
22 gem 'xml-simple' ; autoload :XmlSimple, 'xmlsimple' |
23 gem 'builder' ; autoload :Builder, 'builder' # A different kind of buildr, one we use to create XML. |
24 require 'highline/import' |
25 |
26 |
27 module Buildr |
28 |
29 module Util |
30 extend self |
31 |
32 def java_platform? |
33 RUBY_PLATFORM =~ /java/ |
34 end |
35 |
36 # In order to determine if we are running on a windows OS, |
37 # prefer this function instead of using Gem.win_platform?. |
38 # |
39 # Gem.win_platform? only checks these RUBY_PLATFORM global, |
40 # that in some cases like when running on JRuby is not |
41 # succifient for our purpose: |
42 # |
43 # For JRuby, the value for RUBY_PLATFORM will always be 'java' |
44 # That's why this function checks on Config::CONFIG['host_os'] |
45 def win_os? |
46 Config::CONFIG['host_os'] =~ /windows|cygwin|bccwin|cygwin|djgpp|mingw|mswin|wince/i |
47 end |
48 |
49 # Runs Ruby with these command line arguments. The last argument may be a hash, |
50 # supporting the following keys: |
51 # :command -- Runs the specified script (e.g., :command=>'gem') |
52 # :sudo -- Run as sudo on operating systems that require it. |
53 # :verbose -- Override Rake's verbose flag. |
54 def ruby(*args) |
55 options = Hash === args.last ? args.pop : {} |
56 cmd = [] |
57 ruby_bin = normalize_path(Config::CONFIG['ruby_install_name'], Config::CONFIG['bindir']) |
58 if options.delete(:sudo) && !(win_os? || Process.uid == File.stat(ruby_bin).uid) |
59 cmd << 'sudo' << '-u' << "##{File.stat(ruby_bin).uid}" |
60 end |
61 cmd << ruby_bin |
62 cmd << '-S' << options.delete(:command) if options[:command] |
63 cmd.concat args.flatten |
64 cmd.push options |
65 sh *cmd do |ok, status| |
66 ok or fail "Command ruby failed with status (#{status ? status.exitstatus : 'unknown'}): [#{cmd.join(" ")}]" |
67 end |
68 end |
69 |
70 # Just like File.expand_path, but for windows systems it |
71 # capitalizes the drive name and ensures backslashes are used |
72 def normalize_path(path, *dirs) |
73 path = File.expand_path(path, *dirs) |
74 if win_os? |
75 path.gsub!('/', '\\').gsub!(/^[a-zA-Z]+:/) { |s| s.upcase } |
76 else |
77 path |
78 end |
79 end |
80 |
81 # Return the timestamp of file, without having to create a file task |
82 def timestamp(file) |
83 if File.exist?(file) |
84 File.mtime(file) |
85 else |
86 Rake::EARLY |
87 end |
88 end |
89 |
90 # Return the path to the first argument, starting from the path provided by the |
91 # second argument. |
92 # |
93 # For example: |
94 # relative_path('foo/bar', 'foo') |
95 # => 'bar' |
96 # relative_path('foo/bar', 'baz') |
97 # => '../foo/bar' |
98 # relative_path('foo/bar') |
99 # => 'foo/bar' |
100 # relative_path('/foo/bar', 'baz') |
101 # => '/foo/bar' |
102 def relative_path(to, from = '.') |
103 to = Pathname.new(to).cleanpath |
104 return to.to_s if from.nil? |
105 to_path = Pathname.new(File.expand_path(to.to_s, "/")) |
106 from_path = Pathname.new(File.expand_path(from.to_s, "/")) |
107 to_path.relative_path_from(from_path).to_s |
108 end |
109 |
110 # Generally speaking, it's not a good idea to operate on dot files (files starting with dot). |
111 # These are considered invisible files (.svn, .hg, .irbrc, etc). Dir.glob/FileList ignore them |
112 # on purpose. There are few cases where we do have to work with them (filter, zip), a better |
113 # solution is welcome, maybe being more explicit with include. For now, this will do. |
114 def recursive_with_dot_files(*dirs) |
115 FileList[dirs.map { |dir| File.join(dir, '/**/{*,.*}') }].reject { |file| File.basename(file) =~ /^[.]{1,2}$/ } |
116 end |
117 |
118 # :call-seq: |
119 # replace_extension(filename) => filename_with_updated_extension |
120 # |
121 # Replace the file extension, e.g., |
122 # replace_extension("foo.zip", "txt") => "foo.txt" |
123 def replace_extension(filename, new_ext) |
124 ext = File.extname(filename) |
125 if filename =~ /\.$/ |
126 filename + new_ext |
127 elsif ext == "" |
128 filename + "." + new_ext |
129 else |
130 filename[0..-ext.length] + new_ext |
131 end |
132 end |
133 |
134 # Utility methods for running gem commands |
135 module Gems #:nodoc: |
136 extend self |
137 |
138 # Install gems specified by each Gem::Dependency if they are missing. This method prompts the user |
139 # for permission before installing anything. |
140 # |
141 # Returns the installed Gem::Dependency objects or fails if permission not granted or when buildr |
142 # is not running interactively (on a tty) |
143 def install(*dependencies) |
144 raise ArgumentError, "Expected at least one argument" if dependencies.empty? |
145 not_found_deps = [] |
146 to_install = [] |
147 remote = dependencies.each do |dep| |
148 if spec = Gem.source_index.search(dep).last |
149 # Found in local repo |
150 to_install << spec |
151 elsif (spec = Gem::SpecFetcher.fetcher.fetch(dep, true).map { |spec, source| spec }.last) |
152 # Found in remote repo |
153 to_install << spec |
154 else |
155 # Not found anywhere |
156 not_found_deps << "#{dep.name} #{dep.requirement}" |
157 end |
158 end |
159 fail Gem::LoadError, "Build requires the gems #{not_found_deps.join(', ')}, which cannot be found in local or remote repository." unless not_found_deps.empty? |
160 uses = "This build requires the gems #{to_install.map(&:full_name).join(', ')}:" |
161 fail Gem::LoadError, "#{uses} to install, run Buildr interactively." unless $stdout.isatty |
162 unless agree("#{uses} do you want me to install them? [Y/n]", true) |
163 fail Gem::LoadError, 'Cannot build without these gems.' |
164 end |
165 to_install.each do |spec| |
166 say "Installing #{spec.full_name} ... " if verbose |
167 command 'install', spec.name, '-v', spec.version.to_s, :verbose => false |
168 Gem.source_index.load_gems_in Gem::SourceIndex.installed_spec_directories |
169 end |
170 to_install |
171 end |
172 |
173 # Execute a GemRunner command |
174 def command(cmd, *args) |
175 options = Hash === args.last ? args.pop : {} |
176 gem_home = ENV['GEM_HOME'] || Gem.path.find { |f| File.writable?(f) } |
177 options[:sudo] = :root unless Util.win_os? || gem_home |
178 options[:command] = 'gem' |
179 args << options |
180 args.unshift '-i', gem_home if cmd == 'install' && gem_home && !args.any?{ |a| a[/-i|--install-dir/] } |
181 Util.ruby cmd, *args |
182 end |
183 |
184 end # Gems |
185 |
186 end # Util |
187 end |
188 |
189 |
190 class Object #:nodoc: |
191 unless defined? instance_exec # 1.9 |
192 module InstanceExecMethods #:nodoc: |
193 end |
194 include InstanceExecMethods |
195 |
196 # Evaluate the block with the given arguments within the context of |
197 # this object, so self is set to the method receiver. |
198 # |
199 # From Mauricio's http://eigenclass.org/hiki/bounded+space+instance_exec |
200 def instance_exec(*args, &block) |
201 begin |
202 old_critical, Thread.critical = Thread.critical, true |
203 n = 0 |
204 n += 1 while respond_to?(method_name = "__instance_exec#{n}") |
205 InstanceExecMethods.module_eval { define_method(method_name, &block) } |
206 ensure |
207 Thread.critical = old_critical |
208 end |
209 |
210 begin |
211 send(method_name, *args) |
212 ensure |
213 InstanceExecMethods.module_eval { remove_method(method_name) } rescue nil |
214 end |
215 end |
216 end |
217 end |
218 |
219 module Kernel #:nodoc: |
220 unless defined? tap # 1.9 |
221 def tap |
222 yield self if block_given? |
223 self |
224 end |
225 end |
226 end |
227 |
228 class Symbol #:nodoc: |
229 unless defined? to_proc # 1.9 |
230 # Borrowed from Ruby 1.9. |
231 def to_proc |
232 Proc.new{|*args| args.shift.__send__(self, *args)} |
233 end |
234 end |
235 end |
236 |
237 unless defined? BasicObject # 1.9 |
238 class BasicObject #:nodoc: |
239 (instance_methods - ['__send__', '__id__', '==', 'send', 'send!', 'respond_to?', 'equal?', 'object_id']). |
240 each do |method| |
241 undef_method method |
242 end |
243 |
244 def self.ancestors |
245 [Kernel] |
246 end |
247 end |
248 end |
249 |
250 |
251 class OpenObject < Hash |
252 |
253 def initialize(source=nil, &block) |
254 super &block |
255 update source if source |
256 end |
257 |
258 def method_missing(symbol, *args) |
259 if symbol.to_s =~ /=$/ |
260 self[symbol.to_s[0..-2].to_sym] = args.first |
261 else |
262 self[symbol] |
263 end |
264 end |
265 end |
266 |
267 |
268 class Hash |
269 |
270 class << self |
271 |
272 # :call-seq: |
273 # Hash.from_java_properties(string) |
274 # |
275 # Returns a hash from a string in the Java properties file format. For example: |
276 # str = 'foo=bar\nbaz=fab' |
277 # Hash.from_properties(str) |
278 # => { 'foo'=>'bar', 'baz'=>'fab' }.to_properties |
279 def from_java_properties(string) |
280 hash = {} |
281 input_stream = Java.java.io.StringBufferInputStream.new(string) |
282 java_properties = Java.java.util.Properties.new |
283 java_properties.load input_stream |
284 keys = java_properties.keySet.iterator |
285 while keys.hasNext |
286 # Calling key.next in JRuby returns a java.lang.String, behaving as a Ruby string and life is good. |
287 # MRI, unfortunately, treats next() like the interface says returning an object that's not a String, |
288 # and the Hash doesn't work the way we need it to. Unfortunately, we can call toString on MRI's object, |
289 # but not on the JRuby one; calling to_s on the JRuby object returns what we need, but ... you guessed it. |
290 # So this seems like the one hack to unite them both. |
291 #key = Java.java.lang.String.valueOf(keys.next.to_s) |
292 key = keys.next |
293 key = key.toString unless String === key |
294 hash[key] = java_properties.getProperty(key) |
295 end |
296 hash |
297 end |
298 |
299 end |
300 |
301 # :call-seq: |
302 # only(keys*) => hash |
303 # |
304 # Returns a new hash with only the specified keys. |
305 # |
306 # For example: |
307 # { :a=>1, :b=>2, :c=>3, :d=>4 }.only(:a, :c) |
308 # => { :a=>1, :c=>3 } |
309 def only(*keys) |
310 keys.inject({}) { |hash, key| has_key?(key) ? hash.merge(key=>self[key]) : hash } |
311 end |
312 |
313 |
314 # :call-seq: |
315 # except(keys*) => hash |
316 # |
317 # Returns a new hash without the specified keys. |
318 # |
319 # For example: |
320 # { :a=>1, :b=>2, :c=>3, :d=>4 }.except(:a, :c) |
321 # => { :b=>2, :d=>4 } |
322 def except(*keys) |
323 (self.keys - keys).inject({}) { |hash, key| hash.merge(key=>self[key]) } |
324 end |
325 |
326 # :call-seq: |
327 # to_java_properties => string |
328 # |
329 # Convert hash to string format used for Java properties file. For example: |
330 # { 'foo'=>'bar', 'baz'=>'fab' }.to_properties |
331 # => foo=bar |
332 # baz=fab |
333 def to_java_properties |
334 keys.sort.map { |key| |
335 value = self[key].gsub(/[\t\r\n\f\\]/) { |escape| "\\" + {"\t"=>"t", "\r"=>"r", "\n"=>"n", "\f"=>"f", "\\"=>"\\"}[escape] } |
336 "#{key}=#{value}" |
337 }.join("\n") |
338 end |
339 |
340 end |
341 |
342 if Buildr::Util.java_platform? |
343 require 'ffi' |
344 |
345 # Workaround for BUILDR-535: when requiring 'ffi', JRuby defines an :error |
346 # method with arity 0. |
347 class Module |
348 remove_method :error if method_defined?(:error) |
349 end |
350 |
351 # Fix for BUILDR-292. |
352 # JRuby fails to rename a file on different devices |
353 # this monkey-patch wont be needed when JRUBY-3381 gets resolved. |
354 module FileUtils #:nodoc: |
355 alias_method :__mv_native, :mv |
356 |
357 def mv(from, to, options = nil) |
358 dir_to = File.directory?(to) ? to : File.dirname(to) |
359 Array(from).each do |from| |
360 dir_from = File.dirname(from) |
361 if File.stat(dir_from).dev != File.stat(dir_to).dev |
362 cp from, to, options |
363 rm from, options |
364 else |
365 __mv_native from, to, options |
366 end |
367 end |
368 end |
369 private :mv |
370 end |
371 |
372 module RakeFileUtils #:nodoc: |
373 def rake_merge_option(args, defaults) |
374 defaults[:verbose] = false if defaults[:verbose] == :default |
375 |
376 if Hash === args.last |
377 defaults.update(args.last) |
378 args.pop |
379 end |
380 args.push defaults |
381 args |
382 end |
383 private :rake_merge_option |
384 end |
385 |
386 module Buildr |
387 class ProcessStatus |
388 attr_reader :pid, :termsig, :stopsig, :exitstatus |
389 |
390 def initialize(pid, success, exitstatus) |
391 @pid = pid |
392 @success = success |
393 @exitstatus = exitstatus |
394 |
395 @termsig = nil |
396 @stopsig = nil |
397 end |
398 |
399 def &(num) |
400 pid & num |
401 end |
402 |
403 def ==(other) |
404 pid == other.pid |
405 end |
406 |
407 def >>(num) |
408 pid >> num |
409 end |
410 |
411 def coredump? |
412 false |
413 end |
414 |
415 def exited? |
416 true |
417 end |
418 |
419 def stopped? |
420 false |
421 end |
422 |
423 def success? |
424 @success |
425 end |
426 |
427 def to_i |
428 pid |
429 end |
430 |
431 def to_int |
432 pid |
433 end |
434 |
435 def to_s |
436 pid.to_s |
437 end |
438 end |
439 end |
440 |
441 module FileUtils |
442 extend FFI::Library |
443 |
444 ffi_lib FFI::Platform::LIBC |
445 |
446 alias_method :__jruby_system__, :system |
447 attach_function :system, [:string], :int |
448 alias_method :__native_system__, :system |
449 alias_method :system, :__jruby_system__ |
450 |
451 # code "borrowed" directly from Rake |
452 def sh(*cmd, &block) |
453 options = (Hash === cmd.last) ? cmd.pop : {} |
454 unless block_given? |
455 show_command = cmd.join(" ") |
456 show_command = show_command[0,42] + "..." |
457 |
458 block = lambda { |ok, status| |
459 ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]" |
460 } |
461 end |
462 if RakeFileUtils.verbose_flag == :default |
463 options[:verbose] = false |
464 else |
465 options[:verbose] ||= RakeFileUtils.verbose_flag |
466 end |
467 options[:noop] ||= RakeFileUtils.nowrite_flag |
468 rake_check_options options, :noop, :verbose |
469 rake_output_message cmd.join(" ") if options[:verbose] |
470 unless options[:noop] |
471 if Buildr::Util.win_os? |
472 # Ruby uses forward slashes regardless of platform, |
473 # unfortunately cd c:/some/path fails on Windows |
474 pwd = Dir.pwd.gsub(%r{/}, '\\') |
475 cd = "cd /d \"#{pwd}\" && " |
476 else |
477 cd = "cd '#{Dir.pwd}' && " |
478 end |
479 args = if cmd.size > 1 then cmd[1..cmd.size] else [] end |
480 |
481 res = if Buildr::Util.win_os? && cmd.size == 1 |
482 __native_system__("#{cd} call #{cmd.first}") |
483 else |
484 arg_str = args.map { |a| "'#{a}'" } |
485 __native_system__(cd + cmd.first + ' ' + arg_str.join(' ')) |
486 end |
487 status = Buildr::ProcessStatus.new(0, res == 0, res) # KLUDGE |
488 block.call(res == 0, status) |
489 end |
490 end |
491 |
492 end |
493 else |
494 module FileUtils |
495 # code "borrowed" directly from Rake |
496 def sh(*cmd, &block) |
497 options = (Hash === cmd.last) ? cmd.pop : {} |
498 unless block_given? |
499 show_command = cmd.join(" ") |
500 show_command = show_command[0,42] + "..." |
501 |
502 block = lambda { |ok, status| |
503 ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]" |
504 } |
505 end |
506 if RakeFileUtils.verbose_flag == :default |
507 options[:verbose] = false |
508 else |
509 options[:verbose] ||= RakeFileUtils.verbose_flag |
510 end |
511 options[:noop] ||= RakeFileUtils.nowrite_flag |
512 rake_check_options options, :noop, :verbose |
513 rake_output_message cmd.join(" ") if options[:verbose] |
514 unless options[:noop] |
515 if Buildr::Util.win_os? |
516 # Ruby uses forward slashes regardless of platform, |
517 # unfortunately cd c:/some/path fails on Windows |
518 pwd = Dir.pwd.gsub(%r{/}, '\\') |
519 cd = "cd /d \"#{pwd}\" && " |
520 else |
521 cd = "cd '#{Dir.pwd}' && " |
522 end |
523 |
524 args = if cmd.size > 1 then cmd[1..cmd.size] else [] end |
525 |
526 res = if Buildr::Util.win_os? && cmd.size == 1 |
527 system("#{cd} call #{cmd.first}") |
528 else |
529 arg_str = args.map { |a| "'#{a}'" } |
530 system(cd + cmd.first + ' ' + arg_str.join(' ')) |
531 end |
532 |
533 block.call(res, $?) |
534 end |
535 end |
536 end |
537 end |
Generated on 2011-07-06 23:35:37 -0700 with rcov 0.9.8