diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..108f3d8486 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "rdbg", + "name": "Attach with rdbg", + "request": "attach", + "rdbgPath": "${workspaceFolder}/Library/Homebrew/vendor/portable-ruby/current/lib/ruby/gems/3.3.0/gems/debug-1.9.1/exe/rdbg", + "env": { + "TMPDIR": "/private/tmp/", + } + } + ] +} diff --git a/Library/Homebrew/abstract_command.rb b/Library/Homebrew/abstract_command.rb index 8fd46295ef..03b493c834 100644 --- a/Library/Homebrew/abstract_command.rb +++ b/Library/Homebrew/abstract_command.rb @@ -2,6 +2,7 @@ # frozen_string_literal: true require "cli/parser" +require "shell_command" module Homebrew # Subclass this to implement a `brew` command. This is preferred to declaring a named function in the `Homebrew` @@ -41,6 +42,9 @@ module Homebrew sig { returns(T::Boolean) } def dev_cmd? = T.must(name).start_with?("Homebrew::DevCmd") + sig { returns(T::Boolean) } + def ruby_cmd? = !include?(Homebrew::ShellCommand) + sig { returns(CLI::Parser) } def parser = CLI::Parser.new(self, &@parser_block) diff --git a/Library/Homebrew/commands.rb b/Library/Homebrew/commands.rb index 29fd1b9469..bd15e09d2b 100644 --- a/Library/Homebrew/commands.rb +++ b/Library/Homebrew/commands.rb @@ -41,6 +41,11 @@ module Commands require?(HOMEBREW_DEV_CMD_PATH/cmd) end + def self.valid_ruby_cmd?(cmd) + (valid_internal_cmd?(cmd) || valid_internal_dev_cmd?(cmd) || external_ruby_v2_cmd_path(cmd)) && + Homebrew::AbstractCommand.command(cmd)&.ruby_cmd? + end + def self.method_name(cmd) cmd.to_s .tr("-", "_") diff --git a/Library/Homebrew/dev-cmd/debugger.rb b/Library/Homebrew/dev-cmd/debugger.rb new file mode 100644 index 0000000000..255766207e --- /dev/null +++ b/Library/Homebrew/dev-cmd/debugger.rb @@ -0,0 +1,47 @@ +# typed: strict +# frozen_string_literal: true + +module Homebrew + module DevCmd + class Debugger < AbstractCommand + cmd_args do + description <<~EOS + Run the specified Homebrew command in debug mode. + + To pass flags to the command, use `--` to separate them from the `brew` flags. + For example: `brew debugger -- list --formula`. + EOS + switch "-s", "--stop", + description: "Stop at the beginning of the script." + switch "-O", "--open", + description: "Start remote debugging over a Unix socket." + + named_args :command, min: 1 + end + + sig { override.void } + def run + raise UsageError, "Debugger is only supported with portable Ruby!" unless HOMEBREW_USING_PORTABLE_RUBY + + unless Commands.valid_ruby_cmd?(args.named.first) + raise UsageError, "`#{args.named.first}` is not a valid Ruby command!" + end + + brew_rb = (HOMEBREW_LIBRARY_PATH/"brew.rb").resolved_path + nonstop = "1" unless args.stop? + debugger_method = if args.open? + "open" + else + "start" + end + + with_env RUBY_DEBUG_NONSTOP: nonstop, RUBY_DEBUG_FORK_MODE: "parent" do + system(*HOMEBREW_RUBY_EXEC_ARGS, + "-I", $LOAD_PATH.join(File::PATH_SEPARATOR), + "-rdebug/#{debugger_method}", + brew_rb, *args.named) + end + end + end + end +end diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/dev_cmd/debugger.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/dev_cmd/debugger.rbi new file mode 100644 index 0000000000..25d8b7d75f --- /dev/null +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/dev_cmd/debugger.rbi @@ -0,0 +1,25 @@ +# typed: true + +# DO NOT EDIT MANUALLY +# This is an autogenerated file for dynamic methods in `Homebrew::DevCmd::Debugger`. +# Please instead update this file by running `bin/tapioca dsl Homebrew::DevCmd::Debugger`. + + +class Homebrew::DevCmd::Debugger + sig { returns(Homebrew::DevCmd::Debugger::Args) } + def args; end +end + +class Homebrew::DevCmd::Debugger::Args < Homebrew::CLI::Args + sig { returns(T::Boolean) } + def O?; end + + sig { returns(T::Boolean) } + def open?; end + + sig { returns(T::Boolean) } + def s?; end + + sig { returns(T::Boolean) } + def stop?; end +end