[前][次][番号順一覧][スレッド一覧]

ruby-changes:2893

From: ko1@a...
Date: 21 Dec 2007 12:23:00 +0900
Subject: [ruby-changes:2893] jim - Ruby:r14385 (trunk): Added Rake 0.8.0

jim	2007-12-21 12:22:43 +0900 (Fri, 21 Dec 2007)

  New Revision: 14385

  Added directories:
    trunk/lib/rake/
    trunk/lib/rake/loaders/
  Added files:
    trunk/bin/rake
    trunk/lib/rake/classic_namespace.rb
    trunk/lib/rake/clean.rb
    trunk/lib/rake/gempackagetask.rb
    trunk/lib/rake/loaders/makefile.rb
    trunk/lib/rake/packagetask.rb
    trunk/lib/rake/rake_test_loader.rb
    trunk/lib/rake/rdoctask.rb
    trunk/lib/rake/ruby182_test_unit_fix.rb
    trunk/lib/rake/runtest.rb
    trunk/lib/rake/tasklib.rb
    trunk/lib/rake/testtask.rb
    trunk/lib/rake.rb

  Log:
    Added Rake 0.8.0
  Added: trunk/lib/rake/

  Added: trunk/lib/rake/loaders/

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/clean.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/rake_test_loader.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/gempackagetask.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/bin/rake?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/bin/rake?r1=14385&r2=14384
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/tasklib.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/packagetask.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake.rb?r1=14385&r2=14384
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/runtest.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/ruby182_test_unit_fix.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/ruby182_test_unit_fix.rb?r1=14385&r2=14384
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/classic_namespace.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/rdoctask.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/loaders/makefile.rb?revision=14385&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rake/testtask.rb?revision=14385&view=markup

