Skip to content

Commit

Permalink
Rearrange requires
Browse files Browse the repository at this point in the history
This improves the load time of most brew commands. For an example of
one of the simplest commands this speeds up:

Without Bootsnap:
```
$ hyperfine 'git checkout master; brew help' 'git checkout optimise_requires; brew help'
Benchmark 1: git checkout master; brew help
  Time (mean ± σ):     525.0 ms ±  35.8 ms    [User: 229.9 ms, System: 113.1 ms]
  Range (min … max):   465.3 ms … 576.6 ms    10 runs

Benchmark 2: git checkout optimise_requires; brew help
  Time (mean ± σ):     383.3 ms ±  25.1 ms    [User: 133.0 ms, System: 72.1 ms]
  Range (min … max):   353.0 ms … 443.6 ms    10 runs

Summary
  git checkout optimise_requires; brew help ran
    1.37 ± 0.13 times faster than git checkout master; brew help
```

With Bootsnap:
```
$ hyperfine 'git checkout master; brew help' 'git checkout optimise_requires; brew help'
Benchmark 1: git checkout master; brew help
  Time (mean ± σ):     386.0 ms ±  30.9 ms    [User: 130.2 ms, System: 93.8 ms]
  Range (min … max):   359.5 ms … 469.3 ms    10 runs

Benchmark 2: git checkout optimise_requires; brew help
  Time (mean ± σ):     330.2 ms ±  32.4 ms    [User: 93.4 ms, System: 73.0 ms]
  Range (min … max):   302.9 ms … 413.9 ms    10 runs

Summary
  git checkout optimise_requires; brew help ran
    1.17 ± 0.15 times faster than git checkout master; brew help
```
  • Loading branch information
MikeMcQuaid committed Jul 14, 2024
1 parent 0f9ca1d commit 9fa56ac
Show file tree
Hide file tree
Showing 34 changed files with 114 additions and 82 deletions.
8 changes: 7 additions & 1 deletion Library/Homebrew/abstract_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ class << self
attr_reader :args_class

sig { returns(String) }
def command_name = Utils.underscore(T.must(name).split("::").fetch(-1)).tr("_", "-").delete_suffix("-cmd")
def command_name
require "utils"

Utils.underscore(T.must(name).split("::").fetch(-1))
.tr("_", "-")
.delete_suffix("-cmd")
end

# @return the AbstractCommand subclass associated with the brew CLI command name.
sig { params(name: String).returns(T.nilable(T.class_of(AbstractCommand))) }
Expand Down
1 change: 0 additions & 1 deletion Library/Homebrew/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
Warnings.ignore :default_gems do
require "base64" # TODO: Add this to the Gemfile or remove it before moving to Ruby 3.4.
end
require "extend/cachable"

module Homebrew
# Helper functions for using Homebrew's formulae.brew.sh API.
Expand Down
12 changes: 10 additions & 2 deletions Library/Homebrew/brew.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
help_flag = true
help_cmd_index = i
elsif !cmd && help_flag_list.exclude?(arg)
require "commands"
cmd = ARGV.delete_at(i)
cmd = Commands::HOMEBREW_INTERNAL_COMMAND_ALIASES.fetch(cmd, cmd)
end
Expand All @@ -59,13 +60,13 @@

ENV["PATH"] = path.to_s

require "abstract_command"
require "commands"
require "settings"

internal_cmd = Commands.valid_internal_cmd?(cmd) || Commands.valid_internal_dev_cmd?(cmd) if cmd

unless internal_cmd
require "tap"

# Add contributed commands to PATH before checking.
homebrew_path.append(Tap.cmd_directories)

Expand All @@ -88,6 +89,8 @@
cmd_class = Homebrew::AbstractCommand.command(cmd)
if cmd_class
command_instance = cmd_class.new

require "utils/analytics"
Utils::Analytics.report_command_run(command_instance)
command_instance.run
else
Expand All @@ -102,6 +105,8 @@
end
exec "brew-#{cmd}", *ARGV
else
require "tap"

possible_tap = OFFICIAL_CMD_TAPS.find { |_, cmds| cmds.include?(cmd) }
possible_tap = Tap.fetch(possible_tap.first) if possible_tap

Expand Down Expand Up @@ -142,6 +147,7 @@
Homebrew::Help.help cmd, remaining_args: args&.remaining, usage_error: e.message
rescue SystemExit => e
onoe "Kernel.exit" if args&.debug? && !e.success?
require "utils/backtrace"
$stderr.puts Utils::Backtrace.clean(e) if args&.debug? || ARGV.include?("--debug")
raise
rescue Interrupt
Expand Down Expand Up @@ -179,13 +185,15 @@
raise if e.message.empty?

