#!/usr/bin/env ruby
#=== Summary
#
#A command-line front-end to byebug
#
#Command invocation:
#
# byebug [options] [--] [script-options] ruby-script-to-debug
# byebug [options] [script-options]
# byebug [--version | --help]
#
#=== Options
#
#-A | --annotate level::
# Set gdb-style annotation to level, a number. Additional information
# is output automatically when program state is changed. This can be used by
# front-ends such as GNU Emacs to post this updated information without
# having to poll for it.
#
#-d | --debug::
# Set $DEBUG true.
#
#--help::
# Show invocation help and exit.
#
#-I | --include path
# Add path to $LOAD_PATH. Like the ruby -I command,
# it supports multiple load paths separated by colons.
#
#--post-mortem::
# Activate post-mortem mode.
#
#--no-quit::
# Do not quit when script terminates. Instead rerun the program.
#
#--no-stop::
# Do not stop when script is loaded.
#
#--nx::
# Don’t execute commands found in any initialization files like
# .byebugrc.
#
#-r | --requirescript::
# Require the library, before executing your script.
#
#--script=file::
# Run script file file
#
#--v::
# Print the version number, then turn on verbose mode if a script name is
# given. If no script name is given just exit after printing the version
# number.
#
#--verbose::
# Turn on verbose mode.
#
#--version::
# Show the version number and exit.
#
#-x | --trace::
# Show lines before executing them.
#
require 'optparse'
require 'ostruct'
require File.dirname(__FILE__) + "/../lib/byebug"
def debug_program(options)
# Make sure Ruby script syntax checks okay.
# Otherwise we get a load message that looks like byebug has a problem.
output = `ruby -c "#{Byebug::PROG_SCRIPT}" 2>&1`
if $?.exitstatus != 0 and RUBY_PLATFORM !~ /mswin/
puts output
exit $?.exitstatus
end
print "\032\032starting\n" if Byebug.annotate and Byebug.annotate > 2
# Record where we are we can know if the call stack has been truncated or not.
Byebug.start_sentinal = caller[0]
if bt = Byebug.debug_load(Byebug::PROG_SCRIPT, options.stop)
print bt.backtrace.map{|l| "\t#{l}"}.join("\n"), "\n"
print "Uncaught exception: #{bt}\n"
end
end
# Do a shell-like path lookup for prog_script and return the results.
# If we can't find anything return prog_script.
def whence_file(prog_script)
if prog_script.index(File::SEPARATOR)
# Don't search since this name has path separator components
return prog_script
end
for dirname in ENV['PATH'].split(File::PATH_SEPARATOR) do
prog_script_try = File.join(dirname, prog_script)
return prog_script_try if File.exist?(prog_script_try)
end
# Failure
return prog_script
end
options = OpenStruct.new(
'annotate' => Byebug.annotate,
'nx' => false,
'post_mortem' => false,
'quit' => true,
'restart_script' => nil,
'script' => nil,
'stop' => true,
'tracing' => false,
'verbose_long' => false
)
def process_options(options)
program = File.basename($0)
opts = OptionParser.new do |opts|
opts.banner = < --
EOB
opts.separator ""
opts.separator "Options:"
opts.on("-A", "--annotate LEVEL", Integer, "Set annotation level") {
|annotate| Byebug.annotate = annotate }
opts.on("-d", "--debug", "Set $DEBUG=true") {
$DEBUG = true }
opts.on('-I', '--include PATH', String,
'Add PATH (single or multiple:path:list) to $LOAD_PATH.') {
|path| $LOAD_PATH.unshift(*path.split(':')) }
opts.on('--no-quit', 'Do not quit when script finishes') {
options.quit = false }
opts.on('--no-stop', 'Do not stop when script is loaded') {
options.stop = false }
opts.on('-nx', 'Don\'t run any byebug initialization files') {
options.nx = true }
opts.on('--post-mortem', 'Run byebug in post-mortem mode') {
options.post_mortem = true }
opts.on('-r', '--require SCRIPT', String, 'Require library before script') {
|name| if name == 'debug'
puts 'byebug not compatible with Ruby\'s \'debug\' lib, option ignored'
else
require name
end }
opts.on('--restart-script FILE', String,
'Name of the script file to run. Erased after read') do
|restart_script|
options.restart_script = restart_script
unless File.exists?(options.restart_script)
puts "Script file '#{options.restart_script}' is not found"
exit
end
end
opts.on('--script FILE', String, 'Name of the script file to run') do
|script|
options.script = script
unless File.exists?(options.script)
puts "Script file '#{options.script}' is not found"
exit
end
end
opts.on('-x', '--trace', 'Turn on line tracing') {
options.tracing = true }
opts.separator ''
opts.separator 'Common options:'
opts.on_tail('--help', 'Show this message') do
puts opts
exit
end
opts.on_tail('--version', 'Print program version') do
puts "byebug #{Byebug::VERSION}"
exit
end
opts.on('--verbose', 'Turn on verbose mode') do
$VERBOSE = true
options.verbose_long = true
end
opts.on_tail('-v', 'Print version number, then turn on verbose mode') do
puts "byebug #{Byebug::VERSION}"
$VERBOSE = true
end
end
return opts
end
# What file is used for byebug startup commands.
unless defined?(OPTS_INITFILE)
OPTS_INITFILE = '.byebugoptrc'
HOME_DIR = ENV['HOME'].to_s
end
begin
initfile = File.join(HOME_DIR, OPTS_INITFILE)
eval(File.read(initfile)) if File.exist?(initfile)
rescue
end
opts = process_options(options)
begin
Byebug::ARGV = ARGV.clone if not defined? Byebug::ARGV
Byebug::BYEBUG_SCRIPT = File.expand_path(__FILE__)
Byebug::IGNORED_FILES << Byebug::BYEBUG_SCRIPT
Byebug::INITIAL_DIR = Dir.pwd
opts.parse! ARGV
rescue StandardError => e
puts opts
puts
puts e.message
exit(-1)
end
if ARGV.empty?
exit if $VERBOSE and not options.verbose_long
puts opts
puts
puts 'Must specify a script to run'
exit(-1)
end
# save script name
prog_script = ARGV.shift
prog_script = whence_file(prog_script) unless File.exist?(prog_script)
Byebug::PROG_SCRIPT = File.expand_path prog_script
# Set up trace hook for byebug
Byebug.start post_mortem: options.post_mortem
# load initrc script (e.g. .byebugrc)
Byebug.run_init_script(StringIO.new) unless options.nx
# run restore-settings startup script if specified
if options.restart_script
require 'fileutils'
Byebug.run_script(options.restart_script)
FileUtils.rm(options.restart_script)
end
# run startup script if specified
if options.script
Byebug.run_script(options.script)
end
options.stop = false if options.tracing
Byebug.tracing = options.tracing
loop do
begin
debug_program(options)
rescue SyntaxError
puts $!.backtrace.map{|l| "\t#{l}"}.join("\n")
puts "Uncaught Syntax Error\n"
rescue
print $!.backtrace.map{|l| "\t#{l}"}.join("\n"), "\n"
print "Uncaught exception: #{$!}\n"
end
print "The program finished.\n" unless Byebug.annotate.to_i > 1
break if options.quit
interface = Byebug::LocalInterface.new
# Not sure if ControlCommandProcessor is really the right
# thing to use. CommandProcessor requires a state.
processor = Byebug::ControlCommandProcessor.new(interface)
processor.process_commands
end