aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Nagel2014-09-18 14:16:07 -0500
committerJack Nagel2014-09-18 14:16:07 -0500
commit3bbc9998a52b86b206465c887e75ee419418c26d (patch)
tree622674f6d85c874793483740c52397b1de30a6f7
parent67a916427880df69ae34215c9d03cda4849f1ae6 (diff)
downloadbrew-3bbc9998a52b86b206465c887e75ee419418c26d.tar.bz2
Rewrite debugger to remove monkeypatches and use of call/cc
-rw-r--r--Library/Homebrew/build.rb20
-rw-r--r--Library/Homebrew/debrew.rb193
-rw-r--r--Library/Homebrew/debrew/exception.rb7
-rw-r--r--Library/Homebrew/debrew/menu.rb39
-rw-r--r--Library/Homebrew/debrew/raise_plus.rb46
-rw-r--r--Library/Homebrew/formula_installer.rb1
-rw-r--r--Library/Homebrew/test/test_raise_plus.rb45
7 files changed, 141 insertions, 210 deletions
diff --git a/Library/Homebrew/build.rb b/Library/Homebrew/build.rb
index 34903c9aa..14209dd81 100644
--- a/Library/Homebrew/build.rb
+++ b/Library/Homebrew/build.rb
@@ -8,7 +8,7 @@ require "build_options"
require "cxxstdlib"
require "keg"
require "extend/ENV"
-require "debrew" if ARGV.debug?
+require "debrew"
require "fcntl"
class Build
@@ -109,6 +109,11 @@ class Build
end
end
+ if ARGV.debug?
+ formula.extend(Debrew::Formula)
+ formula.resources.each { |r| r.extend(Debrew::Resource) }
+ end
+
formula.brew do
if ARGV.flag? '--git'
system "git", "init"
@@ -129,17 +134,7 @@ class Build
else
formula.prefix.mkpath
- formula.resources.each { |r| r.extend(ResourceDebugger) } if ARGV.debug?
-
- begin
- formula.install
- rescue Exception => e
- if ARGV.debug?
- debrew(e, formula)
- else
- raise
- end
- end
+ formula.install
stdlibs = detect_stdlibs
Tab.create(formula, ENV.compiler, stdlibs.first, formula.build).write
@@ -190,7 +185,6 @@ begin
build = Build.new(formula, options)
build.install
rescue Exception => e
- e.continuation = nil if ARGV.debug?
Marshal.dump(e, error_pipe)
error_pipe.close
exit! 1
diff --git a/Library/Homebrew/debrew.rb b/Library/Homebrew/debrew.rb
index e8df1aeec..fbddadbd5 100644
--- a/Library/Homebrew/debrew.rb
+++ b/Library/Homebrew/debrew.rb
@@ -1,75 +1,148 @@
-require 'debrew/menu'
-require 'debrew/raise_plus'
-require 'set'
-
-unless ENV['HOMEBREW_NO_READLINE']
- begin
- require 'rubygems'
- require 'ruby-debug'
- rescue LoadError
+require "mutex_m"
+require "debrew/irb" unless ENV["HOMEBREW_NO_READLINE"]
+
+module Debrew
+ extend Mutex_m
+
+ Ignorable = Module.new
+
+ module Raise
+ def raise(*)
+ super
+ rescue Exception => e
+ e.extend(Ignorable)
+ super(e) unless Debrew.debug(e) == :ignore
+ end
+
+ alias_method :fail, :raise
end
- require 'debrew/irb'
-end
+ module Formula
+ def install
+ Debrew.debrew { super }
+ end
+ end
-class Object
- include RaisePlus
-end
+ module Resource
+ def unpack(target=nil)
+ return super if target
+ super do
+ begin
+ yield self
+ rescue Exception => e
+ Debrew.debug(e)
+ end
+ end
+ end
+ end
+
+ class Menu
+ Entry = Struct.new(:name, :action)
+
+ attr_accessor :prompt, :entries
+
+ def initialize
+ @entries = []
+ end
+
+ def choice(name, &action)
+ entries << Entry.new(name.to_s, action)
+ end
+
+ def self.choose
+ menu = new
+ yield menu
+
+ choice = nil
+ while choice.nil?
+ menu.entries.each_with_index { |e, i| puts "#{i+1}. #{e.name}" }
+ print menu.prompt unless menu.prompt.nil?
-module ResourceDebugger
- def stage(target=nil, &block)
- return super if target
+ input = $stdin.gets.chomp
- super do
- begin
- block.call(self)
- rescue Exception => e
- if ARGV.debug?
- debrew e
+ i = input.to_i
+ if i > 0
+ choice = menu.entries[i-1]
else
- raise
+ possible = menu.entries.find_all { |e| e.name.start_with?(input) }
+
+ case possible.size
+ when 0 then puts "No such option"
+ when 1 then choice = possible.first
+ else puts "Multiple options match: #{possible.map(&:name).join(" ")}"
+ end
end
end
+
+ choice[:action].call
end
end
-end
-$debugged_exceptions = Set.new
-
-def debrew(exception, formula=nil)
- raise exception unless $debugged_exceptions.add?(exception)
-
- puts "#{exception.backtrace.first}"
- puts "#{Tty.red}#{exception.class.name}#{Tty.reset}: #{exception}"
-
- begin
- again = false
- choose do |menu|
- menu.prompt = "Choose an action: "
- menu.choice(:raise) { original_raise exception }
- menu.choice(:ignore) { exception.restart } if exception.continuation
- menu.choice(:backtrace) { puts exception.backtrace; again = true }
- menu.choice(:debug) do
- puts "When you exit the debugger, execution will continue."
- exception.restart { debugger }
- end if Object.const_defined?(:Debugger)
- menu.choice(:irb) do
- puts "When you exit this IRB session, execution will continue."
- exception.restart do
- # we need to capture the binding after returning from raise
- set_trace_func proc { |event, file, line, id, binding, classname|
- if event == 'return'
- set_trace_func nil
- IRB.start_within(binding)
- end
- }
+ class << self
+ alias_method :original_raise, :raise
+ end
+
+ @active = false
+ @debugged_exceptions = Set.new
+
+ def self.active?
+ @active
+ end
+
+ def self.debugged_exceptions
+ @debugged_exceptions
+ end
+
+ def self.debrew
+ @active = true
+ Object.send(:include, Raise)
+
+ begin
+ yield
+ rescue Exception => e
+ debug(e)
+ ensure
+ @active = false
+ end
+ end
+
+ def self.debug(e)
+ original_raise(e) unless active? &&
+ debugged_exceptions.add?(e) &&
+ try_lock
+
+ begin
+ puts "#{e.backtrace.first}"
+ puts "#{Tty.red}#{e.class.name}#{Tty.reset}: #{e}"
+
+ loop do
+ Menu.choose do |menu|
+ menu.prompt = "Choose an action: "
+
+ menu.choice(:raise) { original_raise(e) }
+ menu.choice(:ignore) { return :ignore } if Ignorable === e
+ menu.choice(:backtrace) { puts e.backtrace }
+
+ menu.choice(:irb) do
+ puts "When you exit this IRB session, execution will continue."
+ set_trace_func proc { |event, _, _, id, binding, klass|
+ if klass == Raise && id == :raise && event == "return"
+ set_trace_func(nil)
+ synchronize { IRB.start_within(binding) }
+ end
+ }
+
+ return :ignore
+ end if Object.const_defined?(:IRB) && Ignorable === e
+
+ menu.choice(:shell) do
+ puts "When you exit this shell, you will return to the menu."
+ interactive_shell
+ end
end
- end if Object.const_defined?(:IRB) && exception.continuation
- menu.choice(:shell) do
- puts "When you exit this shell, you will return to the menu."
- interactive_shell formula
- again=true
end
+ ensure
+ unlock
end
- end while again
+ end
end
diff --git a/Library/Homebrew/debrew/exception.rb b/Library/Homebrew/debrew/exception.rb
deleted file mode 100644
index da8e56dae..000000000
--- a/Library/Homebrew/debrew/exception.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Exception
- attr_accessor :continuation
-
- def restart(&block)
- continuation.call block
- end
-end
diff --git a/Library/Homebrew/debrew/menu.rb b/Library/Homebrew/debrew/menu.rb
deleted file mode 100644
index c59c500b8..000000000
--- a/Library/Homebrew/debrew/menu.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-class Menu
- attr_accessor :prompt
- attr_accessor :entries
-
- def initialize
- @entries = []
- end
-
- def choice(name, &action)
- entries << { :name => name, :action => action }
- end
-end
-
-def choose
- menu = Menu.new
- yield menu
-
- choice = nil
- while choice.nil?
- menu.entries.each_with_index do |entry, i|
- puts "#{i+1}. #{entry[:name]}"
- end
- print menu.prompt unless menu.prompt.nil?
- reply = $stdin.gets.chomp
-
- i = reply.to_i
- if i > 0
- choice = menu.entries[i-1]
- else
- possible = menu.entries.find_all {|e| e[:name].to_s.start_with? reply }
- case possible.size
- when 0 then puts "No such option"
- when 1 then choice = possible.first
- else puts "Multiple options match: #{possible.map{|e| e[:name]}.join(' ')}"
- end
- end
- end
- choice[:action].call
-end
diff --git a/Library/Homebrew/debrew/raise_plus.rb b/Library/Homebrew/debrew/raise_plus.rb
deleted file mode 100644
index 29bf4106a..000000000
--- a/Library/Homebrew/debrew/raise_plus.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'continuation' if RUBY_VERSION.to_f >= 1.9
-
-class Exception
- attr_accessor :continuation
-
- def restart(&block)
- continuation.call block
- end
-end
-
-module RaisePlus
- alias :original_raise :raise
-
- private
-
- def raise(*args)
- exception = case
- when args.size == 0
- $!.nil? ? RuntimeError.exception : $!
- when args.size == 1 && args[0].is_a?(String)
- RuntimeError.exception(args[0])
- when args.size == 2 && args[0].is_a?(Exception)
- args[0].exception(args[1])
- when args[0].is_a?(Class) && args[0].ancestors.include?(Exception)
- args[0].exception(args[1])
- else
- args[0]
- end
-
- # passing something other than a String or Exception is illegal, but if someone does it anyway,
- # that object won't have backtrace or continuation methods. in that case, let's pass it on to
- # the original raise, which will reject it
- return super exception unless exception.is_a?(Exception)
-
- # keep original backtrace if reraising
- exception.set_backtrace(args.size >= 3 ? args[2] : caller) if exception.backtrace.nil?
-
- blk = callcc do |cc|
- exception.continuation = cc
- super exception
- end
- blk.call unless blk.nil?
- end
-
- alias :fail :raise
-end
diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb
index bb76a69cd..d457da430 100644
--- a/Library/Homebrew/formula_installer.rb
+++ b/Library/Homebrew/formula_installer.rb
@@ -12,6 +12,7 @@ require 'formula_cellar_checks'
require 'install_renamed'
require 'cmd/tap'
require 'hooks/bottles'
+require 'debrew'
class FormulaInstaller
include FormulaCellarChecks
diff --git a/Library/Homebrew/test/test_raise_plus.rb b/Library/Homebrew/test/test_raise_plus.rb
deleted file mode 100644
index 109c6e737..000000000
--- a/Library/Homebrew/test/test_raise_plus.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-require 'testing_env'
-require 'debrew/raise_plus'
-
-class RaisePlusTests < Homebrew::TestCase
- include RaisePlus
-
- def test_raises_runtime_error_when_no_args
- assert_raises(RuntimeError) { raise }
- end
-
- def test_raises_runtime_error_with_string_arg
- raise "foo"
- rescue Exception => e
- assert_kind_of RuntimeError, e
- assert_equal "foo", e.to_s
- end
-
- def test_raises_given_exception_with_new_to_s
- a = Exception.new("foo")
- raise a, "bar"
- rescue Exception => e
- assert_equal "bar", e.to_s
- end
-
- def test_raises_same_instance
- a = Exception.new("foo")
- raise a
- rescue Exception => e
- assert_same e, a
- end
-
- def test_raises_exception_class
- assert_raises(StandardError) { raise StandardError }
- end
-
- def test_raises_type_error_for_bad_args
- assert_raises(TypeError) { raise 1 }
- end
-
- def test_raise_is_private
- assert_raises(NoMethodError) do
- Object.new.extend(RaisePlus).raise(RuntimeError)
- end
- end
-end