onoe e
require "utils/backtrace"
$stderr.puts Utils::Backtrace.clean(e) if args&.debug? || ARGV.include?("--debug")

exit 1
rescue Exception => e # rubocop:disable Lint/RescueException
onoe e

method_deprecated_error = e.is_a?(MethodDeprecatedError)
require "utils/backtrace"
$stderr.puts Utils::Backtrace.clean(e) if args&.debug? || ARGV.include?("--debug") || !method_deprecated_error

if OS.unsupported_configuration?
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
require "fcntl"
require "socket"
require "cmd/install"
require "json/add/exception"

# A formula build.
class Build
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/audit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require "livecheck/livecheck"
require "source_location"
require "system_command"
require "utils/backtrace"
require "utils/curl"
require "utils/git"
require "utils/shared_audits"
Expand Down
7 changes: 0 additions & 7 deletions Library/Homebrew/cli/named_args.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# frozen_string_literal: true

require "delegate"
require "api"
require "cli/args"

module Homebrew
Expand All @@ -29,12 +28,6 @@ def initialize(
cask_options: false,
without_api: false
)
require "cask/cask"
require "cask/cask_loader"
require "formulary"
require "keg"
require "missing_formula"

@args = args
@override_spec = override_spec
@force_bottle = force_bottle
Expand Down
2 changes: 2 additions & 0 deletions Library/Homebrew/cli/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
require "env_config"
require "cask/config"
require "cli/args"
require "commands"
require "optparse"
require "utils/tty"
require "utils/formatter"

module Homebrew
module CLI
Expand Down
2 changes: 2 additions & 0 deletions Library/Homebrew/cmd/tap-info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class TapInfo < AbstractCommand

sig { override.void }
def run
require "tap"

taps = if args.installed?
Tap
else
Expand Down
16 changes: 14 additions & 2 deletions Library/Homebrew/commands.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# typed: true
# frozen_string_literal: true

require "completions"

# Helper functions for commands.
module Commands
HOMEBREW_CMD_PATH = (HOMEBREW_LIBRARY_PATH/"cmd").freeze
Expand Down Expand Up @@ -72,16 +70,22 @@ def self.internal_dev_cmd_path(cmd)

# Ruby commands which can be `require`d without being run.
def self.external_ruby_v2_cmd_path(cmd)
require "tap"

path = which("#{cmd}.rb", Tap.cmd_directories)
path if require?(path)
end

# Ruby commands which are run by being `require`d.
def self.external_ruby_cmd_path(cmd)
require "tap"

which("brew-#{cmd}.rb", PATH.new(ENV.fetch("PATH")).append(Tap.cmd_directories))
end

def self.external_cmd_path(cmd)
require "tap"

which("brew-#{cmd}", PATH.new(ENV.fetch("PATH")).append(Tap.cmd_directories))
end

Expand Down Expand Up @@ -112,6 +116,8 @@ def self.internal_developer_commands_paths
end

def self.official_external_commands_paths(quiet:)
require "tap"

OFFICIAL_CMD_TAPS.flat_map do |tap_name, cmds|
tap = Tap.fetch(tap_name)
tap.install(quiet:) unless tap.installed?
Expand All @@ -137,6 +143,8 @@ def self.find_internal_commands(path)
end

def self.external_commands
require "tap"

Tap.cmd_directories.flat_map do |path|
find_commands(path).select(&:executable?)
.map { basename_without_extension(_1) }
Expand All @@ -156,6 +164,8 @@ def self.find_commands(path)
end

def self.rebuild_internal_commands_completion_list
require "completions"

cmds = internal_commands + internal_developer_commands + internal_commands_aliases
cmds.reject! { |cmd| Homebrew::Completions::COMPLETIONS_EXCLUSION_LIST.include? cmd }

Expand All @@ -164,6 +174,8 @@ def self.rebuild_internal_commands_completion_list
end

def self.rebuild_commands_completion_list
require "completions"

# Ensure that the cache exists so we can build the commands list
HOMEBREW_CACHE.mkpath

Expand Down
3 changes: 3 additions & 0 deletions Library/Homebrew/dev-cmd/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def run

require "formula_assertions"
require "formula_free_port"
require "utils/fork"