Index: lib/rake/classic_namespace.rb
===================================================================
--- lib/rake/classic_namespace.rb	(revision 0)
+++ lib/rake/classic_namespace.rb	(revision 14385)
@@ -0,0 +1,8 @@
+# The following classes used to be in the top level namespace.
+# Loading this file enables compatibility with older Rakefile that
+# referenced Task from the top level.
+
+Task = Rake::Task
+FileTask = Rake::FileTask
+FileCreationTask = Rake::FileCreationTask
+RakeApp = Rake::Application
Index: lib/rake/packagetask.rb
===================================================================
--- lib/rake/packagetask.rb	(revision 0)
+++ lib/rake/packagetask.rb	(revision 14385)
@@ -0,0 +1,184 @@
+#!/usr/bin/env ruby
+
+# Define a package task libarary to aid in the definition of
+# redistributable package files.
+
+require 'rake'
+require 'rake/tasklib'
+
+module Rake
+
+  # Create a packaging task that will package the project into
+  # distributable files (e.g zip archive or tar files).
+  #
+  # The PackageTask will create the following targets:
+  #
+  # [<b>:package</b>]
+  #   Create all the requested package files.
+  #
+  # [<b>:clobber_package</b>]
+  #   Delete all the package files.  This target is automatically
+  #   added to the main clobber target.
+  #
+  # [<b>:repackage</b>]
+  #   Rebuild the package files from scratch, even if they are not out
+  #   of date.
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tgz"</b>]
+  #   Create a gzipped tar package (if <em>need_tar</em> is true).  
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.gz"</b>]
+  #   Create a gzipped tar package (if <em>need_tar_gz</em> is true).  
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.bz2"</b>]
+  #   Create a bzip2'd tar package (if <em>need_tar_bz2</em> is true).  
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.zip"</b>]
+  #   Create a zip package archive (if <em>need_zip</em> is true).
+  #
+  # Example:
+  #
+  #   Rake::PackageTask.new("rake", "1.2.3") do |p|
+  #     p.need_tar = true
+  #     p.package_files.include("lib/**/*.rb")
+  #   end
+  #
+  class PackageTask < TaskLib
+    # Name of the package (from the GEM Spec).
+    attr_accessor :name
+
+    # Version of the package (e.g. '1.3.2').
+    attr_accessor :version
+
+    # Directory used to store the package files (default is 'pkg').
+    attr_accessor :package_dir
+
+    # True if a gzipped tar file (tgz) should be produced (default is false).
+    attr_accessor :need_tar
+
+    # True if a gzipped tar file (tar.gz) should be produced (default is false).
+    attr_accessor :need_tar_gz
+
+    # True if a bzip2'd tar file (tar.bz2) should be produced (default is false).
+    attr_accessor :need_tar_bz2
+
+    # True if a zip file should be produced (default is false)
+    attr_accessor :need_zip
+
+    # List of files to be included in the package.
+    attr_accessor :package_files
+
+    # Tar command for gzipped or bzip2ed archives.  The default is 'tar'.
+    attr_accessor :tar_command
+
+    # Zip command for zipped archives.  The default is 'zip'.
+    attr_accessor :zip_command
+
+    # Create a Package Task with the given name and version. 
+    def initialize(name=nil, version=nil)
+      init(name, version)
+      yield self if block_given?
+      define unless name.nil?
+    end
+
+    # Initialization that bypasses the "yield self" and "define" step.
+    def init(name, version)
+      @name = name
+      @version = version
+      @package_files = Rake::FileList.new
+      @package_dir = 'pkg'
+      @need_tar = false
+      @need_tar_gz = false
+      @need_tar_bz2 = false
+      @need_zip = false
+      @tar_command = 'tar'
+      @zip_command = 'zip'
+    end
+
+    # Create the tasks defined by this task library.
+    def define
+      fail "Version required (or :noversion)" if @version.nil?
+      @version = nil if :noversion == @version
+
+      desc "Build all the packages"
+      task :package
+      
+      desc "Force a rebuild of the package files"
+      task :repackage => [:clobber_package, :package]
+      
+      desc "Remove package products" 
+      task :clobber_package do
+        rm_r package_dir rescue nil
+      end
+
+      task :clobber => [:clobber_package]
+
+      [
+        [need_tar, tgz_file, "z"],
+        [need_tar_gz, tar_gz_file, "z"],
+        [need_tar_bz2, tar_bz2_file, "j"]
+      ].each do |(need, file, flag)|
+        if need
+          task :package => ["#{package_dir}/#{file}"]
+          file "#{package_dir}/#{file}" => [package_dir_path] + package_files do
+            chdir(package_dir) do
+              sh %{#{@tar_command} #{flag}cvf #{file} #{package_name}}
+            end
+          end
+        end
+      end
+      
+      if need_zip
+        task :package => ["#{package_dir}/#{zip_file}"]
+        file "#{package_dir}/#{zip_file}" => [package_dir_path] + package_files do
+          chdir(package_dir) do
+            sh %{#{@zip_command} -r #{zip_file} #{package_name}}
+          end
+        end
+      end
+
+      directory package_dir
+
+      file package_dir_path => @package_files do
+        mkdir_p package_dir rescue nil
+        @package_files.each do |fn|
+          f = File.join(package_dir_path, fn)
+          fdir = File.dirname(f)
+          mkdir_p(fdir) if !File.exist?(fdir)
+          if File.directory?(fn)
+            mkdir_p(f)
+          else
+            rm_f f
+            safe_ln(fn, f)
+          end
+        end
+      end
+      self
+    end
+
+    def package_name
+      @version ? "#{@name}-#{@version}" : @name
+    end
+      
+    def package_dir_path
+      "#{package_dir}/#{package_name}"
+    end
+
+    def tgz_file
+      "#{package_name}.tgz"
+    end
+
+    def tar_gz_file
+      "#{package_name}.tar.gz"
+    end
+
+    def tar_bz2_file
+      "#{package_name}.tar.bz2"
+    end
+
+    def zip_file
+      "#{package_name}.zip"
+    end
+  end
+
+end
Index: lib/rake/ruby182_test_unit_fix.rb
===================================================================
--- lib/rake/ruby182_test_unit_fix.rb	(revision 0)
+++ lib/rake/ruby182_test_unit_fix.rb	(revision 14385)
@@ -0,0 +1,23 @@
+module Test
+  module Unit
+    module Collector
+      class Dir
+        undef collect_file
+        def collect_file(name, suites, already_gathered)
+          # loadpath = $:.dup
+          dir = File.dirname(File.expand_path(name))
+          $:.unshift(dir) unless $:.first == dir
+          if(@req)
+            @req.require(name)
+          else
+            require(name)
+          end
+          find_test_cases(already_gathered).each{|t| add_suite(suites, t.suite)}
+        ensure
+          # $:.replace(loadpath)
+          $:.delete_at $:.rindex(dir)
+        end
+      end
+    end
+  end
+end

Property changes on: lib/rake/ruby182_test_unit_fix.rb
___________________________________________________________________
Name: svn:executable
   + *

Index: lib/rake/clean.rb
===================================================================
--- lib/rake/clean.rb	(revision 0)
+++ lib/rake/clean.rb	(revision 14385)
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+
+# The 'rake/clean' file defines two file lists (CLEAN and CLOBBER) and
+# two rake tasks (:clean and :clobber).
+#
+# [:clean] Clean up the project by deleting scratch files and backup
+#          files.  Add files to the CLEAN file list to have the :clean
+#          target handle them.
+#
+# [:clobber] Clobber all generated and non-source files in a project.
+#            The task depends on :clean, so all the clean files will
+#            be deleted as well as files in the CLOBBER file list.
+#            The intent of this task is to return a project to its
+#            pristine, just unpacked state.
+
+require 'rake'
+
+CLEAN = Rake::FileList["**/*~", "**/*.bak", "**/core"]
+CLEAN.clear_exclude.exclude { |fn| 
+  fn.pathmap("%f") == 'core' && File.directory?(fn) 
+}
+
+desc "Remove any temporary products."
+task :clean do
+  CLEAN.each { |fn| rm_r fn rescue nil }
+end
+
+CLOBBER = Rake::FileList.new
+
+desc "Remove any generated file."
+task :clobber => [:clean] do
+  CLOBBER.each { |fn| rm_r fn rescue nil }
+end
Index: lib/rake/testtask.rb
===================================================================
--- lib/rake/testtask.rb	(revision 0)
+++ lib/rake/testtask.rb	(revision 14385)
@@ -0,0 +1,161 @@
+#!/usr/bin/env ruby
+
+# Define a task library for running unit tests.
+
+require 'rake'
+require 'rake/tasklib'
+
+module Rake
+
+  # Create a task that runs a set of tests.
+  #
+  # Example:
+  #  
+  #   Rake::TestTask.new do |t|
+  #     t.libs << "test"
+  #     t.test_files = FileList['test/test*.rb']
+  #     t.verbose = true
+  #   end
+  #
+  # If rake is invoked with a "TEST=filename" command line option,
+  # then the list of test files will be overridden to include only the
+  # filename specified on the command line.  This provides an easy way
+  # to run just one test.
+  #
+  # If rake is invoked with a "TESTOPTS=options" command line option,
+  # then the given options are passed to the test process after a
+  # '--'.  This allows Test::Unit options to be passed to the test
+  # suite.
+  #
+  # Examples:
+  #
+  #   rake test                           # run tests normally
+  #   rake test TEST=just_one_file.rb     # run just one test file.
+  #   rake test TESTOPTS="-v"             # run in verbose mode
+  #   rake test TESTOPTS="--runner=fox"   # use the fox test runner
+  #
+  class TestTask < TaskLib
+
+    # Name of test task. (default is :test)
+    attr_accessor :name
+
+    # List of directories to added to $LOAD_PATH before running the
+    # tests. (default is 'lib')
+    attr_accessor :libs
+
+    # True if verbose test output desired. (default is false)
+    attr_accessor :verbose
+
+    # Test options passed to the test suite.  An explicit
+    # TESTOPTS=opts on the command line will override this. (default
+    # is NONE)
+    attr_accessor :options
+
+    # Request that the tests be run with the warning flag set.
+    # E.g. warning=true implies "ruby -w" used to run the tests.
+    attr_accessor :warning
+
+    # Glob pattern to match test files. (default is 'test/test*.rb')
+    attr_accessor :pattern
+
+    # Style of test loader to use.  Options are:
+    #
+    # * :rake -- Rake provided test loading script (default).
+    # * :testrb -- Ruby provided test loading script.
+    # * :direct -- Load tests using command line loader.
+    # 
+    attr_accessor :loader
+
+    # Array of commandline options to pass to ruby when running test loader.
+    attr_accessor :ruby_opts
+
+    # Explicitly define the list of test files to be included in a
+    # test.  +list+ is expected to be an array of file names (a
+    # FileList is acceptable).  If both +pattern+ and +test_files+ are
+    # used, then the list of test files is the union of the two.
+    def test_files=(list)
+      @test_files = list
+    end
+
+    # Create a testing task.
+    def initialize(name=:test)
+      @name = name
+      @libs = ["lib"]
+      @pattern = nil
+      @options = nil
+      @test_files = nil
+      @verbose = false
+      @warning = false
+      @loader = :rake
+      @ruby_opts = []
+      yield self if block_given?
+      @pattern = 'test/test*.rb' if @pattern.nil? && @test_files.nil?
+      define
+    end
+
+    # Create the tasks defined by this task lib.
+    def define
+      lib_path = @libs.join(File::PATH_SEPARATOR)
+      desc "Run tests" + (@name==:test ? "" : " for #{@name}")
+      task @name do
+        run_code = ''
+        RakeFileUtils.verbose(@verbose) do
+          run_code =
+            case @loader
+            when :direct
+              "-e 'ARGV.each{|f| load f}'"
+            when :testrb
+              "-S testrb #{fix}"
+            when :rake
+              rake_loader
+            end
+          @ruby_opts.unshift( "-I#{lib_path}" )
+          @ruby_opts.unshift( "-w" ) if @warning
+          ruby @ruby_opts.join(" ") +
+            " \"#{run_code}\" " +
+            file_list.collect { |fn| "\"#{fn}\"" }.join(' ') +
+            " #{option_list}"
+        end
+      end
+      self
+    end
+
+    def option_list # :nodoc:
+      ENV['TESTOPTS'] || @options || ""
+    end
+
+    def file_list # :nodoc:
+      if ENV['TEST']
+        FileList[ ENV['TEST'] ]
+      else
+        result = []
+        result += @test_files.to_a if @test_files
+        result += FileList[ @pattern ].to_a if @pattern
+        FileList[result]
+      end
+    end
+
+    def fix # :nodoc:
+      case RUBY_VERSION
+      when '1.8.2'
+        find_file 'rake/ruby182_test_unit_fix'
+      else
+        nil
+      end || ''
+    end
+
+    def rake_loader # :nodoc:
+      find_file('rake/rake_test_loader') or
+        fail "unable to find rake test loader"
+    end
+
+    def find_file(fn) # :nodoc:
+      $LOAD_PATH.each do |path|
+        file_path = File.join(path, "#{fn}.rb")
+        return file_path if File.exist? file_path
+      end
+      nil
+    end
+
+  end
+end
Index: lib/rake/runtest.rb
===================================================================
--- lib/rake/runtest.rb	(revision 0)
+++ lib/rake/runtest.rb	(revision 14385)
@@ -0,0 +1,23 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'test/unit/assertions'
+
+module Rake
+  include Test::Unit::Assertions
+
+  def run_tests(pattern='test/test*.rb', log_enabled=false)
+    Dir["#{pattern}"].each { |fn|
+      puts fn if log_enabled
+      begin
+        load fn
+      rescue Exception => ex
+        puts "Error in #{fn}: #{ex.message}"
+        puts ex.backtrace
+        assert false
+      end
+    }
+  end
+
+  extend self
+end
Index: lib/rake/gempackagetask.rb
===================================================================
--- lib/rake/gempackagetask.rb	(revision 0)
+++ lib/rake/gempackagetask.rb	(revision 14385)
@@ -0,0 +1,103 @@
+#!/usr/bin/env ruby
+
+# Define a package task library to aid in the definition of GEM
+# packages.
+
+require 'rubygems'
+require 'rake'
+require 'rake/packagetask'
+require 'rubygems/user_interaction'
+require 'rubygems/builder'
+
+begin
+  Gem.manage_gems
+rescue NoMethodError => ex
+  # Using rubygems prior to 0.6.1
+end
+
+module Rake
+
+  # Create a package based upon a Gem spec.  Gem packages, as well as
+  # zip files and tar/gzipped packages can be produced by this task.
+  #
+  # In addition to the Rake targets generated by PackageTask, a
+  # GemPackageTask will also generate the following tasks:
+  #
+  # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.gem"</b>]
+  #   Create a Ruby GEM package with the given name and version.
+  #
+  # Example using a Ruby GEM spec:
+  #
+  #   require 'rubygems'
+  #
+  #   spec = Gem::Specification.new do |s|
+  #     s.platform = Gem::Platform::RUBY
+  #     s.summary = "Ruby based make-like utility."
+  #     s.name = 'rake'
+  #     s.version = PKG_VERSION
+  #     s.requirements << 'none'
+  #     s.require_path = 'lib'
+  #     s.autorequire = 'rake'
+  #     s.files = PKG_FILES
+  #     s.description = <<EOF
+  #   Rake is a Make-like program implemented in Ruby. Tasks
+  #   and dependencies are specified in standard Ruby syntax. 
+  #   EOF
+  #   end
+  #   
+  #   Rake::GemPackageTask.new(spec) do |pkg|
+  #     pkg.need_zip = true
+  #     pkg.need_tar = true
+  #   end
+  #
+  class GemPackageTask < PackageTask
+    # Ruby GEM spec containing the metadata for this package.  The
+    # name, version and package_files are automatically determined
+    # from the GEM spec and don't need to be explicitly provided.
+    attr_accessor :gem_spec
+
+    # Create a GEM Package task library.  Automatically define the gem
+    # if a block is given.  If no block is supplied, then +define+
+    # needs to be called to define the task.
+    def initialize(gem_spec)
+      init(gem_spec)
+      yield self if block_given?
+      define if block_given?
+    end
+
+    # Initialization tasks without the "yield self" or define
+    # operations.
+    def init(gem)
+      super(gem.name, gem.version)
+      @gem_spec = gem
+      @package_files += gem_spec.files if gem_spec.files
+    end
+
+    # Create the Rake tasks and actions specified by this
+    # GemPackageTask.  (+define+ is automatically called if a block is
+    # given to +new+).
+    def define
+      super
+      task :package => [:gem]
+      desc "Build the gem file #{gem_file}"
+      task :gem => ["#{package_dir}/#{gem_file}"]
+      file "#{package_dir}/#{gem_file}" => [package_dir] + @gem_spec.files do
+        when_writing("Creating GEM") {
+          Gem::Builder.new(gem_spec).build
+          verbose(true) {
+            mv gem_file, "#{package_dir}/#{gem_file}"
+          }
+        }
+      end
+    end
+    
+    def gem_file
+      if @gem_spec.platform == Gem::Platform::RUBY
+        "#{package_name}.gem"
+      else
+        "#{package_name}-#{@gem_spec.platform}.gem"
+      end
+    end
+    
+  end
+end
Index: lib/rake/loaders/makefile.rb
===================================================================
--- lib/rake/loaders/makefile.rb	(revision 0)
+++ lib/rake/loaders/makefile.rb	(revision 14385)
@@ -0,0 +1,40 @@
+#!/usr/bin/env ruby
+
+module Rake
+
+  # Makefile loader to be used with the import file loader.
+  class MakefileLoader
+
+    # Load the makefile dependencies in +fn+.
+    def load(fn)
+      buffer = ''
+      open(fn) do |mf|
+        mf.each do |line|
+          next if line =~ /^\s*#/
+          buffer << line
+          if buffer =~ /\\$/
+            buffer.sub!(/\\\n/, ' ')
+            state = :append
+          else
+            process_line(buffer)
+            buffer = ''
+          end
+        end
+      end
+      process_line(buffer) if buffer != ''
+    end
+
+    private
+
+    # Process one logical line of makefile data.
+    def process_line(line)
+      file_task, args = line.split(':')
+      return if args.nil?
+      dependents = args.split
+      file file_task => dependents
+    end
+  end
+
+  # Install the handler
+  Rake.application.add_loader('mf', MakefileLoader.new)
+end
Index: lib/rake/rake_test_loader.rb
===================================================================
--- lib/rake/rake_test_loader.rb	(revision 0)
+++ lib/rake/rake_test_loader.rb	(revision 14385)
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+
+# Load the test files from the command line.
+
+ARGV.each { |f| load f unless f =~ /^-/  }
Index: lib/rake/tasklib.rb
===================================================================
--- lib/rake/tasklib.rb	(revision 0)
+++ lib/rake/tasklib.rb	(revision 14385)
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+
+require 'rake'
+
+module Rake
+
+  # Base class for Task Libraries.
+  class TaskLib
+
+    include Cloneable
+
+    # Make a symbol by pasting two strings together. 
+    def paste(a,b)
+      (a.to_s + b.to_s).intern
+    end
+  end
+
+end
Index: lib/rake/rdoctask.rb
===================================================================
--- lib/rake/rdoctask.rb	(revision 0)
+++ lib/rake/rdoctask.rb	(revision 14385)
@@ -0,0 +1,147 @@
+#!/usr/bin/env ruby
+
+require 'rake'
+require 'rake/tasklib'
+
+module Rake
+
+  # Create a documentation task that will generate the RDoc files for
+  # a project.
+  #
+  # The RDocTask will create the following targets:
+  #
+  # [<b><em>rdoc</em></b>]
+  #   Main task for this RDOC task.  
+  #
+  # [<b>:clobber_<em>rdoc</em></b>]
+  #   Delete all the rdoc files.  This target is automatically
+  #   added to the main clobber target.
+  #
+  # [<b>:re<em>rdoc</em></b>]
+  #   Rebuild the rdoc files from scratch, even if they are not out
+  #   of date.
+  #
+  # Simple Example:
+  #
+  #   Rake::RDocTask.new do |rd|
+  #     rd.main = "README.rdoc"
+  #     rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+  #   end
+  #
+  # You may wish to give the task a different name, such as if you are
+  # generating two sets of documentation.  For instance, if you want to have a
+  # development set of documentation including private methods:
+  #
+  #   Rake::RDocTask.new(:rdoc_dev) do |rd|
+  #     rd.main = "README.doc"
+  #     rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+  #     rd.options << "--all"
+  #   end
+  #
+  # The tasks would then be named :<em>rdoc_dev</em>, :clobber_<em>rdoc_dev</em>, and
+  # :re<em>rdoc_dev</em>.
+  #
+  class RDocTask < TaskLib
+    # Name of the main, top level task.  (default is :rdoc)
+    attr_accessor :name
+
+    # Name of directory to receive the html output files. (default is "html")
+    attr_accessor :rdoc_dir
+
+    # Title of RDoc documentation. (default is none)
+    attr_accessor :title
+
+    # Name of file to be used as the main, top level file of the
+    # RDoc. (default is none)
+    attr_accessor :main
+
+    # Name of template to be used by rdoc. (default is 'html')
+    attr_accessor :template
+
+    # List of files to be included in the rdoc generation. (default is [])
+    attr_accessor :rdoc_files
+
+    # List of options to be passed rdoc.  (default is [])
+    attr_accessor :options
+
+    # Run the rdoc process as an external shell (default is false)
+    attr_accessor :external
+
+    # Create an RDoc task named <em>rdoc</em>.  Default task name is +rdoc+.
+    def initialize(name=:rdoc)  # :yield: self
+      @name = name
+      @rdoc_files = Rake::FileList.new
+      @rdoc_dir = 'html'
+      @main = nil
+      @title = nil
+      @template = 'html'
+      @external = false
+      @options = []
+      yield self if block_given?
+      define
+    end
+    
+    # Create the tasks defined by this task lib.
+    def define
+      if name.to_s != "rdoc"
+        desc "Build the RDOC HTML Files"
+      end
+
+      desc "Build the #{name} HTML Files"
+      task name
+      
+      desc "Force a rebuild of the RDOC files"
+      task paste("re", name) => [paste("clobber_", name), name]
+      
+      desc "Remove rdoc products" 
+      task paste("clobber_", name) do
+        rm_r rdoc_dir rescue nil
+      end
+
+      task :clobber => [paste("clobber_", name)]
+      
+      directory @rdoc_dir
+      task name => [rdoc_target]
+      file rdoc_target => @rdoc_files + [$rakefile] do
+        rm_r @rdoc_dir rescue nil
+        args = option_list + @rdoc_files
+        if @external
+          argstring = args.join(' ')
+          sh %{ruby -Ivendor vender/rd #{argstring}}
+        else
+          require 'rdoc/rdoc'
+          RDoc::RDoc.new.document(args)
+        end
+      end
+      self
+    end
+
+    def option_list
+      result = @options.dup
+      result << "-o" << @rdoc_dir
+      result << "--main" << quote(main) if main
+      result << "--title" << quote(title) if title
+      result << "-T" << quote(template) if template
+      result
+    end
+
+    def quote(str)
+      if @external
+        "'#{str}'"
+      else
+        str
+      end
+    end
+
+    def option_string
+      option_list.join(' ')
+    end
+
+    private
+
+    def rdoc_target
+      "#{rdoc_dir}/index.html"
+    end
+
+  end
+end
Index: lib/rake.rb
===================================================================
--- lib/rake.rb	(revision 0)
+++ lib/rake.rb	(revision 14385)
@@ -0,0 +1,2241 @@
+#!/usr/bin/env ruby
+
+#--
+
+# Copyright (c) 2003, 2004, 2005, 2006, 2007  Jim Weirich
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+#++
+#
+# = Rake -- Ruby Make
+#
+# This is the main file for the Rake application.  Normally it is referenced
+# as a library via a require statement, but it can be distributed
+# independently as an application.
+
+RAKEVERSION = '0.8.0'
+
+require 'rbconfig'
+require 'ftools'
+require 'getoptlong'
+require 'fileutils'
+require 'singleton'
+require 'thread'
+require 'ostruct'
+
+######################################################################
+# Rake extensions to Module.
+#
+class Module
+  # Check for an existing method in the current class before extending.  IF
+  # the method already exists, then a warning is printed and the extension is
+  # not added.  Otherwise the block is yielded and any definitions in the
+  # block will take effect.
+  #
+  # Usage:
+  #
+  #   class String
+  #     rake_extension("xyz") do
+  #       def xyz
+  #         ...
+  #       end
+  #     end
+  #   end
+  #
+  def rake_extension(method)
+    if instance_methods.include?(method.to_s) || instance_methods.include?(method.to_sym)
+      $stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists"
+    else
+      yield
+    end
+  end
+end # module Module
+
+
+######################################################################
+# User defined methods to be added to String.
+#
+class String
+  rake_extension("ext") do
+    # Replace the file extension with +newext+.  If there is no extenson on
+    # the string, append the new extension to the end.  If the new extension
+    # is not given, or is the empty string, remove any existing extension.
+    #
+    # +ext+ is a user added method for the String class.
+    def ext(newext='')
+      return self.dup if ['.', '..'].include? self
+      if newext != ''
+        newext = (newext =~ /^\./) ? newext : ("." + newext)
+      end
+      dup.sub!(%r(([^/\\])\.[^./\\]*$)) { $1 + newext } || self + newext
+    end
+  end
+
+  rake_extension("pathmap") do
+    # Explode a path into individual components.  Used by +pathmap+.
+    def pathmap_explode
+      head, tail = File.split(self)
+      return [self] if head == self
+      return [tail] if head == '.' || tail == '/'
+      return [head, tail] if head == '/'
+      return head.pathmap_explode + [tail]
+    end
+    protected :pathmap_explode
+
+    # Extract a partial path from the path.  Include +n+ directories from the
+    # front end (left hand side) if +n+ is positive.  Include |+n+|
+    # directories from the back end (right hand side) if +n+ is negative.
+    def pathmap_partial(n)
+      dirs = File.dirname(self).pathmap_explode
+      partial_dirs =
+        if n > 0
+          dirs[0...n]
+        elsif n < 0
+          dirs.reverse[0...-n].reverse
+        else
+          "."
+        end
+      File.join(partial_dirs)
+    end
+    protected :pathmap_partial
+      
+    # Preform the pathmap replacement operations on the given path. The
+    # patterns take the form 'pat1,rep1;pat2,rep2...'.
+    def pathmap_replace(patterns, &block)
+      result = self
+      patterns.split(';').each do |pair|
+        pattern, replacement = pair.split(',')
+        pattern = Regexp.new(pattern)
+        if replacement == '*' && block_given?
+          result = result.sub(pattern, &block)
+        elsif replacement
+          result = result.sub(pattern, replacement)
+        else
+          result = result.sub(pattern, '')
+        end
+      end
+      result
+    end
+    protected :pathmap_replace
+
+    # Map the path according to the given specification.  The specification
+    # controls the details of the mapping.  The following special patterns are
+    # recognized:
+    #
+    # * <b>%p</b> -- The complete path.
+    # * <b>%f</b> -- The base file name of the path, with its file extension,
+    #   but without any directories.
+    # * <b>%n</b> -- The file name of the path without its file extension.
+    # * <b>%d</b> -- The directory list of the path.
+    # * <b>%x</b> -- The file extension of the path.  An empty string if there
+    #   is no extension.
+    # * <b>%X</b> -- Everything *but* the file extension.
+    # * <b>%s</b> -- The alternate file separater if defined, otherwise use
+    #   the standard file separator.
+    # * <b>%%</b> -- A percent sign.
+    #
+    # The %d specifier can also have a numeric prefix (e.g. '%2d'). If the
+    # number is positive, only return (up to) +n+ directories in the path,
+    # starting from the left hand side.  If +n+ is negative, return (up to)
+    # |+n+| directories from the right hand side of the path.
+    #
+    # Examples:
+    #
+    #   'a/b/c/d/file.txt'.pathmap("%2d")   => 'a/b'
+    #   'a/b/c/d/file.txt'.pathmap("%-2d")  => 'c/d'
+    #
+    # Also the %d, %p, $f, $n, %x, and %X operators can take a
+    # pattern/replacement argument to perform simple string substititions on a
+    # particular part of the path.  The pattern and replacement are speparated
+    # by a comma and are enclosed by curly braces.  The replacement spec comes
+    # after the % character but before the operator letter.  (e.g.
+    # "%{old,new}d").  Muliple replacement specs should be separated by
+    # semi-colons (e.g. "%{old,new;src,bin}d").
+    #
+    # Regular expressions may be used for the pattern, and back refs may be
+    # used in the replacement text.  Curly braces, commas and semi-colons are
+    # excluded from both the pattern and replacement text (let's keep parsing
+    # reasonable).
+    #
+    # For example:
+    #
+    #    "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class")
+    #
+    # returns:
+    #
+    #    "bin/org/onestepback/proj/A.class"
+    #
+    # If the replacement text is '*', then a block may be provided to perform
+    # some arbitrary calculation for the replacement.
+    #
+    # For example:
+    #
+    #   "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext|
+    #      ext.downcase
+    #   }
+    #
+    # Returns:
+    #
+    #  "/path/to/file.txt"
+    #
+    def pathmap(spec=nil, &block)
+      return self if spec.nil?
+      result = ''
+      spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
+        case frag
+        when '%f'
+          result << File.basename(self)
+        when '%n'
+          result << File.basename(self).ext
+        when '%d'
+          result << File.dirname(self)
+        when '%x'
+          result << $1 if self =~ /[^\/](\.[^.]+)$/
+        when '%X'
+          if self =~ /^(.*[^\/])(\.[^.]+)$/
+            result << $1
+          else
+            result << self
+          end
+        when '%p'
+          result << self
+        when '%s'
+          result << (File::ALT_SEPARATOR || File::SEPARATOR)
+        when '%-'
+          # do nothing
+        when '%%'
+          result << "%"
+        when /%(-?\d+)d/
+          result << pathmap_partial($1.to_i)
+        when /^%\{([^}]*)\}(\d*[dpfnxX])/
+          patterns, operator = $1, $2
+          result << pathmap('%' + operator).pathmap_replace(patterns, &block)
+        when /^%/
+          fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
+        else
+          result << frag
+        end
+      end
+      result
+    end
+  end
+end # class String
+
+##############################################################################
+module Rake
+
+  # --------------------------------------------------------------------------
+  # Rake module singleton methods.
+  #
+  class << self
+    # Current Rake Application
+    def application
+      @application ||= Rake::Application.new
+    end
+
+    # Set the current Rake application object.
+    def application=(app)
+      @application = app
+    end
+
+    # Return the original directory where the Rake application was started.
+    def original_dir
+      application.original_dir
+    end
+
+  end
+
+  # ##########################################################################
+  # Mixin for creating easily cloned objects.
+  #
+  module Cloneable
+    # Clone an object by making a new object and setting all the instance
+    # variables to the same values.
+    def clone
+      sibling = self.class.new
+      instance_variables.each do |ivar|
+        value = self.instance_variable_get(ivar)
+        new_value = value.clone rescue value
+        sibling.instance_variable_set(ivar, new_value)
+      end
+      sibling
+    end
+    alias dup clone
+  end
+
+  ####################################################################
+  # TaskAguments manage the arguments passed to a task.
+  #
+  class TaskArguments
+    include Enumerable
+
+    attr_reader :names
+
+    def initialize(names, values, parent=nil)
+      @names = names
+      @parent = parent
+      @hash = {}
+      names.each_with_index { |name, i|
+        @hash[name.to_sym] = values[i]
+      }
+    end
+
+    # Create a new argument scope using the prerequisite argument
+    # names.
+    def new_scope(names)
+      values = names.collect { |n| self[n] }
+      self.class.new(names, values, self)
+    end
+
+    # Find an argument value by name or index.
+    def [](index)
+      lookup(index.to_sym)
+    end
+
+    def each(&block)
+      @hash.each(&block)
+    end
+
+    def method_missing(sym, *args, &block)
+      lookup(sym.to_sym)
+    end
+
+    def to_hash
+      @hash
+    end
+
+    def to_s
+      @hash.inspect
+    end
+
+    def inspect
+      to_s
+    end
+    
+    protected
+    
+    def lookup(name)
+      if @hash.has_key?(name)
+        @hash[name]
+      elsif ENV.has_key?(name.to_s)
+        ENV[name.to_s]
+      elsif ENV.has_key?(name.to_s.upcase)
+        ENV[name.to_s.upcase]
+      elsif @parent
+        @parent.lookup(name)
+      end
+    end
+  end
+
+  ####################################################################
+  # InvocationChain tracks the chain of task invocations to detect
+  # circular dependencies.
+  class InvocationChain
+    def initialize(value, tail)
+      @value = value
+      @tail = tail
+    end
+
+    def member?(obj)
+      @value == obj || @tail.member?(obj)
+    end
+
+    def append(value)
+      if member?(value)
+        fail RuntimeError, "Circular dependency detected: #{to_s} => #{value}"
+      end
+      self.class.new(value, self)
+    end
+
+    def to_s
+      "#{prefix}#{@value}"
+    end
+
+    def self.append(value, chain)
+      chain.append(value)
+    end
+
+    private
+
+    def prefix
+      "#{@tail.to_s} => "
+    end
+
+    class EmptyInvocationChain
+      def member?(obj)
+        false
+      end
+      def append(value)
+        InvocationChain.new(value, self)
+      end
+      def to_s
+        "TOP"
+      end
+    end
+
+    EMPTY = EmptyInvocationChain.new
+
+  end # class InvocationChain
+
+end # module Rake
+
+module Rake
+
+  # #########################################################################
+  # A Task is the basic unit of work in a Rakefile.  Tasks have associated
+  # actions (possibly more than one) and a list of prerequisites.  When
+  # invoked, a task will first ensure that all of its prerequisites have an
+  # opportunity to run and then it will execute its own actions.
+  #
+  # Tasks are not usually created directly using the new method, but rather
+  # use the +file+ and +task+ convenience methods.
+  #
+  class Task
+    # List of prerequisites for a task.
+    attr_reader :prerequisites
+
+    # Application owning this task.
+    attr_accessor :application
+
+    # Comment for this task.  Restricted to a single line of no more than 50
+    # characters.
+    attr_reader :comment
+
+    # Full text of the (possibly multi-line) comment.
+    attr_reader :full_comment
+
+    # Array of nested namespaces names used for task lookup by this task.
+    attr_reader :scope
+
+    # Return task name
+    def to_s
+      name
+    end
+
+    def inspect
+      "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>"
+    end
+
+    # List of sources for task.
+    attr_writer :sources
+    def sources
+      @sources ||= []
+    end
+
+    # First source from a rule (nil if no sources)
+    def source
+      @sources.first if defined?(@sources)
+    end
+
+    # Create a task named +task_name+ with no actions or prerequisites. Use
+    # +enhance+ to add actions and prerequisites.
+    def initialize(task_name, app)
+      @name = task_name.to_s
+      @prerequisites = FileList[]
+      @actions = []
+      @already_invoked = false
+      @full_comment = nil
+      @comment = nil
+      @lock = Mutex.new
+      @application = app
+      @scope = app.current_scope
+      @arg_names = nil
+    end
+
+    # Enhance a task with prerequisites or actions.  Returns self.
+    def enhance(deps=nil, &block)
+      @prerequisites |= deps if deps
+      @actions << block if block_given?
+      self
+    end
+
+    # Name of the task, including any namespace qualifiers.
+    def name
+      @name.to_s
+    end
+
+    # Name of task with argument list description.
+    def name_with_args # :nodoc:
+      if arg_description
+        "#{name}#{arg_description}"
+      else
+        name
+      end
+    end
+
+    # Argument description (nil if none).
+    def arg_description # :nodoc:
+      @arg_names ? "[#{(arg_names || []).join(',')}]" : nil
+    end
+
+    # Name of arguments for this task.
+    def arg_names
+      @arg_names || []
+    end
+
+    # Invoke the task if it is needed.  Prerequites are invoked first.
+    def invoke(*args)
+      task_args = TaskArguments.new(arg_names, args)
+      invoke_with_call_chain(task_args, InvocationChain::EMPTY)
+    end
+
+    # Same as invoke, but explicitly pass a call chain to detect
+    # circular dependencies.
+    def invoke_with_call_chain(task_args, invocation_chain)
+      new_chain = InvocationChain.append(self, invocation_chain)
+      @lock.synchronize do
+        if application.options.trace
+          puts "** Invoke #{name} #{format_trace_flags}"
+        end
+        return if @already_invoked
+        @already_invoked = true
+        invoke_prerequisites(task_args, new_chain)
+        execute(task_args) if needed?
+      end
+    end
+    protected :invoke_with_call_chain
+
+    # Invoke all the prerequisites of a task.
+    def invoke_prerequisites(task_args, invocation_chain)
+      @prerequisites.each { |n|
+        prereq = application[n, @scope]
+        prereq_args = task_args.new_scope(prereq.arg_names)
+        prereq.invoke_with_call_chain(prereq_args, invocation_chain)
+      }
+    end
+
+    # Format the trace flags for display.
+    def format_trace_flags
+      flags = []
+      flags << "first_time" unless @already_invoked
+      flags << "not_needed" unless needed?
+      flags.empty? ? "" : "(" + flags.join(", ") + ")"
+    end
+    private :format_trace_flags
+
+    # Execute the actions associated with this task.
+    def execute(args)
+      if application.options.dryrun
+        puts "** Execute (dry run) #{name}"
+        return
+      end
+      if application.options.trace
+        puts "** Execute #{name}"
+      end
+      application.enhance_with_matching_rule(name) if @actions.empty?
+      @actions.each do |act|
+        case act.arity
+        when 1
+          act.call(self)
+        else
+          act.call(self, args)
+        end
+      end
+    end
+
+    # Is this task needed?
+    def needed?
+      true
+    end
+
+    # Timestamp for this task.  Basic tasks return the current time for their
+    # time stamp.  Other tasks can be more sophisticated.
+    def timestamp
+      @prerequisites.collect { |p| application[p].timestamp }.max || Time.now
+    end
+
+    # Add a description to the task.  The description can consist of an option
+    # argument list (enclosed brackets) and an optional comment.
+    def add_description(description)
+      return if ! description
+      comment = description.strip
+      add_comment(comment) if comment && ! comment.empty?
+    end
+
+    # Writing to the comment attribute is the same as adding a description.
+    def comment=(description)
+      add_description(description)
+    end
+
+    # Add a comment to the task.  If a comment alread exists, separate
+    # the new comment with " / ".
+    def add_comment(comment)
+      if @full_comment
+        @full_comment << " / "
+      else
+        @full_comment = ''
+      end
+      @full_comment << comment
+      if @full_comment =~ /\A([^.]+?\.)( |$)/
+        @comment = $1
+      else
+        @comment = @full_comment
+      end
+    end
+    private :add_comment
+
+    # Set the names of the arguments for this task. +args+ should be
+    # an array of symbols, one for each argument name.
+    def set_arg_names(args)
+      @arg_names = args.map { |a| a.to_sym }
+    end
+
+    # Return a string describing the internal state of a task.  Useful for
+    # debugging.
+    def investigation
+      result = "------------------------------\n"
+      result << "Investigating #{name}\n"
+      result << "class: #{self.class}\n"
+      result <<  "task needed: #{needed?}\n"
+      result <<  "timestamp: #{timestamp}\n"
+      result << "pre-requisites: \n"
+      prereqs = @prerequisites.collect {|name| application[name]}
+      prereqs.sort! {|a,b| a.timestamp <=> b.timestamp}
+      prereqs.each do |p|
+        result << "--#{p.name} (#{p.timestamp})\n"
+      end
+      latest_prereq = @prerequisites.collect{|n| application[n].timestamp}.max
+      result <<  "latest-prerequisite time: #{latest_prereq}\n"
+      result << "................................\n\n"
+      return result
+    end
+
+    # ----------------------------------------------------------------
+    # Rake Module Methods
+    #
+    class << self
+
+      # Clear the task list.  This cause rake to immediately forget all the
+      # tasks that have been assigned.  (Normally used in the unit tests.)
+      def clear
+        Rake.application.clear
+      end
+
+      # List of all defined tasks.
+      def tasks
+        Rake.application.tasks
+      end
+
+      # Return a task with the given name.  If the task is not currently
+      # known, try to synthesize one from the defined rules.  If no rules are
+      # found, but an existing file matches the task name, assume it is a file
+      # task with no dependencies or actions.
+      def [](task_name)
+        Rake.application[task_name]
+      end
+
+      # TRUE if the task name is already defined.
+      def task_defined?(task_name)
+        Rake.application.lookup(task_name) != nil
+      end
+
+      # Define a task given +args+ and an option block.  If a rule with the
+      # given name already exists, the prerequisites and actions are added to
+      # the existing task.  Returns the defined task.
+      def define_task(*args, &block)
+        Rake.application.define_task(self, *args, &block)
+      end
+
+      # Define a rule for synthesizing tasks.
+      def create_rule(*args, &block)
+        Rake.application.create_rule(*args, &block)
+      end
+
+      # Apply the scope to the task name according to the rules for
+      # this kind of task.  Generic tasks will accept the scope as
+      # part of the name.
+      def scope_name(scope, task_name)
+        (scope + [task_name]).join(':')
+      end
+
+    end # class << Rake::Task
+  end # class Rake::Task
+
+
+  # #########################################################################
+  # A FileTask is a task that includes time based dependencies.  If any of a
+  # FileTask's prerequisites have a timestamp that is later than the file
+  # represented by this task, then the file must be rebuilt (using the
+  # supplied actions).
+  #
+  class FileTask < Task
+
+    # Is this file task needed?  Yes if it doesn't exist, or if its time stamp
+    # is out of date.
+    def needed?
+      return true unless File.exist?(name)
+      return true if out_of_date?(timestamp)
+      false
+    end
+
+    # Time stamp for file task.
+    def timestamp
+      if File.exist?(name)
+        File.mtime(name.to_s)
+      else
+        Rake::EARLY
+      end
+    end
+
+    private
+
+    # Are there any prerequisites with a later time than the given time stamp?
+    def out_of_date?(stamp)
+      @prerequisites.any? { |n| application[n].timestamp > stamp}
+    end
+
+    # ----------------------------------------------------------------
+    # Task class methods.
+    #
+    class << self
+      # Apply the scope to the task name according to the rules for this kind
+      # of task.  File based tasks ignore the scope when creating the name.
+      def scope_name(scope, task_name)
+        task_name
+      end
+    end
+  end # class Rake::FileTask
+
+  # #########################################################################
+  # A FileCreationTask is a file task that when used as a dependency will be
+  # needed if and only if the file has not been created.  Once created, it is
+  # not re-triggered if any of its dependencies are newer, nor does trigger
+  # any rebuilds of tasks that depend on it whenever it is updated.
+  #
+  class FileCreationTask < FileTask
+    # Is this file task needed?  Yes if it doesn't exist.
+    def needed?
+      ! File.exist?(name)
+    end
+
+    # Time stamp for file creation task.  This time stamp is earlier
+    # than any other time stamp.
+    def timestamp
+      Rake::EARLY
+    end
+  end
+
+  # #########################################################################
+  # Same as a regular task, but the immediate prerequisites are done in
+  # parallel using Ruby threads.
+  #
+  class MultiTask < Task
+    def invoke_prerequisites(args, invocation_chain)
+      threads = @prerequisites.collect { |p|
+        Thread.new(p) { |r| application[r].invoke_with_call_chain(args, invocation_chain) }
+      }
+      threads.each { |t| t.join }
+    end
+  end
+end # module Rake
+
+# ###########################################################################
+# Task Definition Functions ...
+
+# Declare a basic task.
+#
+# Example:
+#   task :clobber => [:clean] do
+#     rm_rf "html"
+#   end
+#
+def task(*args, &block)
+  Rake::Task.define_task(*args, &block)
+end
+
+
+# Declare a file task.
+#
+# Example:
+#   file "config.cfg" => ["config.template"] do
+#     open("config.cfg", "w") do |outfile|
+#       open("config.template") do |infile|
+#         while line = infile.gets
+#           outfile.puts line
+#         end
+#       end
+#     end
+#  end
+#
+def file(args, &block)
+  Rake::FileTask.define_task(args, &block)
+end
+
+# Declare a file creation task.
+# (Mainly used for the directory command).
+def file_create(args, &block)
+  Rake::FileCreationTask.define_task(args, &block)
+end
+
+# Declare a set of files tasks to create the given directories on demand.
+#
+# Example:
+#   directory "testdata/doc"
+#
+def directory(dir)
+  Rake.each_dir_parent(dir) do |d|
+    file_create d do |t|
+      mkdir_p t.name if ! File.exist?(t.name)
+    end
+  end
+end
+
+# Declare a task that performs its prerequisites in parallel. Multitasks does
+# *not* guarantee that its prerequisites will execute in any given order
+# (which is obvious when you think about it)
+#
+# Example:
+#   multitask :deploy => [:deploy_gem, :deploy_rdoc]
+#
+def multitask(args, &block)
+  Rake::MultiTask.define_task(args, &block)
+end
+
+# Create a new rake namespace and use it for evaluating the given block.
+# Returns a NameSpace object that can be used to lookup tasks defined in the
+# namespace.
+#
+# E.g.
+#
+#   ns = namespace "nested" do
+#     task :run
+#   end
+#   task_run = ns[:run] # find :run in the given namespace.
+#
+def namespace(name=nil, &block)
+  Rake.application.in_namespace(name, &block)
+end
+
+# Declare a rule for auto-tasks.
+#
+# Example:
+#  rule '.o' => '.c' do |t|
+#    sh %{cc -o #{t.name} #{t.source}}
+#  end
+#
+def rule(*args, &block)
+  Rake::Task.create_rule(*args, &block)
+end
+
+# Describe the next rake task.
+#
+# Example:
+#   desc "Run the Unit Tests"
+#   task :test => [:build]
+#     runtests
+#   end
+#
+def desc(description)
+  Rake.application.last_description = description
+end
+
+# Import the partial Rakefiles +fn+.  Imported files are loaded _after_ the
+# current file is completely loaded.  This allows the import statement to
+# appear anywhere in the importing file, and yet allowing the imported files
+# to depend on objects defined in the importing file.
+#
+# A common use of the import statement is to include files containing
+# dependency declarations.
+#
+# See also the --rakelibdir command line option.
+#
+# Example:
+#   import ".depend", "my_rules"
+#
+def import(*fns)
+  fns.each do |fn|
+    Rake.application.add_import(fn)
+  end
+end
+
+# ###########################################################################
+# This a FileUtils extension that defines several additional commands to be
+# added to the FileUtils utility functions.
+#
+module FileUtils
+  RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
+
+  OPT_TABLE['sh']  = %w(noop verbose)
+  OPT_TABLE['ruby'] = %w(noop verbose)
+
+  # Run the system command +cmd+. If multiple arguments are given the command
+  # is not run with the shell (same semantics as Kernel::exec and
+  # Kernel::system).
+  #
+  # Example:
+  #   sh %{ls -ltr}
+  #
+  #   sh 'ls', 'file with spaces'
+  #
+  #   # check exit status after command runs
+  #   sh %{grep pattern file} do |ok, res|
+  #     if ! ok
+  #       puts "pattern not found (status = #{res.exitstatus})"
+  #     end
+  #   end
+  #
+  def sh(*cmd, &block)
+    options = (Hash === cmd.last) ? cmd.pop : {}
+    unless block_given?
+      show_command = cmd.join(" ")
+      show_command = show_command[0,42] + "..."
+      # TODO code application logic heref show_command.length > 45
+      block = lambda { |ok, status|
+        ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]"
+      }
+    end
+    rake_check_options options, :noop, :verbose
+    rake_output_message cmd.join(" ") if options[:verbose]
+    unless options[:noop]
+      res = system(*cmd)
+      block.call(res, $?)
+    end
+  end
+
+  # Run a Ruby interpreter with the given arguments.
+  #
+  # Example:
+  #   ruby %{-pe '$_.upcase!' <README}
+  #
+  def ruby(*args,&block)
+    options = (Hash === args.last) ? args.pop : {}
+    if args.length > 1 then
+      sh(*([RUBY] + args + [options]), &block)
+    else
+      sh("#{RUBY} #{args.first}", options, &block)
+    end
+  end
+
+  LN_SUPPORTED = [true]
+
+  #  Attempt to do a normal file link, but fall back to a copy if the link
+  #  fails.
+  def safe_ln(*args)
+    unless LN_SUPPORTED[0]
+      cp(*args)
+    else
+      begin
+        ln(*args)
+      rescue StandardError, NotImplementedError => ex
+        LN_SUPPORTED[0] = false
+        cp(*args)
+      end
+    end
+  end
+
+  # Split a file path into individual directory names.
+  #
+  # Example:
+  #   split_all("a/b/c") =>  ['a', 'b', 'c']
+  #
+  def split_all(path)
+    head, tail = File.split(path)
+    return [tail] if head == '.' || tail == '/'
+    return [head, tail] if head == '/'
+    return split_all(head) + [tail]
+  end
+end
+
+# ###########################################################################
+# RakeFileUtils provides a custom version of the FileUtils methods that
+# respond to the <tt>verbose</tt> and <tt>nowrite</tt> commands.
+#
+module RakeFileUtils
+  include FileUtils
+
+  class << self
+    attr_accessor :verbose_flag, :nowrite_flag
+  end
+  RakeFileUtils.verbose_flag = true
+  RakeFileUtils.nowrite_flag = false
+
+  $fileutils_verbose = true
+  $fileutils_nowrite = false
+
+  FileUtils::OPT_TABLE.each do |name, opts|
+    default_options = []
+    if opts.include?('verbose')
+      default_options << ':verbose => RakeFileUtils.verbose_flag'
+    end
+    if opts.include?('noop')
+      default_options << ':noop => RakeFileUtils.nowrite_flag'
+    end
+
+    next if default_options.empty?
+    module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+    def #{name}( *args, &block )
+      super(
+        *rake_merge_option(args,
+          #{default_options.join(', ')}
+          ), &block)
+    end
+    EOS
+  end
+
+  # Get/set the verbose flag controlling output from the FileUtils utilities.
+  # If verbose is true, then the utility method is echoed to standard output.
+  #
+  # Examples:
+  #    verbose              # return the current value of the verbose flag
+  #    verbose(v)           # set the verbose flag to _v_.
+  #    verbose(v) { code }  # Execute code with the verbose flag set temporarily to _v_.
+  #                         # Return to the original value when code is done.
+  def verbose(value=nil)
+    oldvalue = RakeFileUtils.verbose_flag
+    RakeFileUtils.verbose_flag = value unless value.nil?
+    if block_given?
+      begin
+        yield
+      ensure
+        RakeFileUtils.verbose_flag = oldvalue
+      end
+    end
+    RakeFileUtils.verbose_flag
+  end
+
+  # Get/set the nowrite flag controlling output from the FileUtils utilities.
+  # If verbose is true, then the utility method is echoed to standard output.
+  #
+  # Examples:
+  #    nowrite              # return the current value of the nowrite flag
+  #    nowrite(v)           # set the nowrite flag to _v_.
+  #    nowrite(v) { code }  # Execute code with the nowrite flag set temporarily to _v_.
+  #                         # Return to the original value when code is done.
+  def nowrite(value=nil)
+    oldvalue = RakeFileUtils.nowrite_flag
+    RakeFileUtils.nowrite_flag = value unless value.nil?
+    if block_given?
+      begin
+        yield
+      ensure
+        RakeFileUtils.nowrite_flag = oldvalue
+      end
+    end
+    oldvalue
+  end
+
+  # Use this function to prevent protentially destructive ruby code from
+  # running when the :nowrite flag is set.
+  #
+  # Example:
+  #
+  #   when_writing("Building Project") do
+  #     project.build
+  #   end
+  #
+  # The following code will build the project under normal conditions. If the
+  # nowrite(true) flag is set, then the example will print:
+  #      DRYRUN: Building Project
+  # instead of actually building the project.
+  #
+  def when_writing(msg=nil)
+    if RakeFileUtils.nowrite_flag
+      puts "DRYRUN: #{msg}" if msg
+    else
+      yield
+    end
+  end
+
+  # Merge the given options with the default values.
+  def rake_merge_option(args, defaults)
+    if Hash === args.last
+      defaults.update(args.last)
+      args.pop
+    end
+    args.push defaults
+    args
+  end
+  private :rake_merge_option
+
+  # Send the message to the default rake output (which is $stderr).
+  def rake_output_message(message)
+    $stderr.puts(message)
+  end
+  private :rake_output_message
+
+  # Check that the options do not contain options not listed in +optdecl+.  An
+  # ArgumentError exception is thrown if non-declared options are found.
+  def rake_check_options(options, *optdecl)
+    h = options.dup
+    optdecl.each do |name|
+      h.delete name
+    end
+    raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
+  end
+  private :rake_check_options
+
+  extend self
+end
+
+# ###########################################################################
+# Include the FileUtils file manipulation functions in the top level module,
+# but mark them private so that they don't unintentionally define methods on
+# other objects.
+
+include RakeFileUtils
+private(*FileUtils.instance_methods(false))
+private(*RakeFileUtils.instance_methods(false))
+
+######################################################################
+module Rake
+
+  class RuleRecursionOverflowError < StandardError
+    def initialize(*args)
+      super
+      @targets = []
+    end
+
+    def add_target(target)
+      @targets << target
+    end
+
+    def message
+      super + ": [" + @targets.reverse.join(' => ') + "]"
+    end
+  end
+
+  # #########################################################################
+  # A FileList is essentially an array with a few helper methods defined to
+  # make file manipulation a bit easier.
+  #
+  # FileLists are lazy.  When given a list of glob patterns for possible files
+  # to be included in the file list, instead of searching the file structures
+  # to find the files, a FileList holds the pattern for latter use.
+  #
+  # This allows us to define a number of FileList to match any number of
+  # files, but only search out the actual files when then FileList itself is
+  # actually used.  The key is that the first time an element of the
+  # FileList/Array is requested, the pending patterns are resolved into a real
+  # list of file names.
+  #
+  class FileList
+
+    include Cloneable
+
+    # == Method Delegation
+    #
+    # The lazy evaluation magic of FileLists happens by implementing all the
+    # array specific methods to call +resolve+ before delegating the heavy
+    # lifting to an embedded array object (@items).
+    #
+    # In addition, there are two kinds of delegation calls.  The regular kind
+    # delegates to the @items array and returns the result directly.  Well,
+    # almost directly.  It checks if the returned value is the @items object
+    # itself, and if so will return the FileList object instead.
+    #
+    # The second kind of delegation call is used in methods that normally
+    # return a new Array object.  We want to capture the return value of these
+    # methods and wrap them in a new FileList object.  We enumerate these
+    # methods in the +SPECIAL_RETURN+ list below.
+
+    # List of array methods (that are not in +Object+) that need to be
+    # delegated.
+    ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
+
+    # List of additional methods that must be delegated.
+    MUST_DEFINE = %w[to_a inspect]
+
+    # List of methods that should not be delegated here (we define special
+    # versions of them explicitly below).
+    MUST_NOT_DEFINE = %w[to_a to_ary partition *]
+
+    # List of delegated methods that return new array values which need
+    # wrapping.
+    SPECIAL_RETURN = %w[
+      map collect sort sort_by select find_all reject grep
+      compact flatten uniq values_at
+      + - & |
+    ]
+
+    DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq
+
+    # Now do the delegation.
+    DELEGATING_METHODS.each_with_index do |sym, i|
+      if SPECIAL_RETURN.include?(sym)
+        ln = __LINE__+1
+        class_eval %{
+          def #{sym}(*args, &block)
+            resolve
+            result = @items.send(:#{sym}, *args, &block)
+            FileList.new.import(result)
+          end
+        }, __FILE__, ln
+      else
+        ln = __LINE__+1
+        class_eval %{
+          def #{sym}(*args, &block)
+            resolve
+            result = @items.send(:#{sym}, *args, &block)
+            result.object_id == @items.object_id ? self : result
+          end
+        }, __FILE__, ln
+      end
+    end
+
+    # Create a file list from the globbable patterns given.  If you wish to
+    # perform multiple includes or excludes at object build time, use the
+    # "yield self" pattern.
+    #
+    # Example:
+    #   file_list = FileList.new('lib/**/*.rb', 'test/test*.rb')
+    #
+    #   pkg_files = FileList.new('lib/**/*') do |fl|
+    #     fl.exclude(/\bCVS\b/)
+    #   end
+    #
+    def initialize(*patterns)
+      @pending_add = []
+      @pending = false
+      @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
+      @exclude_procs = DEFAULT_IGNORE_PROCS.dup
+      @exclude_re = nil
+      @items = []
+      patterns.each { |pattern| include(pattern) }
+      yield self if block_given?
+    end
+
+    # Add file names defined by glob patterns to the file list.  If an array
+    # is given, add each element of the array.
+    #
+    # Example:
+    #   file_list.include("*.java", "*.cfg")
+    #   file_list.include %w( math.c lib.h *.o )
+    #
+    def include(*filenames)
+      # TODO: check for pending
+      filenames.each do |fn|
+        if fn.respond_to? :to_ary
+          include(*fn.to_ary)
+        else
+          @pending_add << fn
+        end
+      end
+      @pending = true
+      self
+    end
+    alias :add :include
+
+    # Register a list of file name patterns that should be excluded from the
+    # list.  Patterns may be regular expressions, glob patterns or regular
+    # strings.  In addition, a block given to exclude will remove entries that
+    # return true when given to the block.
+    #
+    # Note that glob patterns are expanded against the file system. If a file
+    # is explicitly added to a file list, but does not exist in the file
+    # system, then an glob pattern in the exclude list will not exclude the
+    # file.
+    #
+    # Examples:
+    #   FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
+    #   FileList['a.c', 'b.c'].exclude(/^a/)  => ['b.c']
+    #
+    # If "a.c" is a file, then ...
+    #   FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
+    #
+    # If "a.c" is not a file, then ...
+    #   FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
+    #
+    def exclude(*patterns, &block)
+      patterns.each do |pat|
+        @exclude_patterns << pat
+      end
+      if block_given?
+        @exclude_procs << block
+      end
+      resolve_exclude if ! @pending
+      self
+    end
+
+
+    # Clear all the exclude patterns so that we exclude nothing.
+    def clear_exclude
+      @exclude_patterns = []
+      @exclude_procs = []
+      calculate_exclude_regexp if ! @pending
+      self
+    end
+
+    # Define equality.
+    def ==(array)
+      to_ary == array
+    end
+
+    # Return the internal array object.
+    def to_a
+      resolve
+      @items
+    end
+
+    # Return the internal array object.
+    def to_ary
+      to_a
+    end
+
+    # Lie about our class.
+    def is_a?(klass)
+      klass == Array || super(klass)
+    end
+    alias kind_of? is_a?
+
+    # Redefine * to return either a string or a new file list.
+    def *(other)
+      result = @items * other
+      case result
+      when Array
+        FileList.new.import(result)
+      else
+        result
+      end
+    end
+
+    # Resolve all the pending adds now.
+    def resolve
+      if @pending
+        @pending = false
+        @pending_add.each do |fn| resolve_add(fn) end
+        @pending_add = []
+        resolve_exclude
+      end
+      self
+    end
+
+    def calculate_exclude_regexp
+      ignores = []
+      @exclude_patterns.each do |pat|
+        case pat
+        when Regexp
+          ignores << pat
+        when /[*?]/
+          Dir[pat].each do |p| ignores << p end
+        else
+          ignores << Regexp.quote(pat)
+        end
+      end
+      if ignores.empty?
+        @exclude_re = /^$/
+      else
+        re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|")
+        @exclude_re = Regexp.new(re_str)
+      end
+    end
+
+    def resolve_add(fn)
+      case fn
+      when %r{[*?\[\{]}
+        add_matching(fn)
+      else
+        self << fn
+      end
+    end
+    private :resolve_add
+
+    def resolve_exclude
+      calculate_exclude_regexp
+      reject! { |fn| exclude?(fn) }
+      self
+    end
+    private :resolve_exclude
+
+    # Return a new FileList with the results of running +sub+ against each
+    # element of the oringal list.
+    #
+    # Example:
+    #   FileList['a.c', 'b.c'].sub(/\.c$/, '.o')  => ['a.o', 'b.o']
+    #
+    def sub(pat, rep)
+      inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
+    end
+
+    # Return a new FileList with the results of running +gsub+ against each
+    # element of the original list.
+    #
+    # Example:
+    #   FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
+    #      => ['lib\\test\\file', 'x\\y']
+    #
+    def gsub(pat, rep)
+      inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
+    end
+
+    # Same as +sub+ except that the oringal file list is modified.
+    def sub!(pat, rep)
+      each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
+      self
+    end
+
+    # Same as +gsub+ except that the original file list is modified.
+    def gsub!(pat, rep)
+      each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
+      self
+    end
+
+    # Apply the pathmap spec to each of the included file names, returning a
+    # new file list with the modified paths.  (See String#pathmap for
+    # details.)
+    def pathmap(spec=nil)
+      collect { |fn| fn.pathmap(spec) }
+    end
+
+    # Return a new array with <tt>String#ext</tt> method applied to each
+    # member of the array.
+    #
+    # This method is a shortcut for:
+    #
+    #    array.collect { |item| item.ext(newext) }
+    #
+    # +ext+ is a user added method for the Array class.
+    def ext(newext='')
+      collect { |fn| fn.ext(newext) }
+    end
+
+
+    # Grep each of the files in the filelist using the given pattern. If a
+    # block is given, call the block on each matching line, passing the file
+    # name, line number, and the matching line of text.  If no block is given,
+    # a standard emac style file:linenumber:line message will be printed to
+    # standard out.
+    def egrep(pattern)
+      each do |fn|
+        open(fn) do |inf|
+          count = 0
+          inf.each do |line|
+            count += 1
+            if pattern.match(line)
+              if block_given?
+                yield fn, count, line
+              else
+                puts "#{fn}:#{count}:#{line}"
+              end
+            end
+          end
+        end
+      end
+    end
+
+    # Return a new file list that only contains file names from the current
+    # file list that exist on the file system.
+    def existing
+      select { |fn| File.exist?(fn) }
+    end
+
+    # Modify the current file list so that it contains only file name that
+    # exist on the file system.
+    def existing!
+      resolve
+      @items = @items.select { |fn| File.exist?(fn) }
+      self
+    end
+
+    # FileList version of partition.  Needed because the nested arrays should
+    # be FileLists in this version.
+    def partition(&block)       # :nodoc:
+      resolve
+      result = @items.partition(&block)
+      [
+        FileList.new.import(result[0]),
+        FileList.new.import(result[1]),
+      ]
+    end
+
+    # Convert a FileList to a string by joining all elements with a space.
+    def to_s
+      resolve
+      self.join(' ')
+    end
+
+    # Add matching glob patterns.
+    def add_matching(pattern)
+      Dir[pattern].each do |fn|
+        self << fn unless exclude?(fn)
+      end
+    end
+    private :add_matching
+
+    # Should the given file name be excluded?
+    def exclude?(fn)
+      calculate_exclude_regexp unless @exclude_re
+      fn =~ @exclude_re || @exclude_procs.any? { |p| p.call(fn) }
+    end
+
+    DEFAULT_IGNORE_PATTERNS = [
+      /(^|[\/\\])CVS([\/\\]|$)/,
+      /(^|[\/\\])\.svn([\/\\]|$)/,
+      /\.bak$/,
+      /~$/
+    ]
+    DEFAULT_IGNORE_PROCS = [
+      proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) }
+    ]
+#    @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
+
+    def import(array)
+      @items = array
+      self
+    end
+
+    class << self
+      # Create a new file list including the files listed. Similar to:
+      #
+      #   FileList.new(*args)
+      def [](*args)
+        new(*args)
+      end
+    end
+  end # FileList
+end
+
+module Rake
+  class << self
+
+    # Yield each file or directory component.
+    def each_dir_parent(dir)
+      old_length = nil
+      while dir != '.' && dir.length != old_length
+        yield(dir)
+        old_length = dir.length
+        dir = File.dirname(dir)
+      end
+    end
+  end
+end # module Rake
+
+# Alias FileList to be available at the top level.
+FileList = Rake::FileList
+
+# ###########################################################################
+module Rake
+
+  # Default Rakefile loader used by +import+.
+  class DefaultLoader
+    def load(fn)
+      Kernel.load(File.expand_path(fn))
+    end
+  end
+
+  # EarlyTime is a fake timestamp that occurs _before_ any other time value.
+  class EarlyTime
+    include Comparable
+    include Singleton
+
+    def <=>(other)
+      -1
+    end
+
+    def to_s
+      "<EARLY TIME>"
+    end
+  end
+
+  EARLY = EarlyTime.instance
+end # module Rake
+
+# ###########################################################################
+# Extensions to time to allow comparisons with an early time class.
+#
+class Time
+  alias rake_original_time_compare :<=>
+  def <=>(other)
+    if Rake::EarlyTime === other
+      - other.<=>(self)
+    else
+      rake_original_time_compare(other)
+    end
+  end
+end # class Time
+
+module Rake
+
+  ####################################################################
+  # The NameSpace class will lookup task names in the the scope
+  # defined by a +namespace+ command.
+  #
+  class NameSpace
+
+    # Create a namespace lookup object using the given task manager
+    # and the list of scopes.
+    def initialize(task_manager, scope_list)
+      @task_manager = task_manager
+      @scope = scope_list.dup
+    end
+
+    # Lookup a task named +name+ in the namespace.
+    def [](name)
+      @task_manager.lookup(name, @scope)
+    end
+
+    # Return the list of tasks defined in this namespace.
+    def tasks
+      @task_manager.tasks
+    end
+  end # NameSpace
+
+
+  ####################################################################
+  # The TaskManager module is a mixin for managing tasks.
+  module TaskManager
+    # Track the last comment made in the Rakefile.
+    attr_accessor :last_description
+    alias :last_comment :last_description    # Backwards compatibility
+
+    def initialize
+      super
+      @tasks = Hash.new
+      @rules = Array.new
+      @scope = Array.new
+      @last_description = nil
+    end
+
+    def create_rule(*args, &block)
+      pattern, arg_names, deps = resolve_args(args)
+      pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
+      @rules << [pattern, deps, block]
+    end
+
+    def define_task(task_class, *args, &block)
+      task_name, arg_names, deps = resolve_args(args)
+      task_name = task_class.scope_name(@scope, task_name)
+      deps = [deps] unless deps.respond_to?(:to_ary)
+      deps = deps.collect {|d| d.to_s }
+      task = intern(task_class, task_name)
+      task.set_arg_names(arg_names) unless arg_names.empty?
+      task.add_description(@last_description)
+      @last_description = nil
+      task.enhance(deps, &block)
+      task
+    end
+
+    # Lookup a task.  Return an existing task if found, otherwise
+    # create a task of the current type.
+    def intern(task_class, task_name)
+      @tasks[task_name.to_s] ||= task_class.new(task_name, self)
+    end
+
+    # Find a matching task for +task_name+.
+    def [](task_name, scopes=nil)
+      task_name = task_name.to_s
+      self.lookup(task_name, scopes) or
+        enhance_with_matching_rule(task_name) or
+        synthesize_file_task(task_name) or
+        fail "Don't know how to build task '#{task_name}'"
+    end
+
+    def synthesize_file_task(task_name)
+      return nil unless File.exist?(task_name)
+      define_task(Rake::FileTask, task_name)
+    end
+
+    # Resolve the arguments for a task/rule.  Returns a triplet of
+    # [task_name, arg_name_list, prerequisites].
+    def resolve_args(args)
+      task_name = args.shift
+      arg_names = args #.map { |a| a.to_sym }
+      needs = []
+      if task_name.is_a?(Hash)
+        hash = task_name
+        task_name = hash.keys[0]
+        needs = hash[task_name]
+      end
+      if arg_names.last.is_a?(Hash)
+        hash = arg_names.pop
+        needs = hash[:needs]
+        fail "Unrecognized keys in task hash: #{hash.keys.inspect}" if hash.size > 1
+      end
+      needs = [needs] unless needs.respond_to?(:to_ary)
+      [task_name, arg_names, needs]
+    end
+
+    # If a rule can be found that matches the task name, enhance the
+    # task with the prerequisites and actions from the rule.  Set the
+    # source attribute of the task appropriately for the rule.  Return
+    # the enhanced task or nil of no rule was found.
+    def enhance_with_matching_rule(task_name, level=0)
+      fail Rake::RuleRecursionOverflowError,
+        "Rule Recursion Too Deep" if level >= 16
+      @rules.each do |pattern, extensions, block|
+        if md = pattern.match(task_name)
+          task = attempt_rule(task_name, extensions, block, level)
+          return task if task
+        end
+      end
+      nil
+    rescue Rake::RuleRecursionOverflowError => ex
+      ex.add_target(task_name)
+      fail ex
+    end
+
+    # List of all defined tasks in this application.
+    def tasks
+      @tasks.values.sort_by { |t| t.name }
+    end
+
+    # Clear all tasks in this application.
+    def clear
+      @tasks.clear
+      @rules.clear
+    end
+
+    # Lookup a task, using scope and the scope hints in the task name.
+    # This method performs straight lookups without trying to
+    # synthesize file tasks or rules.  Special scope names (e.g. '^')
+    # are recognized.  If no scope argument is supplied, use the
+    # current scope.  Return nil if the task cannot be found.
+    def lookup(task_name, initial_scope=nil)
+      initial_scope ||= @scope
+      task_name = task_name.to_s
+      if task_name =~ /^rake:/
+        scopes = []
+        task_name = task_name.sub(/^rake:/, '')
+      elsif task_name =~ /^(\^+)/
+        scopes = initial_scope[0, initial_scope.size - $1.size]
+        task_name = task_name.sub(/^(\^+)/, '')
+      else
+        scopes = initial_scope
+      end
+      lookup_in_scope(task_name, scopes)
+    end
+
+    # Lookup the task name
+    def lookup_in_scope(name, scope)
+      n = scope.size
+      while n >= 0
+        tn = (scope[0,n] + [name]).join(':')
+        task = @tasks[tn]
+        return task if task
+        n -= 1
+      end
+      nil
+    end
+    private :lookup_in_scope
+
+    # Return the list of scope names currently active in the task
+    # manager.
+    def current_scope
+      @scope.dup
+    end
+
+    # Evaluate the block in a nested namespace named +name+.  Create
+    # an anonymous namespace if +name+ is nil.
+    def in_namespace(name)
+      name ||= generate_name
+      @scope.push(name)
+      ns = NameSpace.new(self, @scope)
+      yield(ns)
+      ns
+    ensure
+      @scope.pop
+    end
+
+    private
+
+    # Generate an anonymous namespace name.
+    def generate_name
+      @seed ||= 0
+      @seed += 1
+      "_anon_#{@seed}"
+    end
+
+    # Attempt to create a rule given the list of prerequisites.
+    def attempt_rule(task_name, extensions, block, level)
+      sources = make_sources(task_name, extensions)
+      prereqs = sources.collect { |source|
+        if File.exist?(source) || Rake::Task.task_defined?(source)
+          source
+        elsif parent = enhance_with_matching_rule(sources.first, level+1)
+          parent.name
+        else
+          return nil
+        end
+      }
+      task = FileTask.define_task({task_name => prereqs}, &block)
+      task.sources = prereqs
+      task
+    end
+
+    # Make a list of sources from the list of file name extensions /
+    # translation procs.
+    def make_sources(task_name, extensions)
+      extensions.collect { |ext|
+        case ext
+        when /%/
+          task_name.pathmap(ext)
+        when %r{/}
+          ext
+        when /^\./
+          task_name.ext(ext)
+        when String
+          ext
+        when Proc
+          if ext.arity == 1
+            ext.call(task_name)
+          else
+            ext.call
+          end
+        else
+          fail "Don't know how to handle rule dependent: #{ext.inspect}"
+        end
+      }.flatten
+    end
+
+  end # TaskManager
+
+  ######################################################################
+  # Rake main application object.  When invoking +rake+ from the
+  # command line, a Rake::Application object is created and run.
+  #
+  class Application
+    include TaskManager
+
+    # The name of the application (typically 'rake')
+    attr_reader :name
+
+    # The original directory where rake was invoked.
+    attr_reader :original_dir
+
+    # Name of the actual rakefile used.
+    attr_reader :rakefile
+
+    # List of the top level task names (task names from the command line).
+    attr_reader :top_level_tasks
+
+    DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
+
+    OPTIONS = [     # :nodoc:
+      ['--classic-namespace', '-C', GetoptLong::NO_ARGUMENT,
+        "Put Task and FileTask in the top level namespace"],
+      ['--describe',  '-D', GetoptLong::OPTIONAL_ARGUMENT,
+        "Describe the tasks (matching optional PATTERN), then exit."],
+      ['--rakefile', '-f', GetoptLong::OPTIONAL_ARGUMENT,
+        "Use FILE as the rakefile."],
+      ['--help',     '-h', '-H', GetoptLong::NO_ARGUMENT,
+        "Display this help message."],
+      ['--libdir',   '-I', GetoptLong::REQUIRED_ARGUMENT,
+        "Include LIBDIR in the search path for required modules."],
+      ['--dry-run',  '-n', GetoptLong::NO_ARGUMENT,
+        "Do a dry run without executing actions."],
+      ['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
+        "Do not search parent directories for the Rakefile."],
+      ['--prereqs',  '-P', GetoptLong::NO_ARGUMENT,
+        "Display the tasks and dependencies, then exit."],
+      ['--quiet',    '-q', GetoptLong::NO_ARGUMENT,
+        "Do not log messages to standard output."],
+      ['--require',  '-r', GetoptLong::REQUIRED_ARGUMENT,
+        "Require MODULE before executing rakefile."],
+      ['--rakelibdir', '-R', GetoptLong::REQUIRED_ARGUMENT,
+        "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')"],
+      ['--silent',   '-s', GetoptLong::NO_ARGUMENT,
+        "Like --quiet, but also suppresses the 'in directory' announcement."],
+      ['--tasks',    '-T', GetoptLong::OPTIONAL_ARGUMENT,
+        "Display the tasks (matching optional PATTERN) with descriptions, then exit."],
+      ['--trace',    '-t', GetoptLong::NO_ARGUMENT,
+        "Turn on invoke/execute tracing, enable full backtrace."],
+      ['--verbose',  '-v', GetoptLong::NO_ARGUMENT,
+        "Log message to standard output (default)."],
+      ['--version',  '-V', GetoptLong::NO_ARGUMENT,
+        "Display the program version."],
+    ]
+
+    # Initialize a Rake::Application object.
+    def initialize
+      super
+      @name = 'rake'
+      @rakefiles = DEFAULT_RAKEFILES.dup
+      @rakefile = nil
+      @pending_imports = []
+      @imported = []
+      @loaders = {}
+      @default_loader = Rake::DefaultLoader.new
+      @original_dir = Dir.pwd
+      @top_level_tasks = []
+      add_loader('rf', DefaultLoader.new)
+      add_loader('rake', DefaultLoader.new)
+    end
+
+    # Run the Rake application.  The run method performs the following three steps:
+    #
+    # * Initialize the command line options (+init+).
+    # * Define the tasks (+load_rakefile+).
+    # * Run the top level tasks (+run_tasks+).
+    #
+    # If you wish to build a custom rake command, you should call +init+ on your
+    # application.  The define any tasks.  Finally, call +top_level+ to run your top
+    # level tasks.
+    def run
+      standard_exception_handling do
+        init
+        load_rakefile
+        top_level
+      end
+    end
+
+    # Initialize the command line parameters and app name.
+    def init(app_name='rake')
+      standard_exception_handling do
+        @name = app_name
+        handle_options
+        collect_tasks
+      end
+    end
+
+    # Find the rakefile and then load it and any pending imports.
+    def load_rakefile
+      standard_exception_handling do
+        raw_load_rakefile
+      end
+    end
+
+    # Run the top level tasks of a Rake application.
+    def top_level
+      standard_exception_handling do
+        if options.show_tasks
+          display_tasks_and_comments
+        elsif options.show_prereqs
+          display_prerequisites
+        else
+          top_level_tasks.each { |task_name| invoke_task(task_name) }
+        end
+      end
+    end
+
+    # Add a loader to handle imported files ending in the extension
+    # +ext+.
+    def add_loader(ext, loader)
+      ext = ".#{ext}" unless ext =~ /^\./
+      @loaders[ext] = loader
+    end
+
+    # Application options from the command line
+    def options
+      @options ||= OpenStruct.new
+    end
+
+    # private ----------------------------------------------------------------
+
+    def invoke_task(task_string)
+      name, args = parse_task_string(task_string)
+      t = self[name]
+      t.invoke(*args)
+    end
+
+    def parse_task_string(string)
+      if string =~ /^([^\[]+)(\[(.*)\])$/
+        name = $1
+        args = $3.split(/\s*,\s*/)
+      else
+        name = string
+        args = []
+      end
+      [name, args]
+    end
+
+    # Provide standard execption handling for the given block.
+    def standard_exception_handling
+      begin
+        yield
+      rescue SystemExit => ex
+        # Exit silently with current status
+        exit(ex.status)
+      rescue SystemExit, GetoptLong::InvalidOption => ex
+        # Exit silently
+        exit(1)
+      rescue Exception => ex
+        # Exit with error message
+        $stderr.puts "rake aborted!"
+        $stderr.puts ex.message
+        if options.trace
+          $stderr.puts ex.backtrace.join("\n")
+        else
+          $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
+          $stderr.puts "(See full trace by running task with --trace)"
+        end
+        exit(1)
+      end
+    end
+
+    # True if one of the files in RAKEFILES is in the current directory.
+    # If a match is found, it is copied into @rakefile.
+    def have_rakefile
+      @rakefiles.each do |fn|
+        if File.exist?(fn) || fn == ''
+          @rakefile = fn
+          return true
+        end
+      end
+      return false
+    end
+
+    # Display the rake command line help.
+    def help
+      puts "rake [-f rakefile] {options} targets..."
+      puts
+      puts "Options are ..."
+      puts
+      OPTIONS.sort.each do |long, short, mode, desc|
+        if mode == GetoptLong::REQUIRED_ARGUMENT
+          if desc =~ /\b([A-Z]{2,})\b/
+            long = long + "=#{$1}"
+          end
+        end
+        printf "  %-20s (%s)\n", long, short
+        printf "      %s\n", desc
+      end
+    end
+
+    # Display the tasks and dependencies.
+    def display_tasks_and_comments
+      displayable_tasks = tasks.select { |t|
+        t.comment && t.name =~ options.show_task_pattern
+      }
+      if options.full_description
+        displayable_tasks.each do |t|
+          puts "rake #{t.name_with_args}"
+          t.full_comment.split("\n").each do |line|
+            puts "    #{line}"
+          end
+          puts
+        end
+      else
+        width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
+        max_column = 80 - name.size - width - 7
+        displayable_tasks.each do |t|
+          printf "#{name} %-#{width}s  # %s\n",
+            t.name_with_args, truncate(t.comment, max_column)
+        end
+      end
+    end
+
+    def truncate(string, width)
+      if string.length <= width
+        string
+      else
+        string[0, width-3] + "..."
+      end
+    end
+
+    # Display the tasks and prerequisites
+    def display_prerequisites
+      tasks.each do |t|
+        puts "rake #{t.name}"
+        t.prerequisites.each { |pre| puts "    #{pre}" }
+      end
+    end
+
+    # Return a list of the command line options supported by the
+    # program.
+    def command_line_options
+      OPTIONS.collect { |lst| lst[0..-2] }
+    end
+
+    # Do the option defined by +opt+ and +value+.
+    def do_option(opt, value)
+      case opt
+      when '--describe'
+        options.show_tasks = true
+        options.show_task_pattern = Regexp.new(value || '.')
+        options.full_description = true
+      when '--dry-run'
+        verbose(true)
+        nowrite(true)
+        options.dryrun = true
+        options.trace = true
+      when '--help'
+        help
+        exit
+      when '--libdir'
+        $:.push(value)
+      when '--nosearch'
+        options.nosearch = true
+      when '--prereqs'
+        options.show_prereqs = true
+      when '--quiet'
+        verbose(false)
+      when '--rakefile'
+        @rakefiles.clear
+        @rakefiles << value
+      when '--rakelibdir'
+        options.rakelib = value.split(':')
+      when '--require'
+        begin
+          require value
+        rescue LoadError => ex
+          begin
+            rake_require value
+          rescue LoadError => ex2
+            raise ex
+          end
+        end
+      when '--silent'
+        verbose(false)
+        options.silent = true
+      when '--tasks'
+        options.show_tasks = true
+        options.show_task_pattern = Regexp.new(value || '.')
+        options.full_description = false
+      when '--trace'
+        options.trace = true
+        verbose(true)
+      when '--verbose'
+        verbose(true)
+      when '--version'
+        puts "rake, version #{RAKEVERSION}"
+        exit
+      when '--classic-namespace'
+        require 'rake/classic_namespace'
+        options.classic_namespace = true
+      end
+    end
+
+    # Read and handle the command line options.
+    def handle_options
+      options.rakelib = ['rakelib']
+
+      opts = GetoptLong.new(*command_line_options)
+      opts.each { |opt, value| do_option(opt, value) }
+
+      # If class namespaces are requested, set the global options
+      # according to the values in the options structure.
+      if options.classic_namespace
+        $show_tasks = options.show_tasks
+        $show_prereqs = options.show_prereqs
+        $trace = options.trace
+        $dryrun = options.dryrun
+        $silent = options.silent
+      end
+    rescue NoMethodError => ex
+      raise GetoptLong::InvalidOption, "While parsing options, error = #{ex.class}:#{ex.message}"
+    end
+
+    # Similar to the regular Ruby +require+ command, but will check
+    # for .rake files in addition to .rb files.
+    def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
+      return false if loaded.include?(file_name)
+      paths.each do |path|
+        fn = file_name + ".rake"
+        full_path = File.join(path, fn)
+        if File.exist?(full_path)
+          load full_path
+          loaded << fn
+          return true
+        end
+      end
+      fail LoadError, "Can't find #{file_name}"
+    end
+
+    def raw_load_rakefile # :nodoc:
+      here = Dir.pwd
+      while ! have_rakefile
+        Dir.chdir("..")
+        if Dir.pwd == here || options.nosearch
+          fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})"
+        end
+        here = Dir.pwd
+      end
+      puts "(in #{Dir.pwd})" unless options.silent
+      $rakefile = @rakefile
+      load File.expand_path(@rakefile) if @rakefile != ''
+      options.rakelib.each do |rlib|
+        Dir["#{rlib}/*.rake"].each do |name| add_import name end
+      end
+      load_imports
+    end
+
+    # Collect the list of tasks on the command line.  If no tasks are
+    # given, return a list containing only the default task.
+    # Environmental assignments are processed at this time as well.
+    def collect_tasks
+      @top_level_tasks = []
+      ARGV.each do |arg|
+        if arg =~ /^(\w+)=(.*)$/
+          ENV[$1] = $2
+        else
+          @top_level_tasks << arg
+        end
+      end
+      @top_level_tasks.push("default") if @top_level_tasks.size == 0
+    end
+
+    # Add a file to the list of files to be imported.
+    def add_import(fn)
+      @pending_imports << fn
+    end
+
+    # Load the pending list of imported files.
+    def load_imports
+      while fn = @pending_imports.shift
+        next if @imported.member?(fn)
+        if fn_task = lookup(fn)
+          fn_task.invoke
+        end
+        ext = File.extname(fn)
+        loader = @loaders[ext] || @default_loader
+        loader.load(fn)
+        @imported << fn
+      end
+    end
+
+    # Warn about deprecated use of top level constant names.
+    def const_warning(const_name)
+      @const_warning ||= false
+      if ! @const_warning
+        $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } +
+          %{found at: #{rakefile_location}} # '
+        $stderr.puts %{    Use --classic-namespace on rake command}
+        $stderr.puts %{    or 'require "rake/classic_namespace"' in Rakefile}
+      end
+      @const_warning = true
+    end
+
+    def rakefile_location
+      begin
+        fail
+      rescue RuntimeError => ex
+        ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
+      end
+    end
+  end
+end
+
+
+class Module
+  # Rename the original handler to make it available.
+  alias :rake_original_const_missing :const_missing
+
+  # Check for deprecated uses of top level (i.e. in Object) uses of
+  # Rake class names.  If someone tries to reference the constant
+  # name, display a warning and return the proper object.  Using the
+  # --classic-namespace command line option will define these
+  # constants in Object and avoid this handler.
+  def const_missing(const_name)
+    case const_name
+    when :Task
+      Rake.application.const_warning(const_name)
+      Rake::Task
+    when :FileTask
+      Rake.application.const_warning(const_name)
+      Rake::FileTask
+    when :FileCreationTask
+      Rake.application.const_warning(const_name)
+      Rake::FileCreationTask
+    when :RakeApp
+      Rake.application.const_warning(const_name)
+      Rake::Application
+    else
+      rake_original_const_missing(const_name)
+    end
+  end
+end

Property changes on: lib/rake.rb
___________________________________________________________________
Name: svn:executable
   + *

Index: bin/rake
===================================================================
--- bin/rake	(revision 0)
+++ bin/rake	(revision 14385)
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+
+#--
+# Copyright (c) 2003, 2004, 2005, 2006, 2007  Jim Weirich
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+#++
+
+begin
+  require 'rake'
+rescue LoadError
+  require 'rubygems'
+  require 'rake'
+end
+Rake.application.run

Property changes on: bin/rake
___________________________________________________________________
Name: svn:executable
   + *


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml

[前][次][番号順一覧][スレッド一覧]