$rakefile_path = File.dirname(__FILE__)

# IronRuby rakefile for build, test, and repository integration v2

require 'rubygems'
gem 'pathname2'

require 'pathname2'
require 'tmpdir'

verbose(false) # default to non-verbose output

# Paths to configure

IRONRUBY_COMPILER     = 'ir.exe'
IRONRUBY_BINARY       = 'ir.cmd'
CS_COMPILER           = ENV['mono'].nil? ? 'csc' : 'gmcs'
EXCLUDED_EXTENSIONS   = ['.old', '.suo', '.vspscc', '.vssscc', '.user', '.log', '.pdb', '.cache', '.swp']
EXCLUDED_DIRECTORIES  = ['.svn', 'obj', '.', '..']

require 'context'

# Add some utility methods to Pathname to support filtered lists

class Pathname
  def filtered_subdirs(extras = [])
    return [] unless exist?
    filtered_subdirs = subdirs.find_all { |dir| (dir.to_a & (EXCLUDED_DIRECTORIES + extras)) == [] }
    result = filtered_subdirs.map { |dir| self + dir }
    result.insert(0, self)
  end

  def subdirs
    glob("**/*", File::FNM_DOTMATCH).find_all { |path| (self + path).directory? }
  end

  def files
    raise "Cannot call files on a filename path: #{self}" if !directory?
    entries.find_all { |e| !(self + e).directory? }.sort
  end

  def filtered_files
    raise "Cannot call filtered_files on a filename path: #{self}" if !directory?
    files.find_all { |f| !EXCLUDED_EXTENSIONS.include?((self + f).extname) }.map { |f| f.downcase }
  end
end

# Source repository synchronization tasks