args.named.to_resolved_formulae.each do |f|
# Cannot test uninstalled formulae
Expand Down Expand Up @@ -100,6 +101,8 @@ def run
end
rescue Exception => e # rubocop:disable Lint/RescueException
retry if retry_test?(f)

require "utils/backtrace"
ofail "#{f.full_name}: failed"
$stderr.puts e, Utils::Backtrace.clean(e)
ensure
Expand Down
5 changes: 2 additions & 3 deletions Library/Homebrew/exceptions.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# typed: true
# frozen_string_literal: true

require "shellwords"
require "utils"

# Raised when a command is used wrong.
#
# @api internal
Expand Down Expand Up @@ -554,6 +551,8 @@ def dump(verbose: false)
# installed in a situation where a bottle is required.
class UnbottledError < RuntimeError
def initialize(formulae)
require "utils"

msg = +<<~EOS
The following #{Utils.pluralize("formula", formulae.count, plural: "e")} cannot be installed from #{Utils.pluralize("bottle", formulae.count)} and must be
built from source.
Expand Down
6 changes: 6 additions & 0 deletions Library/Homebrew/extend/kernel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Contains shorthand Homebrew utility methods like `ohai`, `opoo`, `odisabled`.
# TODO: move these out of `Kernel`.

module Kernel
def require?(path)
return false if path.nil?
Expand Down Expand Up @@ -74,6 +75,9 @@ def opoo(message)
# @api public
sig { params(message: T.any(String, Exception)).void }
def onoe(message)
require "utils/formatter"
require "utils/github/actions"

Tty.with($stderr) do |stderr|
stderr.puts Formatter.error(message, label: "Error")
GitHub::Actions.puts_annotation_if_env_set(:error, message.to_s)
Expand Down Expand Up @@ -150,6 +154,8 @@ def odeprecated(method, replacement = nil,
backtrace.each do |line|
next unless (match = line.match(HOMEBREW_TAP_PATH_REGEX))

require "tap"

tap = Tap.fetch(match[:user], match[:repo])
tap_message = +"\nPlease report this issue to the #{tap.full_name} tap"
tap_message += " (not Homebrew/brew or Homebrew/homebrew-core)" unless tap.official?
Expand Down
15 changes: 8 additions & 7 deletions Library/Homebrew/extend/pathname.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
# typed: true
# frozen_string_literal: true

require "context"
require "resource"
require "metafiles"
require "extend/file/atomic"
require "system_command"

module DiskUsageExtension
sig { returns(Integer) }
def disk_usage
Expand Down Expand Up @@ -75,6 +69,8 @@ def compute_disk_usage
end
end

require "system_command"

# Homebrew extends Ruby's `Pathname` to make our code more readable.
# @see https://ruby-doc.org/stdlib-2.6.3/libdoc/pathname/rdoc/Pathname.html Ruby's Pathname API
class Pathname
Expand Down Expand Up @@ -186,6 +182,8 @@ def append_lines(content, **open_args)
# @api public
sig { params(content: String).void }
def atomic_write(content)
require "extend/file/atomic"

old_stat = stat if exist?
File.atomic_write(self) do |file|
file.write(content)
Expand Down Expand Up @@ -433,6 +431,8 @@ def write_jar_script(target_jar, script_name, java_opts = "", java_version: nil)
end

def install_metafiles(from = Pathname.pwd)
require "metafiles"

Pathname(from).children.each do |p|
next if p.directory?
next if File.empty?(p)
Expand Down Expand Up @@ -514,9 +514,10 @@ def rmtree(noop: nil, verbose: nil, secure: nil)
nil
end
end

require "extend/os/pathname"

require "context"

module ObserverPathnameExtension
class << self
include Context
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require "lock_file"
require "formula_pin"
require "hardware"
require "utils"
require "utils/bottles"
require "utils/shebang"
require "utils/shell"
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/formula_installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
require "service"
require "attestation"
require "sbom"
require "utils/fork"

# Installer for a formula.
class FormulaInstaller
Expand Down
2 changes: 2 additions & 0 deletions Library/Homebrew/formulary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
require "digest/sha2"
require "extend/cachable"
require "tab"
require "utils"
require "utils/bottles"
require "service"
require "utils/curl"
require "deprecate_disable"
require "extend/hash/deep_transform_values"
require "extend/hash/keys"
require "tap"

# The {Formulary} is responsible for creating instances of {Formula}.
# It is not meant to be used directly from formulae.
Expand Down
Loading

0 comments on commit 9fa56ac

Please sign in to comment.