ruby/tool/runruby.rb
KJ Tsanaktsidis 082962e857 Work around a hang in fork(2) on FreeBSD
See bug https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=271490

On FreeBSDk, it's possible for fork(2) in a multithreaded process to
hang because of a bug in the lock handling of the dynamic linker. This
is now fixed on FreeBSD master, but it would be good if we could work
around it for Ruby CI which is running 13.1.

Setting LD_BIND_NOW seems to work around the problem (probably because
the dynamic linker doesn't then need to resolve anything through the PLT
when it's first called).
2023-08-30 20:47:39 +01:00

182 lines
4.8 KiB
Ruby
Executable File

#!./miniruby
# Used by "make runruby", configure, and by hand to run a locally-built Ruby
# with correct environment variables and arguments.
show = false
precommand = []
srcdir = File.realpath('..', File.dirname(__FILE__))
case
when ENV['RUNRUBY_USE_GDB'] == 'true'
debugger = :gdb
when ENV['RUNRUBY_USE_LLDB'] == 'true'
debugger = :lldb
when ENV['RUNRUBY_YJIT_STATS']
use_yjit_stat = true
end
while arg = ARGV[0]
break ARGV.shift if arg == '--'
case arg
when '-C', /\A-C(.+)/m
ARGV.shift
Dir.chdir($1 || ARGV.shift)
next
end
/\A--([-\w]+)(?:=(.*))?\z/ =~ arg or break
arg, value = $1, $2
re = Regexp.new('\A'+arg.gsub(/\w+\b/, '\&\\w*')+'\z', "i")
case
when re =~ "srcdir"
srcdir = value
when re =~ "archdir"
archdir = value
when re =~ "cpu"
precommand << "arch" << "-arch" << value
when re =~ "extout"
extout = value
when re =~ "pure"
# obsolete switch do nothing
when re =~ "debugger"
require 'shellwords'
case value
when nil
debugger = :gdb
when "lldb"
debugger = :lldb
when "no"
else
debugger = Shellwords.shellwords(value)
end and precommand |= [:debugger]
when re =~ "precommand"
require 'shellwords'
precommand.concat(Shellwords.shellwords(value))
when re =~ "show"
show = true
when re =~ "chdir"
Dir.chdir(value)
else
break
end
ARGV.shift
end
unless defined?(File.realpath)
def File.realpath(*args)
path = expand_path(*args)
if File.stat(path).directory?
Dir.chdir(path) {Dir.pwd}
else
dir, base = File.split(path)
File.join(Dir.chdir(dir) {Dir.pwd}, base)
end
end
end
begin
conffile = File.realpath('rbconfig.rb', archdir)
rescue Errno::ENOENT => e
# retry if !archdir and ARGV[0] and File.directory?(archdir = ARGV.shift)
abort "#$0: rbconfig.rb not found, use --archdir option"
end
abs_archdir = File.dirname(conffile)
archdir ||= abs_archdir
$:.unshift(abs_archdir)
config = File.read(conffile)
config.sub!(/^(\s*)RUBY_VERSION\b.*(\sor\s*)\n.*\n/, '')
config = Module.new {module_eval(config, conffile)}::RbConfig::CONFIG
install_name = config["RUBY_INSTALL_NAME"]+config['EXEEXT']
ruby = File.join(archdir, install_name)
unless File.exist?(ruby)
abort "#{ruby} is not found.\nTry `make' first, then `make test', please.\n"
end
libs = [abs_archdir]
extout ||= config["EXTOUT"]
if extout
abs_extout = File.expand_path(extout, abs_archdir)
libs << File.expand_path("common", abs_extout) << File.expand_path(config['arch'], abs_extout)
end
libs << File.expand_path("lib", srcdir)
config["bindir"] = abs_archdir
env = {
# Test with the smallest possible machine stack sizes.
# These values are clamped to machine-dependent minimum values in vm_core.h
'RUBY_THREAD_MACHINE_STACK_SIZE' => '1',
'RUBY_FIBER_MACHINE_STACK_SIZE' => '1',
}
runner = File.join(abs_archdir, "exe/#{install_name}")
runner = nil unless File.exist?(runner)
abs_ruby = runner || File.expand_path(ruby)
env["RUBY"] = abs_ruby
env["GEM_PATH"] = env["GEM_HOME"] = File.expand_path(".bundle", srcdir)
env["GEM_COMMAND"] = "#{abs_ruby} -rrubygems #{srcdir}/bin/gem --backtrace"
env["PATH"] = [File.dirname(abs_ruby), abs_archdir, ENV["PATH"]].compact.join(File::PATH_SEPARATOR)
if e = ENV["RUBYLIB"]
libs |= e.split(File::PATH_SEPARATOR)
end
env["RUBYLIB"] = $:.replace(libs).join(File::PATH_SEPARATOR)
gem_path = [abs_archdir, srcdir].map {|d| File.realdirpath(".bundle", d)}
if e = ENV["GEM_PATH"]
gem_path |= e.split(File::PATH_SEPARATOR)
end
env["GEM_PATH"] = gem_path.join(File::PATH_SEPARATOR)
libruby_so = File.join(abs_archdir, config['LIBRUBY_SO'])
if File.file?(libruby_so)
if e = config['LIBPATHENV'] and !e.empty?
env[e] = [abs_archdir, ENV[e]].compact.join(File::PATH_SEPARATOR)
end
end
# Work around a bug in FreeBSD 13.2 which can cause fork(2) to hang
# See: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=271490
env['LD_BIND_NOW'] = 'yes' if /freebsd/ =~ RUBY_PLATFORM
ENV.update env
if debugger
case debugger
when :gdb, nil
debugger = %W'gdb -x #{srcdir}/.gdbinit'
if File.exist?(gdb = 'run.gdb') or
File.exist?(gdb = File.join(abs_archdir, 'run.gdb'))
debugger.push('-x', gdb)
end
debugger << '--args'
when :lldb
debugger = ['lldb', '-O', "command script import #{srcdir}/misc/lldb_cruby.py"]
if File.exist?(lldb = 'run.lldb') or
File.exist?(lldb = File.join(abs_archdir, 'run.lldb'))
debugger.push('-s', lldb)
end
debugger << '--'
end
if idx = precommand.index(:debugger)
precommand[idx, 1] = debugger
else
precommand.concat(debugger)
end
end
cmd = [runner || ruby]
if use_yjit_stat
cmd << '--yjit-stats'
end
cmd.concat(ARGV)
cmd.unshift(*precommand) unless precommand.empty?
if show
require 'shellwords'
env.each {|k,v| puts "#{k}=#{v}"}
puts Shellwords.join(cmd)
end
exec(*cmd, close_others: false)