def push
  IronRuby.source_context do
    rake_output_message "#{'=' * 78}\nTransforming source to target layout\n\n"

    # Copy to temporary directory and transform layout to match target layout
    # This lets us diff the temp target layout with the actual layout

    temp_dir = generate_temp_dir
    nodes = [:root, :gppg, :dlr_core, :dlr_libs, :ironruby, :libraries, :tests, :console, :generator, :test_runner, :scanner, :yaml, :libs]
    nodes.each do |node|
      # special case tests directory to avoid filtering sub-directories
      if node == :tests
        copy_to_temp_dir node, temp_dir
      else
        copy_to_temp_dir node, temp_dir, ['bin'] # always filter out bin subdirectories except in tests
      end
    end

    # Do some post-transform filtering of files

    if IronRuby.is_merlin?
      del temp_dir, 'Ruby.sln'
    else
      puts "copying #{IronRuby.source + 'IronRuby.sln'} to #{temp_dir + 'Merlin/Main/languages/ruby/IronRuby.sln'}"
      copy IronRuby.source + 'IronRuby.sln', temp_dir + 'Merlin/Main/languages/ruby/Ruby.sln'
    end

    # Diff and push temp directory files to the target

    push_to_target temp_dir

    # Transform the project files

    transform_project :ironruby, 'ruby.csproj'
    transform_project :libraries, 'ironruby.libraries.csproj'
    transform_project(:dlr_core, 'microsoft.scripting.core.csproj') do |contents|
      if IronRuby.is_merlin?
        replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Debug\\', '..\..\build\debug\\'
        replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Release\\', '..\..\build\release\\'
        replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\'
        replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\\', '..\..\build\silverlight release\\'
        replace_import_project contents, '..\..\..\..\..\..\Merlin\Main\SpecSharp.targets', '..\..\SpecSharp.targets'
        replace_doc_path contents,    '..\..\..\..\..\..\Merlin\Main\Bin\Debug\Microsoft.Scripting.Core.xml', '..\..\build\debug\Microsoft.Scripting.Core.xml'
        replace_doc_path contents,    '..\..\..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Scripting.Core.xml', '..\..\build\release\Microsoft.Scripting.Core.xml'
        replace_doc_path contents,    '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\Microsoft.Scripting.Core.xml', '..\..\build\silverlight debug\Microsoft.Scripting.Core.xml'
        replace_doc_path contents,    '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\Microsoft.Scripting.Core.xml', '..\..\build\silverlight release\Microsoft.Scripting.Core.xml'
        replace_key_path    contents, '..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk', '..\..\RubyTestKey.snk'
      else
        replace_output_path contents, '..\..\build\debug\\', '..\..\..\..\..\..\Merlin\Main\Bin\Debug\\'
        replace_output_path contents, '..\..\build\release\\', '..\..\..\..\..\..\Merlin\Main\Bin\Release\\'
        replace_output_path contents, '..\..\build\silverlight debug\\', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\\'
        replace_output_path contents, '..\..\build\silverlight release\\', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\\'
        replace_import_project contents, '..\..\SpecSharp.targets', '..\..\..\..\..\..\Merlin\Main\SpecSharp.targets'
        replace_doc_path contents,    '..\..\build\debug\microsoft.scripting.core.xml', '..\..\..\..\..\..\Merlin\Main\Bin\Debug\Microsoft.Scripting.Core.xml'
        replace_doc_path contents,    '..\..\build\release\microsoft.scripting.core.xml', '..\..\..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Scripting.Core.xml'
        replace_doc_path contents,    '..\..\build\silverlight debug\microsoft.scripting.core.xml', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\Microsoft.Scripting.Core.xml'
        replace_doc_path contents,    '..\..\build\silverlight release\microsoft.scripting.core.xml', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\Microsoft.Scripting.Core.xml'
        replace_key_path    contents, '..\..\RubyTestKey.snk', '..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk'
      end
    end
    transform_project(:dlr_libs, 'microsoft.scripting.csproj') do |contents|
      if IronRuby.is_merlin?
        replace_output_path contents, '..\..\Bin\Debug\\', '..\..\build\debug\\'
        replace_output_path contents, '..\..\Bin\Release\\', '..\..\build\release\\'
        replace_output_path contents, '..\..\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\'
        replace_output_path contents, '..\..\Bin\Silverlight Release\\', '..\..\build\silverlight release\\'
        replace_doc_path    contents, '..\..\Bin\Debug\Microsoft.Scripting.xml', '..\..\build\debug\Microsoft.Scripting.xml'
        replace_doc_path    contents, '..\..\Bin\Release\Microsoft.Scripting.xml', '..\..\build\release\Microsoft.Scripting.xml'
        replace_doc_path    contents, '..\..\Bin\Silverlight Debug\Microsoft.Scripting.xml', '..\..\build\silverlight debug\Microsoft.Scripting.xml'
        replace_doc_path    contents, '..\..\Bin\Silverlight Release\Microsoft.Scripting.xml', '..\..\build\silverlight release\Microsoft.Scripting.xml'
        replace_key_path    contents, '..\..\MSSharedLibKey.snk', '..\..\RubyTestKey.snk'
      else
        replace_output_path contents, '..\..\build\debug\\', '..\..\Bin\Debug\\'
        replace_output_path contents, '..\..\build\release\\', '..\..\Bin\Release\\'
        replace_output_path contents, '..\..\build\silverlight debug\\', '..\..\Bin\Silverlight Debug\\'
        replace_output_path contents, '..\..\build\silverlight release\\', '..\..\Bin\Silverlight Release\\'
        replace_doc_path    contents, '..\..\build\debug\Microsoft.Scripting.xml', '..\..\Bin\Debug\Microsoft.Scripting.xml'
        replace_doc_path    contents, '..\..\build\release\Microsoft.Scripting.xml', '..\..\Bin\Release\Microsoft.Scripting.xml'
        replace_doc_path    contents, '..\..\build\silverlight debug\Microsoft.Scripting.xml', '..\..\Bin\Silverlight Debug\Microsoft.Scripting.xml'
        replace_doc_path    contents, '..\..\build\silverlight release\Microsoft.Scripting.xml', '..\..\Bin\Silverlight Release\Microsoft.Scripting.xml'
        replace_key_path    contents, '..\..\RubyTestKey.snk', '..\..\MSSharedLibKey.snk'
      end
    end
    # TODO: add signing to this project
    transform_project(:yaml, 'IronRuby.Libraries.Yaml.csproj') do |contents|
      if IronRuby.is_merlin?
        replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Debug\\', '..\..\build\debug\\'
        replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Release\\', '..\..\build\release\\'
        replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\'
        replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\\', '..\..\build\silverlight release\\'
        replace_import_project contents, '..\..\..\..\..\..\Merlin\Main\SpecSharp.targets', '..\..\SpecSharp.targets'
        replace_key_path    contents, '..\..\..\..\..\Main\MSSharedLibKey.snk', '..\..\RubyTestKey.snk'
      else
        replace_output_path contents, '..\..\build\debug\\', '..\..\..\..\..\..\Merlin\Main\Bin\Debug\\'
        replace_output_path contents, '..\..\build\release\\', '..\..\..\..\..\..\Merlin\Main\Bin\Release\\'
        replace_output_path contents, '..\..\build\silverlight debug\\', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\\'
        replace_output_path contents, '..\..\build\silverlight release\\', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\\'
        replace_import_project contents, '..\..\SpecSharp.targets', '..\..\..\..\..\..\Merlin\Main\SpecSharp.targets'
        replace_key_path    contents, '..\..\RubyTestKey.snk', '..\..\..\..\..\Main\MSSharedLibKey.snk'
      end
    end
    transform_project :generator, 'classinitgenerator.csproj'
    transform_project :console, 'ruby.console.csproj'
    transform_project :test_runner, 'ironruby.tests.csproj'
    transform_project :scanner, 'ironruby.libraries.scanner.csproj'
  end
