#!/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 # #-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. output = `ruby -c "#{Byebug::PROG_SCRIPT}" 2>&1` if $?.exitstatus != 0 puts output exit $?.exitstatus end if bt = Byebug.debug_load(Byebug::PROG_SCRIPT, options.stop) p bt, bt.backtrace 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( 'nx' => false, 'post_mortem' => false, 'quit' => true, 'restart_script' => nil, 'script' => nil, 'server' => nil, 'stop' => true, 'tracing' => false, ) def process_options(options) program = File.basename($0) opts = OptionParser.new do |opts| opts.banner = < -- EOB opts.separator "" opts.separator "Options:" 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('-R', '--remote HOST[:PORT]', String, 'Specify byebug server spec') { |server| host, port = server.split(':', 2) host = 'localhost' if host.empty? port = nil if port && port.empty? port &&= port.to_i options.server = [host, port].compact } 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.exist?(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.exist?(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_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 options.server puts "Connecting to byebug server #{options.server.join(':')}" Byebug.start_client(*options.server) exit end if ARGV.empty? 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 debug_program(options) 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