1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
# frozen_string_literal: true
require "optparse"
require "English"
require_relative "core"
require_relative "version"
require_relative "helpers/bin"
require_relative "helpers/parse"
require_relative "helpers/string"
require_relative "option_setter"
require_relative "processors/control_processor"
module Byebug
#
# Responsible for starting the debugger when started from the command line.
#
class Runner
include Helpers::BinHelper
include Helpers::ParseHelper
include Helpers::StringHelper
#
# Special working modes that don't actually start the debugger.
#
attr_reader :help, :version, :remote
#
# Signals that we should exit after the debugged program is finished.
#
attr_accessor :quit
#
# Signals that we should stop before program starts
#
attr_accessor :stop
#
# Signals that we should run rc scripts before program starts
#
attr_writer :init_script
#
# @param stop [Boolean] Whether the runner should stop right before
# starting the program.
#
# @param quit [Boolean] Whether the runner should quit right after
# finishing the program.
#
def initialize(stop = true, quit = true)
@stop = stop
@quit = quit
end
def help=(text)
@help ||= text
interface.puts("#{text}\n")
end
def version=(number)
@version ||= number
interface.puts prettify <<-VERSION
Running byebug #{number}
VERSION
end
def remote=(host_and_port)
@remote ||= Byebug.parse_host_and_port(host_and_port)
Byebug.start_client(*@remote)
end
def init_script
defined?(@init_script) ? @init_script : true
end
#
# Usage banner.
#
def banner
prettify <<-BANNER
byebug #{Byebug::VERSION}
Usage: byebug [options] <script.rb> -- <script.rb parameters>
BANNER
end
#
# Starts byebug to debug a program.
#
def run
Byebug.mode = :standalone
option_parser.order!($ARGV)
return if non_script_option? || error_in_script?
$PROGRAM_NAME = program
Byebug.run_init_script if init_script
loop do
debug_program
break if quit
ControlProcessor.new(nil, interface).process_commands
end
end
def interface
@interface ||= Context.interface
end
#
# Processes options passed from the command line.
#
def option_parser
@option_parser ||= OptionParser.new(banner, 25) do |opts|
opts.banner = banner
OptionSetter.new(self, opts).setup
end
end
def program
@program ||= begin
candidate = which($ARGV.shift)
if [which("ruby"), RbConfig.ruby].include?(candidate)
which($ARGV.shift)
else
candidate
end
end
end
#
# An option that doesn't need a script specified was given
#
def non_script_option?
version || help || remote
end
#
# There is an error with the specified script
#
def error_in_script?
no_script? || non_existing_script? || invalid_script?
end
#
# No script to debug specified
#
def no_script?
return false unless $ARGV.empty?
print_error("You must specify a program to debug")
true
end
#
# Extracts debugged program from command line args.
#
def non_existing_script?
return false if program
print_error("The script doesn't exist")
true
end
#
# Checks the debugged script has correct syntax
#
def invalid_script?
return false if syntax_valid?(File.read(program))
print_error("The script has incorrect syntax")
true
end
#
# Debugs a script only if syntax checks okay.
#
def debug_program
error = Byebug.debug_load(program, stop)
puts "#{error}\n#{error.backtrace}" if error
end
#
# Prints an error message and a help string
#
def print_error(msg)
interface.errmsg(msg)
interface.puts(option_parser.help)
end
end
end
|