end

desc "push TFS source tree to Subversion"
task :to_svn => [:happy, :clean_tests] do
  raise "must be in MERLIN enlistment to run to_svn" unless IronRuby.is_merlin?
  push
end

desc "push Subversion source tree to TFS"
task :to_merlin => [:happy, :clean_tests] do
  raise "must be in SVN enlistment to run to_merlin" if IronRuby.is_merlin?
  push
end

# Compilation tasks

desc "generate ir.cmd"
task :make_ir_cmd => [:clean_build] do
  ruby_exe_paths = UserEnvironment.find_executable 'ruby.exe'

  if ruby_exe_paths.length == 1 
    if !IronRuby.is_merlin?
      IronRuby.source_context do
        lib_paths = []
        lib_paths << Pathname.new(ruby_exe_paths.first + '\..\lib\ruby\1.8').expand_path
        lib_paths << Pathname.new(ruby_exe_paths.first + '\..\lib\ruby\site_ruby\1.8').expand_path
        lib_paths << get_source_dir(:libs)
        ir_path = build_path + '\ir.exe '

        File.open(build_path + '\ir.cmd', 'w+') do |f|
          f.write "@echo off\n#{ir_path} -I #{lib_paths.join(';')} %*"
        end
      end
    end
  else
    raise ArgumentError.new("Found more than one version of MRI on your path: #{ruby_exe_paths.join(", ")}")
  end
end

desc "clean build directory"
task :clean_build => [:happy] do
  IronRuby.source_context do
    rd build_path
    mkdir build_path
  end
end

desc "compile DLR (Microsoft.Scripting.dll and Microsoft.Scripting.Core.dll)"
task :compile_dlr => [:clean_build] do
  IronRuby.source_context do
    resources = { Pathname.new('Microsoft.Scripting.txt') => Pathname.new('Microsoft.Scripting.Core.resources') }
    compile :dlr_core, :references => ['!System.dll', '!System.Configuration.dll'], :switches => ['target:library', 'define:DLR'], :resources => resources, :output => 'Microsoft.Scripting.Core.dll'
    resources = { Pathname.new('math') + 'MathResources.resx' => Pathname.new('Microsoft.Scripting.Math.MathResources.resources') }
    compile :dlr_libs, :references => ['Microsoft.Scripting.Core.dll', '!System.dll'], :switches => ['target:library'], :resources => resources, :output => 'Microsoft.Scripting.dll'
  end
end

desc "compile ClassInitGenerator.exe"
task :compile_generator => [:compile_ruby]  do
  IronRuby.source_context do
    compile :generator, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll', '!System.dll'], :output => 'ClassInitGenerator.exe'
  end
end

desc "compile IronRuby.dll"
task :compile_ruby => [:compile_dlr] do
  IronRuby.source_context do
    compile :ironruby, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', '!System.dll'], :switches => ['target:library'], :output => 'IronRuby.dll'
  end
end

desc "compile IronRuby.Libraries.dll"
task :compile_libraries => [:compile_ruby] do
  IronRuby.source_context do
    compile :libraries, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll', '!System.dll'], :switches => ['target:library'], :output => 'IronRuby.Libraries.dll'
  end
end

desc "compile just the IronRuby console"
task :compile_console => [:make_ir_cmd,:compile_libraries] do
  IronRuby.source_context do
    compile :console, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll'], :output => IRONRUBY_COMPILER
  end
