#!/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