Much better CTRL-C handling

Let's not show weird error messages when user interrupts during various stages of brew initialization.

Tested by doing `for x in $(brew search); do brew install $x; done` and pressing CTRL-C at random short intervals.
This commit is contained in:
Max Howell 2012-08-22 15:50:27 -04:00
parent fb8c7e0aaf
commit 20ce16a3ff
4 changed files with 53 additions and 42 deletions

View File

@ -1,51 +1,54 @@
#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -W0
# This script is called by formula_installer as a separate instance.
# Rationale: Formula can use __END__, Formula can change ENV
# Thrown exceptions are propogated back to the parent process over a pipe
require 'global'
STD_TRAP = trap("INT") { exit! 130 } # no backtrace thanks
at_exit do
# the whole of everything must be run in at_exit because the formula has to
# be the run script as __END__ must work for *that* formula.
main
end
error_pipe = nil
require 'global'
begin
# The main Homebrew process expects to eventually see EOF on the error
# pipe in FormulaInstaller#build. However, if any child process fails to
# terminate (i.e, fails to close the descriptor), this won't happen, and
# the installer will hang. Set close-on-exec to prevent this.
# Whether it is *wise* to launch daemons from formulae is a separate
# question altogether.
if ENV['HOMEBREW_ERROR_PIPE']
require 'fcntl'
error_pipe = IO.new(ENV['HOMEBREW_ERROR_PIPE'].to_i, 'w')
error_pipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
end
def main
# The main Homebrew process expects to eventually see EOF on the error
# pipe in FormulaInstaller#build. However, if any child process fails to
# terminate (i.e, fails to close the descriptor), this won't happen, and
# the installer will hang. Set close-on-exec to prevent this.
# Whether it is *wise* to launch daemons from formulae is a separate
# question altogether.
if ENV['HOMEBREW_ERROR_PIPE']
require 'fcntl'
error_pipe = IO.new(ENV['HOMEBREW_ERROR_PIPE'].to_i, 'w')
error_pipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
end
raise $! if $! # an exception was already thrown when parsing the formula
raise $! if $! # an exception was already thrown when parsing the formula
require 'hardware'
require 'keg'
trap("INT", STD_TRAP) # restore default CTRL-C handler
# Force any future invocations of sudo to require the user's password to be
# re-entered. This is in-case any build script call sudo. Certainly this is
# can be inconvenient for the user. But we need to be safe.
system "/usr/bin/sudo -k"
require 'hardware'
require 'keg'
install(Formula.factory($0))
rescue Exception => e
unless error_pipe.nil?
Marshal.dump(e, error_pipe)
error_pipe.close
exit! 1
else
onoe e
puts e.backtrace
exit! 2
end
# Force any future invocations of sudo to require the user's password to be
# re-entered. This is in-case any build script call sudo. Certainly this is
# can be inconvenient for the user. But we need to be safe.
system "/usr/bin/sudo -k"
install(Formula.factory($0))
rescue Exception => e
unless error_pipe.nil?
Marshal.dump(e, error_pipe)
error_pipe.close
exit! 1
else
onoe e
puts e.backtrace
exit! 2
end
end

View File

@ -238,6 +238,7 @@ class FormulaInstaller
Process.wait
data = read.read
raise Marshal.load(data) unless data.nil? or data.empty?
raise Interrupt if $?.exitstatus == 130
raise "Suspicious installation failure" unless $?.success?
end
@ -266,15 +267,18 @@ class FormulaInstaller
end
keg = Keg.new(f.prefix)
keg.link
rescue Exception => e
onoe "The `brew link` step did not complete successfully."
puts "The formula built, but is not symlinked into #{HOMEBREW_PREFIX}."
puts "You can try again using `brew link #{f.name}`."
keg.unlink
ohai e, e.backtrace if ARGV.debug?
@show_summary_heading = true
begin
keg.link
rescue Exception => e
onoe "The `brew link` step did not complete successfully"
puts "The formula built, but is not symlinked into #{HOMEBREW_PREFIX}"
puts "You can try again using `brew link #{f.name}'"
ohai e, e.backtrace if ARGV.debug?
@show_summary_heading = true
ignore_interrupts{ keg.unlink }
raise unless e.kind_of? RuntimeError
end
end
def fix_install_names

View File

@ -217,7 +217,7 @@ def inreplace path, before=nil, after=nil
end
def ignore_interrupts
std_trap = trap("INT") {}
std_trap = trap("INT") { puts "One sec, just cleaning up" }
yield
ensure
trap("INT", std_trap)

View File

@ -1,6 +1,8 @@
#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -W0
# -*- coding: utf-8 -*-
std_trap = trap("INT") { exit! 130 } # no backtrace thanks
HOMEBREW_BREW_FILE = ENV['HOMEBREW_BREW_FILE'] = File.expand_path(__FILE__)
require 'pathname'
@ -49,6 +51,8 @@ rescue LoadError => e
end
begin
trap("INT", std_trap) # restore default CTRL-C handler
aliases = {'ls' => :list,
'homepage' => :home,
'-S' => :search,