end

desc "compile IronRuby.Tests"
task :compile_testhost => [:compile_libraries] do
  IronRuby.source_context do
    compile :test_runner, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll', 'IronRuby.Libraries.dll', '!System.dll', '!System.Windows.Forms.dll'], :output => 'IronRuby.Tests.exe'
  end
end

desc "compile IronRuby.Libraries.Scanner"
task :compile_scanner => [:compile_libraries] do
  IronRuby.source_context do
    compile :scanner, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll', 'IronRuby.Libraries.dll', '!System.Core.dll'], :output => 'IronRuby.Libraries.Scanner.exe'
  end
end

desc "compile Yaml"
task :compile_yaml => [:compile_libraries] do
  IronRuby.source_context do
    compile :yaml, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll', 'IronRuby.Libraries.dll'], :switches => ['target:library'], :output => 'IronRuby.Libraries.Yaml.dll'
  end
end

desc "compile everything"
task :compile => [:happy, :clean_build, :compile_dlr, :compile_ruby, :compile_libraries, :compile_console, :compile_testhost, :compile_generator, :compile_yaml] do
end

# Unit test/spec tasks

desc "[deprecated] run old school spec tests"
task :test_libs do
  IronRuby.source_context do
    get_source_dir(:tests).filtered_subdirs.each do |dir|
      chdir(dir) {
        dir.glob('test_*.rb').each { |file| exec_net "\"#{build_path + IRONRUBY_BINARY}\" \"#{file}\"" }
      }
    end
  end
end

desc "run compiler only tests using IronRuby.Tests.exe C# driver"
task :test_compiler do
  IronRuby.source_context do
    exec_net "#{build_path + 'ironruby.tests.exe'}"
    # TODO: make run.rb do the right thing in external svn-only install
    chdir(:tests) { exec "ruby run.rb" }
  end
end

desc "[deprecated] run Ruby spec suite"
task :specs do
  IronRuby.source_context do
    chdir(get_source_dir(:tests) + 'specs') {
      exec_net "\"#{Environment.build + IRONRUBY_BINARY}\" mspec.rb"
    }
  end
end

desc "Alias for mspec:core"
task :mspec => "mspec:core"

desc "Set ruby runner to CRuby"
task :ruby do
  begin
    old_verbose,$VERBOSE = $VERBOSE,nil
    $ruby_imp = "ruby.exe"
    ARGV = [ARGV[0],*ARGV[2..-1]]
  ensure
    $VERBOSE = old_verbose
  end
end

namespace :mspec do
  desc "Run RubySpec core suite"
  task :core => "mspec:ruby_imp" do
    IronRuby.source_context { invoke_mspec($ruby_imp) }
    exit
  end

  desc "Run RubySpec language suite"
  task :lang => "mspec:ruby_imp" do
    IronRuby.source_context { invoke_mspec($ruby_imp, 'language')}
    exit
  end
  
  desc "Run RubySpec library suite"
  task :lib => "mspec:ruby_imp" do
    IronRuby.source_context { invoke_mspec($ruby_imp, 'library')}
    exit
  end
  
  task :ruby_imp do
    IronRuby.source_context {$ruby_imp ||= path_to_ir}
  end
end

def list_specs(klass)
  IronRuby.source_context do
    chdir(get_source_dir(:tests) + "specs/core/#{klass}") do
      Dir.glob('*_spec.rb').each { |file| rake_output_message /(.*)_spec.rb/.match(file)[1] }
    end
  end
end

def invoke_spec(path_to_ruby, klass, name, reporter)
  IronRuby.source_context do
    chdir(get_source_dir(:tests) + 'specs') {
      exec_net "\"#{path_to_ruby}\" -I #{get_source_dir(:libraries)} spec_runner.rb #{klass} #{name} #{reporter}"
      p $?.exitstatus
    }
  end
end

def tags
  get_source_dir(:tests)+'specs\tags'
end

def split_args
  klass = ARGV.length == 1 ? '-' : ARGV[1]
  name = ARGV.length <= 2 ? '-' : ARGV[2]
  reporter = ARGV.length == 4 ? ARGV[3] : "summary"
  [klass, name, reporter]
end

def path_to_ir
  build_path + IRONRUBY_BINARY
end

