diff -Nur a/bin/multiruby b/bin/multiruby --- a/bin/multiruby 2023-06-01 16:53:33.239069974 +0800 +++ b/bin/multiruby 2023-06-01 16:23:24.911069974 +0800 @@ -1,37 +1,24 @@ #!/usr/bin/env ruby -w -require "yaml" +require 'multiruby' -class Array - def human_sort - sort_by { |item| item.to_s.split(/(\d+)/).map { |e| [e.to_i, e] } } - end -end - -root_dir = File.expand_path "~/.rubies" - -versions = Dir.chdir(root_dir) { Dir["*"] }.human_sort +root_dir = Multiruby.root_dir def setenv dir - ENV["PATH"] = "#{dir}/bin:#{ENV["PATH"]}" -end + bin = "#{dir}/bin" + gem = Dir["#{dir}/lib/ruby/gems/*"].first -def unsetenv key - if ENV[key] then - warn "WARNING: %s is set to %p. Removing..." % [key, ENV[key]] - ENV.delete key - end + ENV['PATH'] = bin + File::PATH_SEPARATOR + ENV['PATH'] + ENV['GEM_HOME'] = gem + ENV['GEM_PATH'] = gem end -unsetenv "GEM_HOME" -unsetenv "GEM_PATH" - ## -# multiruby -1 2.0 ruby_args... +# multiruby -1 1.8.7 ruby_args... if ARGV.first == "-1" then ARGV.shift - vers = Dir["#{root_dir}/#{ARGV.shift}*"] + vers = Dir["#{root_dir}/install/#{ARGV.shift}*"] abort "ambiguous version: #{vers.map { |p| File.basename p }.inspect}" if vers.size != 1 @@ -42,24 +29,11 @@ exec "#{dir}/bin/ruby", *ARGV end -def maybe_load_yaml_file config - if config then - if YAML.respond_to? :safe_load_file then - YAML.safe_load_file config, permitted_classes: [Regexp, Symbol] - else - YAML.load_file config - end - end -end +versions = Multiruby.build_and_install +versions = ENV['VERSIONS'].split(/:/) if ENV.has_key? 'VERSIONS' -rcpath = File.expand_path "~/.hoerc" -skip = if File.exist? rcpath then - conf = maybe_load_yaml_file rcpath - conf["multiruby_skip"] || [] - end -excl = (ENV["EXCLUDED_VERSIONS"] || "").split(/:/) + skip -unless excl.empty? then - excludes = Regexp.union(*excl) +if ENV.has_key? 'EXCLUDED_VERSIONS' then + excludes = Regexp.union(*ENV['EXCLUDED_VERSIONS'].split(/:/)) versions = versions.delete_if { |v| v =~ excludes } end @@ -68,12 +42,13 @@ results = {} versions.each do |version| - dir = "#{root_dir}/#{version}" + dir = "#{root_dir}/install/#{version}" ruby = "#{dir}/bin/ruby" - ver = version.delete_prefix "ruby-" + + ruby.sub!(/bin.ruby/, 'bin/rbx') if version =~ /rubinius/ puts - puts "VERSION = #{ver}" + puts "VERSION = #{version}" cmd = [ruby, ARGV].flatten.map { |s| s =~ /\"/ ? "'#{s}'" : s }.join(' ') cmd.sub!(/#{ENV['HOME']}/, '~') puts "CMD = #{cmd}" @@ -84,7 +59,7 @@ system ruby, *ARGV puts puts "RESULT = #{$?}" - results[ver] = $? + results[version] = $? # restore the path to original state ENV['PATH'] = original_path diff -Nur a/bin/multiruby_setup b/bin/multiruby_setup --- a/bin/multiruby_setup 1970-01-01 08:00:00.000000000 +0800 +++ b/bin/multiruby_setup 2023-06-01 16:23:28.919069974 +0800 @@ -0,0 +1,74 @@ +#!/usr/bin/env ruby -w + +require 'multiruby' + +ENV.delete 'RUBYOPT' + +ARGV << "help" if ARGV.empty? + +Dir.chdir Multiruby.root_dir +Multiruby.setup_dirs(false) + +ARGV.each do |spec| + case spec + when "-h", "--help", "help" then + Multiruby.help + exit 0 + when "the_usual" then # TODO: update #help + ARGV.push(*Multiruby::TAGS.map { |v| "mri:tar:#{v.gsub(/_/, '.')}" }) + ARGV << "build" << "update:rubygems" + system "multigem install --no-ri --no-rdoc rake minitest ZenTest gemcutter rubyforge hoe" + when "build" then + Multiruby.build_and_install + when "clean" then + Multiruby.clean + when "list" then + Multiruby.list + when /rm:(.*)/ then + Multiruby.rm $1 + when "rubygems:merge" then + Multiruby.merge_rubygems + when "rubygems:update", "update:rubygems" then + Multiruby.update_rubygems + ARGV << "build" + when "update" then + Multiruby.update + when "tags" then + p Multiruby.tags + when "mri:svn:current" then + ARGV << "mri:svn:releases" << "mri:svn:branches" << "build" + when "mri:svn:releases" then + Multiruby::TAGS.each do |v| + latest = Multiruby.mri_latest_tag v + abort "Can't find tag #{v}" unless latest + ARGV << "mri:svn:tag:#{latest}:mri_rel_#{v}" + end + ARGV << "build" + when /mri:svn:branch:(.*)/ then + ver = "branches/ruby_#{$1}" unless ver == "trunk" + Multiruby.svn_co "#{Multiruby::MRI_SVN}/#{$1}", "mri_#{$1}" + ARGV << "build" + when "mri:svn:branches" then + Multiruby::BRANCHES.each do |v| + ARGV << "mri:svn:branch:#{v}" + end + ARGV << "build" + when /mri:svn:tag:(.*):(.*)/ then + Multiruby.svn_co "#{Multiruby::MRI_SVN}/tags/#{$1}", $2 + ARGV << "build" + when /mri:svn:tag:(.*)/ then + ARGV << "mri:svn:tag:#{$1}:#{$1}" << "build" + when /mri:list:(.*)/ then + v = $1 + ver = v[/\d+\.\d+/] + url = "#{Multiruby::RUBY_URL}/#{ver}/" + + puts Multiruby.matching_versions(url, v).join("\n") + when /mri:tar:(.*)/ then + Multiruby.fetch_tar $1 + ARGV << "build" + else + warn "unknown spec #{spec}" + end +end + diff -Nur a/lib/multiruby.rb b/lib/multiruby.rb --- a/lib/multiruby.rb 1970-01-01 08:00:00.000000000 +0800 +++ b/lib/multiruby.rb 2023-06-01 16:23:33.885069974 +0800 @@ -0,0 +1,413 @@ +require 'fileutils' +require 'open-uri' + +## +# multiruby_setup is a script to help you manage multiruby. +# +# usage: multiruby_setup [-h|cmd|spec...] +# +# cmds: +# +# -h, --help, help = show this help. +# build = build and install everything. used internally. +# clean = clean scm build dirs and remove non-scm build dirs. +# list = print installed versions. +# rm:$version = remove a particular version. +# rubygems:merge = symlink all rubygem dirs to one dir. +# tags = list all tags from svn. +# update = update svn builds. +# update:rubygems = update rubygems and nuke install dirs. +# +# specs: +# +# the_usual = alias for latest versions from tar + rubygems +# mri:svn:current = alias for mri:svn:releases and mri:svn:branches. +# mri:svn:releases = alias for supported releases of mri ruby. +# mri:svn:branches = alias for active branches of mri ruby. +# mri:svn:branch:$branch = install a specific $branch of mri from svn. +# mri:svn:tag:$tag = install a specific $tag of mri from svn. +# mri:tar:$version = install a specific $version of mri from tarball. +# +# environment variables: +# +# GEM_URL = url for rubygems tarballs +# MRI_SVN = url for MRI SVN +# RUBY_URL = url for MRI tarballs +# VERSIONS = what versions to install +# +# RUBYOPT is cleared on installs. +# +# NOTES: +# +# * you can add a symlink to your rubinius build into ~/.multiruby/install +# * I need patches/maintainers for other implementations. +# +module Multiruby + def self.env name, fallback; ENV[name] || fallback; end # :nodoc: + + TAGS = %w( 1_8_7 1_9_1 1_9_2) + BRANCHES = %w(1_8 1_8_7 1_9 trunk) + + VERSIONS = env('VERSIONS', TAGS.join(":").gsub(/_/, '.')).split(/:/) + MRI_SVN = env 'MRI_SVN', 'http://svn.ruby-lang.org/repos/ruby' + RUBY_URL = env 'RUBY_URL', 'http://ftp.ruby-lang.org/pub/ruby' + GEM_URL = env 'GEM_URL', 'http://files.rubyforge.vm.bytemark.co.uk/rubygems' + + HELP = [] + + File.readlines(__FILE__).each do |line| + next unless line =~ /^#( |$)/ + HELP << line.sub(/^# ?/, '') + end + + def self.build_and_install + ENV.delete 'RUBYOPT' + + root_dir = self.root_dir + versions = [] + + Dir.chdir root_dir do + self.setup_dirs + + rubygems = Dir["versions/rubygems*.tgz"] + abort "You should delete all but one rubygem tarball" if rubygems.size > 1 + rubygem_tarball = File.expand_path rubygems.last rescue nil + + Dir.chdir "build" do + Dir["../versions/*"].sort.each do |tarball| + next if tarball =~ /rubygems/ + + build_dir = File.basename tarball, ".tar.gz" + version = build_dir.sub(/^ruby-?/, '') + inst_dir = "#{root_dir}/install/#{version}" + + unless test ?d, inst_dir then + unless test ?d, build_dir then + if test ?d, tarball then + dir = File.basename tarball + FileUtils.ln_sf "../versions/#{dir}", "../build/#{dir}" + else + puts "creating #{inst_dir}" + Dir.mkdir inst_dir + run "tar zxf #{tarball}" + end + end + Dir.chdir build_dir do + puts "building and installing #{version}" + if test ?f, "configure.in" then + gnu_utils_build inst_dir + elsif test ?f, "Rakefile" then + rake_build inst_dir + else + raise "dunno how to build" + end + + if rubygem_tarball and version !~ /1[._-]9|mri_trunk|rubinius/ then + rubygems = File.basename rubygem_tarball, ".tgz" + run "tar zxf #{rubygem_tarball}" unless test ?d, rubygems + + Dir.chdir rubygems do + run "../ruby ./setup.rb --no-rdoc --no-ri", "../log.rubygems" + end + end + end + end + end + end + + versions = Dir["install/*"].map { |path| File.basename path } + end + + versions + end + + def self.clean + self.each_scm_build_dir do |style| + case style + when :svn then + if File.exist? "Rakefile" then + run "rake clean" + elsif File.exist? "Makefile" then + run "make clean" + end + else + FileUtils.rm_rf Dir.pwd + end + end + end + + def self.each_scm_build_dir + Multiruby.in_build_dir do + Dir["*"].each do |dir| + next unless File.directory? dir + Dir.chdir dir do + if File.exist?(".svn") then + yield :svn + else + yield :none + end + end + end + end + end + + def self.matching_versions url, matching=nil + file = URI.parse(url).read + + map = { + "preview" => "beta", + "rc" => "beta2", + "p" => "release", + "tar" => "aargh", + "gz" => "aargh", + } + + versions = file.scan(/href="(ruby.*tar.gz)"/).flatten.sort_by { |s| + s.scan(/\d+|[a-z]+/).map { |a| Integer(a) rescue map[a] || a } + } + + versions = versions.grep(/#{Regexp.escape(matching)}/) if matching + + versions + end + + def self.fetch_tar v + in_versions_dir do + warn " Determining latest version for #{v}" + ver = v[/\d+\.\d+/] + base = matching_versions("#{RUBY_URL}/#{ver}/", v).last + abort "Could not determine release for #{v}" unless base + url = File.join RUBY_URL, ver, base + unless File.file? base then + warn " Fetching #{base} via HTTP... this might take a while." + open(url) do |f| + File.open base, 'w' do |out| + out.write f.read + end + end + end + end + end + + def self.gnu_utils_build inst_dir + run "autoconf" unless test ?f, "configure" + run "./configure --enable-shared --prefix #{inst_dir}", "log.configure" unless + test ?f, "Makefile" + run "(nice make -j4 main; nice make main)", "log.build" + run "make install-nodoc", "log.install" + end + + def self.help + puts HELP.join + end + + def self.in_build_dir + in_root_dir "build" do + yield + end + end + + def self.in_install_dir + in_root_dir "install" do + yield + end + end + + def self.in_root_dir subdir = "" + Dir.chdir File.join(self.root_dir, subdir) do + yield + end + end + + def self.in_tmp_dir + in_root_dir "tmp" do + yield + end + end + + def self.in_versions_dir + in_root_dir "versions" do + yield + end + end + + def self.list + puts "Known versions:" + in_install_dir do + Dir["*"].sort.each do |d| + puts " #{d}" + end + end + end + + def self.merge_rubygems + in_install_dir do + gems = Dir["*/lib/ruby/gems"] + + unless test ?d, "../gems" then + FileUtils.mv gems.first, ".." + end + + gems.each do |d| + FileUtils.rm_rf d + FileUtils.ln_sf "../../../../gems", d + end + end + end + + def self.mri_latest_tag v + Multiruby.tags.grep(/#{v}/).last + end + + def self.rake_build inst_dir + run "rake", "log.build" + FileUtils.ln_sf "../build/#{File.basename Dir.pwd}", inst_dir + end + + def self.rm name + Multiruby.in_root_dir do + FileUtils.rm_rf Dir["*/#{name}"] + f = "versions/ruby-#{name}.tar.gz" + File.unlink f if test ?f, f + end + end + + def self.root_dir + root_dir = File.expand_path(ENV['MULTIRUBY'] || + File.join(ENV['HOME'], ".multiruby")) + + unless test ?d, root_dir then + puts "creating #{root_dir}" + Dir.mkdir root_dir, 0700 + end + + root_dir + end + + def self.run base_cmd, log = nil + cmd = base_cmd + cmd += " > #{log} 2>&1" if log + puts "Running command: #{cmd}" + raise "ERROR: Command failed with exit code #{$?}" unless system cmd + end + + def self.setup_dirs download = true + %w(build install versions tmp).each do |dir| + unless test ?d, dir then + puts "creating #{dir}" + Dir.mkdir dir + if dir == "versions" && download then + warn " Downloading initial ruby tarballs to ~/.multiruby/versions:" + VERSIONS.each do |v| + self.fetch_tar v + end + warn " ...done" + warn " Put other ruby tarballs in ~/.multiruby/versions to use them." + end + end + end + end + + def self.svn_co url, dir + Multiruby.in_versions_dir do + Multiruby.run "svn co #{url} #{dir}" unless File.directory? dir + FileUtils.ln_sf "../versions/#{dir}", "../build/#{dir}" + end + end + + def self.tags + tags = nil + Multiruby.in_tmp_dir do + cache = "svn.tag.cache" + File.unlink cache if Time.now - File.mtime(cache) > 3600 rescue nil + + File.open cache, "w" do |f| + f.write `svn ls #{MRI_SVN}/tags/` + end unless File.exist? cache + + tags = File.read(cache).split(/\n/).grep(/^v/).reject {|s| s =~ /preview/} + end + + tags = tags.sort_by { |t| t.scan(/\d+/).map { |s| s.to_i } } + end + + def self.update + # TODO: + # update will look at the dir name and act accordingly rel_.* will + # figure out latest tag on that name and svn sw to it trunk and + # others will just svn update + + clean = [] + + self.each_scm_build_dir do |style| + dir = File.basename(Dir.pwd) + warn dir + + case style + when :svn then + case dir + when /mri_\d/ then + system "svn cleanup" # just in case + svn_up = `svn up` + in_build_dir do + if svn_up =~ /^[ADUCG] / then + clean << dir + else + warn " no update" + end + FileUtils.ln_sf "../build/#{dir}", "../versions/#{dir}" + end + when /mri_rel_(.+)/ then + ver = $1 + url = `svn info`[/^URL: (.*)/, 1] + latest = self.mri_latest_tag(ver).chomp('/') + new_url = File.join(File.dirname(url), latest) + if new_url != url then + run "svn sw #{new_url}" + clean << dir + else + warn " no update" + end + else + warn " update in this svn dir not supported yet: #{dir}" + end + else + warn " update in non-svn dir not supported yet: #{dir}" + end + end + + in_install_dir do + clean.each do |dir| + warn "removing install/#{dir}" + FileUtils.rm_rf dir + end + end + end + + def self.update_rubygems + warn " Determining latest version for rubygems" + html = URI.parse(GEM_URL).read + + versions = html.scan(/href="rubygems-update-(\d+(?:\.\d+)+).gem/i).flatten + latest = versions.sort_by { |v| v.scan(/\d+/).map { |s| s.to_i } }.last + + Multiruby.in_versions_dir do + file = "rubygems-#{latest}.tgz" + unless File.file? file then + warn " Fetching rubygems-#{latest}.tgz via HTTP." + File.unlink(*Dir["rubygems*"]) + File.open file, 'w' do |f| + f.write URI.parse(GEM_URL+"/"+file).read + end + end + end + + Multiruby.in_build_dir do + FileUtils.rm_rf Dir["rubygems*"] + end + + Multiruby.in_install_dir do + FileUtils.rm_rf Dir["*"] + end + end +end diff -Nur a/Manifest.txt b/Manifest.txt --- a/Manifest.txt 2023-06-01 16:53:33.238069974 +0800 +++ b/Manifest.txt 2023-06-01 16:54:59.287069974 +0800 @@ -7,6 +7,7 @@ articles/how_to_use_zentest.txt bin/multigem bin/multiruby +bin/multiruby_setup bin/unit_diff bin/zentest example.txt @@ -14,6 +15,7 @@ example2.rb lib/focus.rb lib/functional_test_matrix.rb +lib/multiruby.rb lib/unit_diff.rb lib/zentest.rb lib/zentest_mapping.rb