# **************************************************************************** # # Copyright (c) Microsoft Corporation. # # This source code is subject to terms and conditions of the Microsoft Public License. A # copy of the license can be found in the License.html file at the root of this distribution. If # you cannot locate the Microsoft Public License, please send an email to # ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound # by the terms of the Microsoft Public License. # # You must not remove this notice, or any other, from this software. # # # **************************************************************************** require 'rexml/document' require 'fileutils' ENV['HOME'] ||= ENV['USERPROFILE'] SVN_ROOT = Pathname.new 'c:/svn/trunk' DEFAULT_ROBOCOPY_OPTIONS = "/XF *#{EXCLUDED_EXTENSIONS.join(' *')} /NP /COPY:DAT /A-:R " class String def change_tag!(name, value = '') pattern = Regexp.new "\<#{name}\>(.*?)\<\/#{name}\>", Regexp::MULTILINE self.gsub! pattern, "\<#{name}\>#{value}\<\/#{name}\>" end def change_tag_value!(name, old_value, new_value) pattern = Regexp.new "\<#{name}\>#{old_value}\<\/#{name}\>", Regexp::MULTILINE self.gsub! pattern, "\<#{name}\>#{new_value}\<\/#{name}\>" end def change_configuration!(name, value) source = self.clone group = "" found = false self.replace '' source.each do |line| match = line.match /\/ index = match.nil? ? -1 : match.begin(0) self << (found && index > 0 ? " " * 4 + "#{value}\n" : line) found = true if line.include? group found = false if line.include? "" end end end module SvnProvider def add(path) exec_f "svn add --force #{path}" end def delete(path) exec_f "svn delete --force #{path}" end def checkout(path) end end module TfsProvider def add(path) exec_f "tf add #{path}" end def delete(path) exec_f "tf delete #{path}" end def checkout(path) exec_f "tf checkout #{path}" end end class Configuration class Group attr_reader :super_group def initialize(name, super_group) @name = name @switches = {} @references = [] @super_group = super_group end def switches(config, *args) @switches[config] ||= [] @switches[config] += args end def references(*refs) @references += refs end def remove_switches(config, *args) args.each { |arg| @switches[config].delete arg } unless @switches[config].nil? end def get_switches(config) all = @switches[:all].nil? ? [] : @switches[:all] (@switches[config].nil? ? [] : @switches[config]) + all end def get_references @references end def framework_path(*args, &b) @framework_path = (if args.length == 0 b.call elsif args.length == 1 args.first else raise 'framework_path must be called with either a path or a block that defines a path' end << nil) end def resolve_framework_path(path) @framework_path.each do |candidate| raise "cannot resolve path #{path}" if candidate.nil? candidate_path = candidate + path break candidate_path if candidate_path.file? end end end def self.define(&b) @master = {} instance_eval(&b) end def self.group(*args, &b) name = args.first super_group = args.length == 2 ? @master[args.last] : nil @master[name] ||= Group.new(name, super_group) @master[name].instance_eval(&b) end def self.get_switches(group, config) result = [] current = @master[group] while !current.nil? result += current.get_switches(config) current = current.super_group end result end def self.get_references(group) result = [] current = @master[group] while !current.nil? result += current.get_references current = current.super_group end result.map { |assembly| resolve_framework_path(group, assembly) } end def self.resolve_framework_path(group, path) @master[group].resolve_framework_path(path) end end Configuration.define do group(:common) { switches :all, 'nologo', 'noconfig', 'nowarn:1591,1701,1702', 'errorreport:prompt', 'warn:4', 'warnaserror-' switches :debug, 'define:"DEBUG;TRACE;MICROSOFT_SCRIPTING_CORE"', 'debug+', 'debug:full', 'optimize-' switches :release, 'define:TRACE', 'optimize+' references 'System.dll' } group(:desktop, :common) { references 'System.Configuration.dll' } group(:silverlight, :common) { switches :all, 'define:SILVERLIGHT', 'nostdlib+', 'platform:AnyCPU' references 'mscorlib.dll' } if ENV['mono'].nil? group(:desktop) { framework_path [Pathname.new(ENV['windir'].dup) + 'Microsoft.NET/Framework/v2.0.50727', Pathname.new('c:\program files\reference assemblies\microsoft\framework\v3.5')] } group(:silverlight) { framework_path [Pathname.new('c:/program files/microsoft silverlight/2.0.30523.6')] } else group(:mono) { framework_path { libdir = IO.popen('pkg-config --variable=libdir mono').read.strip [Pathname.new(libdir) + 'mono' + '2.0'] } switches :all, 'noconfig' remove_switches ['warnaserror+'] } end end class ProjectContext Mapping = Struct.new(:merlin_path, :svn_path, :recurse) class CommandContext # Low-level command helpers def is_test? ENV['test'] == 'true' end def exec(cmd) if is_test? rake_output_message ">> #{cmd}" else sh cmd end end def exec_net(cmd) if ENV['mono'].nil? exec cmd else exec "mono #{cmd}" end end def exec_f(cmd) begin exec(cmd) rescue end end # context is either :source or :target. project_context is reference # to ProjectContext subclass class def initialize(context, project_context) if context == :source || context == :target @project_context = project_context @context = context else raise "CommandContext#initialize(context) must be :source or :target" end end def get_mapping(name) mapping = @project_context.resolve(name) raise "cannot find #{name} in ProjectContext" if mapping.nil? mapping end # There are three things to consider when getting the source directory path # 1) The context (source or destination) we are running in # 2) The name of the mapping that we want to look up the source path in # 3) Whether we are running in MERLIN_ROOT or SVN_ROOT def get_source_dir(name) mapping = get_mapping name context_path = @project_context.source context_path + (@project_context.is_merlin? ? mapping.merlin_path : mapping.svn_path) end # Getting the target directory path is the same as source except for the # reversal of logic around whether to get svn_path or merlin_path based on # is_merlin? status def get_target_dir(name) mapping = get_mapping name context_path = @project_context.target context_path + (@project_context.is_merlin? ? mapping.svn_path : mapping.merlin_path) end def get_relative_target_dir(name) mapping = get_mapping name @project_context.is_merlin? ? mapping.svn_path : mapping.merlin_path end def is_recursive?(name) get_mapping(name).recurse end # General filesystem related methods def copy_dir(source_dir, target_dir, options = '') log_filename = Pathname.new(Dir.tmpdir) + "rake_transform.log" exec_f %Q{robocopy "#{source_dir}" "#{target_dir}" #{DEFAULT_ROBOCOPY_OPTIONS} "/LOG+:#{log_filename}" #{options}} end def copy_to_temp_dir(name, temp_dir, extras = []) IronRuby.source_context do source = get_source_dir(name) target = get_relative_target_dir(name) if is_recursive? name source_dirs = source.filtered_subdirs(extras) source_dirs.each { |dir| copy_dir dir, temp_dir + target + dir.relative_path_from(source) } else copy_dir source, temp_dir + target end end end def del(name, *paths) dir = name.is_a?(Symbol) ? get_source_dir(name) : name Dir.chdir(dir) { paths.each { |path| exec_f %Q{del "#{path}"} } } end def chdir(env, &b) dir = env.is_a?(Symbol) ? get_source_dir(env) : env Dir.chdir(dir) { instance_eval(&b) } end def rd(name) path = name.is_a?(Symbol) ? get_source_dir(name) : name FileUtils.rm_rf path end def mkdir(name) path = name.is_a?(Symbol) ? get_source_dir(name) : name FileUtils.mkdir_p path end def generate_temp_dir layout = Pathname.new(Dir.tmpdir) + 'layout' del Pathname.new(Dir.tmpdir), 'rake_transform.log' rd layout mkdir layout layout end # Source transformation related methods def diff_directories(temp_dir) source_dirs, target_dirs = [], [] nodes = [:root, :gppg, :dlr_core, :dlr_libs, :ironruby, :libraries, :tests, :console, :generator, :test_runner, :scanner, :yaml, :stdlibs, :ironlibs] nodes.each do |node| if is_recursive? node source_dirs += (temp_dir + get_relative_target_dir(node)).filtered_subdirs.map { |d| d.relative_path_from(temp_dir).downcase } # Target directory may not exist, so we only add if we find it there if get_target_dir(node).directory? target_dirs += get_target_dir(node).filtered_subdirs.map { |d| d.relative_path_from(@project_context.target).downcase } end else # This is also an unusual case - since there is a 1:1 mapping by # definition in a non-recursive directory mapping, this will be # flagged only as a change candidate and not an add or a delete. source_dirs << get_relative_target_dir(node).downcase # Target directory may not exist, so we only add if we find it there target_dirs << get_relative_target_dir(node).downcase if get_target_dir(node).directory? end end added = source_dirs - target_dirs removed = target_dirs - source_dirs change_candidates = source_dirs & target_dirs return added, removed, change_candidates end def push_to_target(temp_dir) rake_output_message "\n#{'=' * 78}\nApplying source changes to target source repository\n\n" rake_output_message "Computing directory structure changes ...\n" added, removed, change_candidates = diff_directories(temp_dir) dest = @project_context.target rake_output_message "Adding new directories to target source control\n" added.each do |dir| copy_dir(temp_dir + dir, dest + dir) add(dest + dir) end rake_output_message "Deleting directories from target source control\n" removed.each do |dir| rd dest + dir delete dest + dir end rake_output_message "Copying files in changed directories to target source control\n" change_candidates.each do |dir| src_file_list = (temp_dir + dir).filtered_files dest_file_list = (dest + dir).filtered_files added = src_file_list - dest_file_list removed = dest_file_list - src_file_list change_candidates = src_file_list & dest_file_list added.each do |file| copy temp_dir + dir + file, dest + dir + file add dest + dir + file end removed.each do |file| delete dest + dir + file end change_candidates.each do |file| source_file = temp_dir + dir + file dest_file = dest + dir + file if !compare_file(source_file, dest_file) checkout dest_file copy source_file, dest_file end end end end # Compiler-related methods def resgen(base_path, resource_map) resource_map.each_pair do |input, output| exec %Q{resgen "#{base_path + input.dup}" "#{build_path + output}"} end end def configuration ENV['configuration'].nil? ? :debug : ENV['configuration'].to_sym end def clr if ENV['mono'].nil? ENV['clr'].nil? ? :desktop : ENV['clr'].to_sym else :mono end end def platform ENV['platform'].nil? ? :windows : ENV['platform'].to_sym end def resolve_framework_path(file) Configuration.resolve_framework_path(clr, file) end def build_path get_source_dir(:build) + "#{clr == :desktop ? configuration : "#{clr}_#{configuration}"}" end def compiler_switches Configuration.get_switches(clr, configuration) end def references(refs, working_dir) references = Configuration.get_references(clr) refs.each do |ref| references << if ref =~ /^\!/ resolve_framework_path(ref[1..ref.length]) else (build_path + ref).relative_path_from(working_dir) end end unless refs.nil? references end def get_case_sensitive_path(pathname) filename = pathname.basename.downcase dir = Pathname.new pathname.dirname result = dir.entries.find { |p| p.downcase.to_s == filename } (Pathname.new(pathname.dirname) + result).to_s end def get_compile_path_list cs_proj_files = Dir['*.csproj'] if cs_proj_files.length == 1 doc = REXML::Document.new(File.open(cs_proj_files.first)) result = doc.elements.collect("/Project/ItemGroup/Compile") { |c| "\"#{c.attributes['Include']}\"" } result.delete_if { |e| e =~ /Silverlight\\SilverlightVersion.cs/ } if ENV['mono'].nil? result else result.map { |p| get_case_sensitive_path(p).gsub('\\', '/') } end else raise ArgumentError.new("Found more than one .csproj file in directory! #{cs_proj_files.join(", ")}") end end def compile(name, args) working_dir = get_source_dir(name) build_dir = build_path Dir.chdir(working_dir) do |p| cs_args = ["out:\"#{build_dir + args[:output]}\""] cs_args += references(args[:references], working_dir).map { |ref| "r:\"#{ref}\"" } cs_args += compiler_switches cs_args += args[:switches] unless args[:switches].nil? unless args[:resources].nil? resgen working_dir, args[:resources] args[:resources].each_value { |res| cs_args << "resource:\"#{build_path + res}\"" } end switches = '' cs_args.each { |opt| switches << ' /' + opt } cmd = CS_COMPILER + switches + ' ' + get_compile_path_list.join(" ") exec cmd end end # Project transformation methods def replace_output_path(contents, old, new) contents.gsub! Regexp.new(Regexp.escape("#{old}"), Regexp::IGNORECASE), "#{new}" end def replace_doc_path(contents, old, new) contents.gsub! Regexp.new(Regexp.escape("#{old}"), Regexp::IGNORECASE), "#{new}" end def replace_key_path(contents, old, new) contents.gsub! Regexp.new(Regexp.escape("#{old}"), Regexp::IGNORECASE), "#{new}" end def replace_import_project(contents, old, new) contents.gsub! Regexp.new(Regexp.escape(""), Regexp::IGNORECASE), "" end def replace_post_build_event(contents, old, new) contents.gsub! Regexp.new(Regexp.escape("#{old}"), Regexp::IGNORECASE), "#{new}" end def replace_app_config_path(contents, old, new) contents.gsub! Regexp.new(Regexp.escape(%Q{}), Regexp::IGNORECASE), %Q{} end def transform_project(name, project) path = get_target_dir(name) + project rake_output_message "Transforming: #{path}" contents = path.read # Extract the project name from .csproj filename project_name = /(.*)\.csproj/.match(project)[1] if @project_context.is_merlin? contents.change_tag! 'SccProjectName' contents.change_tag! 'SccLocalPath' contents.change_tag! 'SccAuxPath' contents.change_tag! 'SccProvider' contents.change_tag! 'DelaySign', 'false' contents.change_tag! 'SignAssembly', 'false' contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG' contents.change_configuration! 'Release|AnyCPU', 'TRACE' contents.change_configuration! 'Silverlight Debug|AnyCPU', 'TRACE;DEBUG;SILVERLIGHT' contents.change_configuration! 'Silverlight Release|AnyCPU', 'TRACE;SILVERLIGHT' if block_given? yield contents else 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\\' end else contents.change_tag! 'SccProjectName', 'SAK' contents.change_tag! 'SccLocalPath', 'SAK' contents.change_tag! 'SccAuxPath', 'SAK' contents.change_tag! 'SccProvider', 'SAK' contents.change_tag! 'DelaySign', 'true' contents.change_tag! 'SignAssembly', 'true' contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG;SIGNED' contents.change_configuration! 'Release|AnyCPU', 'TRACE;SIGNED' contents.change_configuration! 'Silverlight Debug|AnyCPU', 'TRACE;DEBUG;SILVERLIGHT' contents.change_configuration! 'Silverlight Release|AnyCPU', 'TRACE;SILVERLIGHT' if block_given? yield contents 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_key_path contents, '..\..\RubyTestKey.snk', '..\..\..\MSSharedLibKey.snk' end end path.open('w+') { |f| f.write contents } end end # The Rakefile must always be found in the root directory of the source tree. # If ENV['MERLIN_ROOT'] is defined, then we know that we are running on # a machine with an enlistment in the MERLIN repository. This will enable # features that require a source context and a destination context (such as # pushing to / from MERLIN). Otherwise, destination context will always be # nil and we will throw on an attempt to do operations that require a # push. private def self.init_context @rakefile_dir = Pathname.new(File.dirname(File.expand_path(__FILE__)).downcase) if ENV['MERLIN_ROOT'].nil? # Initialize the context for an external contributor who builds from # a non-MERLIN command prompt @source = @rakefile_dir @target = nil else # Initialize @source and @target to point to the right places based # on whether we are within MERLIN_ROOT or SVN_ROOT @merlin_root = Pathname.new(ENV['MERLIN_ROOT'].downcase) + '../../' # hack for changes in TFS layout @ruby_root = @merlin_root + 'merlin/main/languages/ruby' if @rakefile_dir == @ruby_root @source = @merlin_root @target = SVN_ROOT elsif @rakefile_dir == SVN_ROOT @source = @rakefile_dir @target = @merlin_root else raise <<-EOF Rakefile is at #{@rakefile_dir}. This is neither the SVN_ROOT nor the MERLIN_ROOT. Possible causes of this are running from a non-MERLIN command prompt (where MERLIN_ROOT environment variable is defined) or if the SVN_ROOT constant in the Rakefile does not point to where you downloaded the SVN repository for IronRuby. EOF end end @map = {} @initialized = true end def self.make_pathname(path) elements = path.split '/' raise "must be an an array with at least one element: #{elements}" if elements.length < 1 result = Pathname.new elements.first (1..elements.length-1).each { |i| result += elements[i] } result end public def self.map(name, args) init_context unless @initialized @map[name] = Mapping.new(make_pathname(args[:merlin]), make_pathname(args[:svn]), (args[:recurse].nil? ? true : args[:recurse])) end def self.resolve(name) @map[name] end def self.is_merlin? @merlin_root == @source end def self.source_context(&b) context = CommandContext.new(:source, self) context.extend(is_merlin? ? SvnProvider : TfsProvider) context.instance_eval(&b) context end def self.target_context(&b) if @target.nil? raise <<-EOF Cannot invoke commands against target_context if you are not running in a MERLIN_ROOT context. External folks should never see this error as they should never be running commands that require moving things between different contexts. EOF else # Note that this is a bit unusual - the source control commands in the # target are identical to the source control commands for the source. This # is due to the semantics of the operation. The source is always # authoritative in these kinds of push scenarios, so you'll never want to # mutate the source repository, only the target repository. context = CommandContext.new(:target, self) context.extend(is_merlin? ? SvnProvider : TfsProvider) context.instance_eval(&b) context end end def self.source @source end def self.target @target end end class IronRuby < ProjectContext map :root, :merlin => 'merlin/main/languages/ruby', :svn => '.', :recurse => false map :gppg, :merlin => 'merlin/main/utilities/gppg', :svn => 'bin', :recurse => false map :dlr_core, :merlin => 'ndp/fx/src/core/microsoft/scripting', :svn => 'src/microsoft.scripting.core' map :dlr_libs, :merlin => 'merlin/main/runtime/microsoft.scripting', :svn => 'src/microsoft.scripting' map :ironruby, :merlin => 'merlin/main/languages/ruby/ruby', :svn => 'src/ironruby' map :libraries, :merlin => 'merlin/main/languages/ruby/libraries.lca_restricted', :svn => 'src/IronRuby.Libraries' map :yaml, :merlin => 'merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml', :svn => 'src/yaml' map :tests, :merlin => 'merlin/main/languages/ruby/tests', :svn => 'tests/ironruby' map :console, :merlin => 'merlin/main/languages/ruby/console', :svn => 'utils/ironruby.console' map :generator, :merlin => 'merlin/main/languages/ruby/classinitgenerator', :svn => 'utils/ironruby.classinitgenerator' map :test_runner, :merlin => 'merlin/main/languages/ruby/ironruby.tests', :svn => 'utils/IronRuby.Tests' map :scanner, :merlin => 'merlin/main/languages/ruby/utils/ironruby.libraries.scanner', :svn => 'utils/ironruby.libraries.scanner' map :build, :merlin => 'merlin/main/bin', :svn => 'build' map :stdlibs, :merlin => 'merlin/external/languages/ruby/redist-libs', :svn => 'lib' map :ironlibs, :merlin => 'merlin/main/languages/ruby/libs', :svn => 'lib/IronRuby' map :lang_root, :merlin => 'merlin/main', :svn => '.' end # Spec runner helpers class MSpecRunner attr_accessor :files, :examples, :expectations, :failures, :errors, :summaries attr_reader :tempdir SUMMARY_PARSER = /(\d+) files?, (\d+) examples?, (\d+) expectations?, (\d+) failures?, (\d+) errors?/ def initialize @files = 0 @examples = 0 @expectations = 0 @failures = 0 @errors = 0 @summaries = [] @tempdir = Dir.tmpdir end def regression(ruby, klass, method = nil) cmd = %Q{"#{UserEnvironment.mri_binary}" "#{UserEnvironment.mspec}/bin/mspec" ci -t #{ruby} -fm -V -B "#{UserEnvironment.config}" "#{UserEnvironment.rubyspec}/1.8/core/#{klass}"} cmd += "/#{method}_spec.rb" unless method.nil? cmd += " > #{tempdir}/out.txt" system cmd File.open("#{tempdir}/out.txt", 'r') do |f| lines = f.readlines lines.each do |line| if SUMMARY_PARSER =~ line @files += $1.to_i @examples += $2.to_i @expectations += $3.to_i @failures += $4.to_i @errors += $5.to_i @summaries << "#{klass}: #{$1.to_i} files, #{$2.to_i} examples, #{$3.to_i} expectations, #{$4.to_i} failures, #{$5.to_i} errors" end end end end def all_core(method, ruby) send method, ruby, '*' end def why_regression(ruby, klass, method = nil) cmd = %Q{#{UserEnvironment.mri_binary} "#{UserEnvironment.mspec}/bin/mspec" ci -t #{ruby} -fs -V -B "#{UserEnvironment.config}" "#{UserEnvironment.rubyspec}/1.8/core/#{klass}"} cmd += "/#{method}_spec.rb" unless method.nil? system cmd end def test(ruby, klass, method = nil) cmd = %Q{#{UserEnvironment.mri_binary} "#{UserEnvironment.mspec}/bin/mspec" run -t #{ruby} -Gcritical -V -fs -B "#{UserEnvironment.config}" "#{UserEnvironment.rubyspec}/1.8/core/#{klass}"} cmd += "/#{method}_spec.rb" unless method.nil? system cmd end def baseline(ruby, klass, method = nil) cmd = %Q{#{UserEnvironment.mri_binary} "#{UserEnvironment.mspec}/bin/mspec" tag -t #{ruby} -fs -V -Gcritical -B "#{UserEnvironment.config}" "#{UserEnvironment.rubyspec}/1.8/core/#{klass}"} cmd << "/#{method}_spec.rb" unless method.nil? system cmd end def generate_critical_tags lines = [] return unless File.exist? "#{UserEnvironment.tags}\\critical_tags.txt" File.open("#{UserEnvironment.tags}\\critical_tags.txt", 'r') do |f| f.readlines.each do |line| file,_,tag,desc = line.chomp.split(":") fulltag = tag << ":" << desc filepath = "#{UserEnvironment.tags}/1.8/#{file}" filedir = File.dirname(filepath) FileUtils.mkdir_p(filedir) unless File.exist?(filedir) FileUtils.touch(filepath) unless File.exist?(filepath) File.open(filepath, File::RDWR) do |tagfile| if tagfile.readlines.grep(Regexp.new(Regexp.escape(tag))).empty? tagfile.puts fulltag.strip end end end end end def report summaries.each { |s| puts s } puts "\nSummary:\n" puts "#{summaries.length} types, #{files} files, #{examples} examples, #{expectations} expectations, #{failures} failures, #{errors} errors" end end class UserEnvironment # Find path to named executable def self.find_executable(executable) executable.downcase! result = [] search_path = ENV['PATH'].split(File::PATH_SEPARATOR) search_path.each do |dir| path = Pathname.new(dir) file_path = path + executable result << file_path.dirname if file_path.file? file_path = path + (executable + '.exe') result << file_path.dirname if file_path.file? end result end def self.mri_binary self.mri + '/bin/ruby.exe' end def self.method_missing(sym, *args) name = sym.to_s if name =~ /\?$/ File.exist?(self.send(name[0..-2])) elsif self.constants.include?(name.upcase) File.expand_path(UserEnvironment.const_get(name.upcase)) else raise NoMethodError.new("undefined method '#{name}' for #{self}", name, args) end end def initialize path_to_config = ENV['HOME'] + '/.irconfig.rb' load path_to_config if File.exist? path_to_config unless defined?(UserEnvironment::MRI) ruby_exe_paths = UserEnvironment.find_executable 'ruby' unless ruby_exe_paths.empty? UserEnvironment.const_set(:MRI, Pathname.new(ruby_exe_paths.first + '\..\\')) else raise ArgumentError.new("Could not find ruby.exe on your path") end end UserEnvironment.const_set(:TAGS, "#{ENV['HOME']}\\dev\\ironruby-tags".gsub('\\', '/')) unless defined?(UserEnvironment::TAGS) UserEnvironment.const_set(:RUBYSPEC, "#{ENV['HOME']}\\dev\\rubyspec".gsub('\\', '/')) unless defined?(UserEnvironment::RUBYSPEC) UserEnvironment.const_set(:MSPEC, "#{ENV['HOME']}\\dev\\mspec".gsub('\\', '/')) unless defined?(UserEnvironment::MSPEC) UserEnvironment.const_set(:CONFIG, "#{ENV['HOME']}\\dev\\default.mspec".gsub('\\', '/')) unless defined?(UserEnvironment::CONFIG) end end UE = UserEnvironment.new