def extract_reporter(reporter)
   case reporter
   when 'dox'
     '-f specdoc'
   when 'fail'
     '-f fail'
   when 'cov'
     ['-f coverage','run']
   when 'tag'
     ['-f dotted','tag']
   when 'run'
     ['-f dotted','run']
   else
     '-f dotted'
   end
end

def invoke_mspec(path_to_ruby, root_path = "core")
  unless root_path == "language"  
    klass, name, reporter = split_args
  else
    name, reporter, _ = split_args
  end
  IronRuby.source_context do
    root = get_source_dir(:tests) + 'specs'
    spec_file = name == '-' ? '' : "#{name}_spec.rb"
    spec_dir = klass == '-' ? '' : "#{klass}\\"
    spec_suite = spec_dir + spec_file
    run_spec = root + "ruby\\1.8\\#{root_path}\\#{spec_suite}"
    reporter,tag  = extract_reporter(reporter)
    
    chdir(get_source_dir(:tests) +'util'){
      cmd =  "ruby mspec/bin/mspec #{tag || 'ci'} -t #{path_to_ruby} --tags-dir #{tags} #{run_spec} #{reporter}"
      exec_net cmd
    }
  end
end

def spec_runner(path_to_ruby, extra = nil)
  klass, name, reporter = split_args
  if name == "?"
    list_specs(klass)
  else
    invoke_spec(path_to_ruby, klass, name, reporter)
  end
end

desc "run specs - params are: class [method(s)] [reporter]"
task :spec do
  IronRuby.source_context { spec_runner(path_to_ir) }
  exit
end

desc "run specs using Ruby - params are: class [method(s)] [reporter]"
task :rspec do
  spec_runner('ruby.exe')
  exit
end

desc "run specs using IronRuby and Ruby - params are: class [method(s)] [reporter]"
task :spec2 do
  IronRuby.source_context do
    print "IronRuby: "
    spec_runner(path_to_ir)
    print "Ruby:     "
    spec_runner('ruby.exe')
  end
  exit
end

desc "remove output files and generated debugging info from tests directory"
task :clean_tests do
  IronRuby.source_context do
    chdir(:tests) do
      exec "del /s *.log"
      exec "del /s *.pdb"
      exec "del /s *.exe"
      exec "del /s *.dll"
    end
  end
end

desc "generate initializer.generated.cs file for IronRuby.Libraries"
task :gen do
  IronRuby.source_context do
    cmd = "#{build_path + 'ClassInitGenerator.exe'} #{build_path + 'IronRuby.Libraries.dll'} /libraries:Ruby.Builtins;Ruby.StandardLibrary.Threading;Ruby.StandardLibrary.Sockets;Ruby.StandardLibrary.OpenSsl;Ruby.StandardLibrary.Digest;Ruby.StandardLibrary.Zlib;Ruby.StandardLibrary.StringIO;Ruby.StandardLibrary.StringScanner;Ruby.StandardLibrary.Enumerator;Ruby.StandardLibrary.FunctionControl;Ruby.StandardLibrary.FileControl;Ruby.StandardLibrary.BigDecimal /out:#{get_source_dir(:libraries) + 'Initializers.Generated.cs'}"
    exec_net cmd
    cmd = "#{build_path + 'ClassInitGenerator'} #{build_path + 'IronRuby.Libraries.Yaml.dll'} /libraries:Ruby.StandardLibrary.Yaml /out:#{get_source_dir(:yaml) + 'Initializer.Generated.cs'}"
    exec_net cmd
  end
end

def path_exists?(paths, command)
  paths.each do |path|
    return true if (path + command).exists?
  end
  false
end

STDLIB_CLASSES = {}

def walk_classes(klass)
  klass.constants.each do |constant|
    current_klass = klass.const_get(constant)
    if current_klass.is_a?(Module)
      return if STDLIB_CLASSES.has_key?(current_klass.name)
      STDLIB_CLASSES[current_klass.name] = true
      walk_classes(current_klass)
    end
  end
end

desc "perform gap analysis on app's library method usage and IronRuby"
task :gap => [:compile_scanner] do
  IronRuby.source_context do
    libraries_file = Pathname.new(Dir.tmpdir) + "libraries.txt"
    exec "#{build_path + 'IronRuby.Libraries.Scanner.exe'} > \"#{libraries_file}\""

    library_methods = {}
    IO.foreach(libraries_file) { |method| library_methods[method.strip] = true }

    # Generate list of methods used by program
    trace_output_stream = File.open(Pathname.new(Dir.tmpdir) + 'trace.txt', 'w')
    function_table = {}

    ARGV.delete_at 0
    if ARGV.length < 1
      rake_output_message "usage: rake gap program [args]"
      exit(-1)
    end

    app_name = ARGV.first
    ARGV.delete_at 0

    walk_classes(self.class)

    set_trace_func proc { |event, file, line, id, binding, klass|
      if event == "c-call" || event == "call"
        method_name = klass.to_s + "#" + id.to_s
        function_table[method_name] = true
      end
    }

    at_exit do
      rake_output_message 'shutdown ...'
      function_table.keys.sort.each do |method_name|
        class_name = method_name.split("#").first
        if STDLIB_CLASSES.has_key?(class_name) && !library_methods.has_key?(method_name)
          # output methods that aren't in standard library
          trace_output_stream.puts method_name
        end
      end
      trace_output_stream.close
    end

    load app_name
    rake_output_message 'about to exit ...'
    exit
  end
end

desc "is the environment setup for an IronRuby dev?"
task :happy do
  IronRuby.source_context do
    commands = ENV['mono'].nil? ? ['resgen.exe', 'csc.exe'] : ['resgen', 'gmcs']
    commands += ['tf.exe', 'svn.exe'] if IronRuby.is_merlin?

    paths = ENV['PATH'].split(File::PATH_SEPARATOR).collect { |path| Pathname.new path }

    failure = false
    commands.each do |command|
      if !path_exists? paths, command
        rake_output_message "Cannot find #{command} on system path."
        failure = true
      end
    end

    if failure
      rake_output_message "\n"
      rake_output_message "***** Missing commands! You must have the .NET redist and the SDK"
      rake_output_message "***** (for resgen.exe) installed. If you are synchronizing source"
      rake_output_message "***** trees *inside* Microsoft, you must have both tfs.exe and"
      rake_output_message "***** svn.exe on your path."
      abort
    end
  end
end

# New mspec tasks for developers - these set a new regression baseline, run the
# regression tests, reports why a regression test failed, and test a specific class 
# while ignoring exclusions.

def run_specs(method_name) 
  ARGV.delete_at 0
  runner = MSpecRunner.new

  IronRuby.source_context do
    if ARGV.length == 0
      runner.send(:"#{method_name}_all_core", path_to_ir)
    elsif ARGV.length == 1
      runner.send(:"#{method_name}", path_to_ir, ARGV.first)
    else
      rake_output_message "usage: rake #{method_name} [class]"
      exit(-1)
    end
  end
  runner
end

desc "yell if the rubyspec test environment is not setup"
task :testhappy do
  spec_env_ready = File.exist?(UserEnvironment::RUBYSPEC) && File.exist?(UserEnvironment::MSPEC) && File.exist?(UserEnvironment::TAGS)

  if not spec_env_ready
  	rake_output_message "\n"
  	rake_output_message "***** Missing rubyspec test environment! You must have rubyspec, mspec, and ironruby-tags"
  	rake_output_message "***** 1. get GIT from http://code.google.com/p/msysgit/"
  	rake_output_message "***** 2. run: git clone git://github.com/jflam/ironruby-tags.git"
  	rake_output_message "***** 3. run: git clone git://github.com/jflam/mspec.git"
  	rake_output_message "***** 4. run: git clone git://github.com/jflam/rubyspec.git"
  	rake_output_message "***** 5. run: runfirst.cmd"
  	rake_output_message "***** 6. edit: %USERPROFILE%\.irconfig.rb (C:\Users\ironman\.irconfig.rb)"
  end
end

desc "generate new baseline against a class"
task :baseline => [:testhappy] do
  run_specs(:baseline).report
  exit
end

desc "run specs against a class, ignoring all exclusions"
task :test => [:testhappy] do
  run_specs :test
end

desc "show report of why a regression test failed"
task :why_regression => [:testhappy] do
  run_specs :why_regression
  exit
end

desc "run regression tests using mspec"
task :regression => [:testhappy] do
  run_specs(:regression).report
  exit
end

task :default => [:happy] do
  Rake.application.options.show_tasks = true
  Rake.application.options.show_task_pattern = Regexp.new('.')
  Rake.application.display_tasks_and_comments
end
