diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ea56bcfac2..94e1f0964f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,7 +12,6 @@ env: HOMEBREW_DEVELOPER: 1 HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_ENV_HINTS: 1 - HOMEBREW_BOOTSNAP: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 defaults: diff --git a/.github/workflows/rubydoc.yml b/.github/workflows/rubydoc.yml index 0d86cdf88a..93d7b8a6f5 100644 --- a/.github/workflows/rubydoc.yml +++ b/.github/workflows/rubydoc.yml @@ -13,7 +13,6 @@ env: HOMEBREW_DEVELOPER: 1 HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_ENV_HINTS: 1 - HOMEBREW_BOOTSNAP: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 defaults: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c57ffd0bc7..13a6c2a377 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,6 @@ env: HOMEBREW_DEVELOPER: 1 HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_ENV_HINTS: 1 - HOMEBREW_BOOTSNAP: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 HOMEBREW_VERIFY_ATTESTATIONS: 1 diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index 2b6cd5981c..0da43b7ff7 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -72,11 +72,6 @@ module Homebrew description: "Use this as the `bat` theme for syntax highlighting.", default_text: "`$BAT_THEME`.", }, - HOMEBREW_BOOTSNAP: { - description: "If set, use Bootsnap to speed up repeated `brew` calls. " \ - "A no-op on Linux when not using Homebrew's vendored, relocatable Ruby.", - boolean: true, - }, HOMEBREW_BOTTLE_DOMAIN: { description: "Use this URL as the download mirror for bottles. " \ "If bottles at that URL are temporarily unavailable, " \ diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi index a257500a8f..e540cb4ff0 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi @@ -43,9 +43,6 @@ module Homebrew::EnvConfig sig { returns(T.nilable(::String)) } def bat_theme; end - sig { returns(T::Boolean) } - def bootsnap?; end - sig { returns(String) } def bottle_domain; end diff --git a/Library/Homebrew/standalone/init.rb b/Library/Homebrew/standalone/init.rb index 3cdaad7436..db380a571b 100644 --- a/Library/Homebrew/standalone/init.rb +++ b/Library/Homebrew/standalone/init.rb @@ -1,7 +1,10 @@ # typed: true # frozen_string_literal: true -# This file is included before any other files. It intentionally has typing disabled and has minimal use of `require`. +# This file is included before any other files. +# It intentionally has typing disabled and uses `Homebrew::FastBootRequire` +# or `require_relative` to load all files +# (except "rbconfig" which is needed by `Homebrew::FastBootRequire`) required_ruby_major, required_ruby_minor, = ENV.fetch("HOMEBREW_REQUIRED_RUBY_VERSION", "").split(".").map(&:to_i) gems_vendored = if required_ruby_minor.nil? @@ -21,12 +24,30 @@ else vendored_versions.include?("#{ruby_major}.#{ruby_minor}") end.freeze +# Setup Homebrew::FastBootRequire for faster boot requires. +# Inspired by https://github.com/Shopify/bootsnap/wiki/Bootlib::Require +require "rbconfig" + +module Homebrew + module FastBootRequire + ARCHDIR = RbConfig::CONFIG["archdir"].freeze + RUBYLIBDIR = RbConfig::CONFIG["rubylibdir"].freeze + + def self.from_archdir(feature) + require(File.join(ARCHDIR, feature.to_s)) + end + + def self.from_rubylibdir(feature) + require(File.join(RUBYLIBDIR, "#{feature}.rb")) + end + end +end + # We trust base Ruby to provide what we need. # Don't look into the user-installed sitedir, which may contain older versions of RubyGems. -require "rbconfig" $LOAD_PATH.reject! { |path| path.start_with?(RbConfig::CONFIG["sitedir"]) } -require "pathname" +Homebrew::FastBootRequire.from_rubylibdir("pathname") dir = __dir__ || raise("__dir__ is not defined") HOMEBREW_LIBRARY_PATH = Pathname(dir).parent.realpath.freeze HOMEBREW_USING_PORTABLE_RUBY = RbConfig.ruby.include?("/vendor/portable-ruby/").freeze @@ -48,7 +69,7 @@ unless $LOAD_PATH.include?(HOMEBREW_LIBRARY_PATH.to_s) $LOAD_PATH.insert(last_homebrew_path_idx + 1, HOMEBREW_LIBRARY_PATH.to_s) end require_relative "../vendor/bundle/bundler/setup" -require "portable_ruby_gems" if HOMEBREW_USING_PORTABLE_RUBY +Homebrew::FastBootRequire.from_archdir("portable_ruby_gems") if HOMEBREW_USING_PORTABLE_RUBY $LOAD_PATH.unshift "#{HOMEBREW_LIBRARY_PATH}/vendor/bundle/#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/" \ "bundler-#{Homebrew::HOMEBREW_BUNDLER_VERSION}/lib" $LOAD_PATH.uniq! diff --git a/Library/Homebrew/startup.rb b/Library/Homebrew/startup.rb index 1eff1ae226..5c9b3a10a0 100644 --- a/Library/Homebrew/startup.rb +++ b/Library/Homebrew/startup.rb @@ -2,9 +2,10 @@ # frozen_string_literal: true # This file should be the first `require` in all entrypoints of `brew`. +# Bootsnap should be loaded as early as possible. require_relative "standalone/init" +require_relative "startup/bootsnap" require_relative "startup/ruby_path" require "startup/config" -require_relative "startup/bootsnap" require_relative "standalone/sorbet" diff --git a/Library/Homebrew/startup/bootsnap.rb b/Library/Homebrew/startup/bootsnap.rb index 375600fb10..1401ee4d21 100644 --- a/Library/Homebrew/startup/bootsnap.rb +++ b/Library/Homebrew/startup/bootsnap.rb @@ -34,17 +34,21 @@ module Homebrew end private_class_method def self.enabled? - HOMEBREW_USING_PORTABLE_RUBY && ENV["HOMEBREW_NO_BOOTSNAP"].nil? && !ENV["HOMEBREW_BOOTSNAP"].nil? + !ENV["HOMEBREW_BOOTSNAP_GEM_PATH"].to_s.empty? && ENV["HOMEBREW_NO_BOOTSNAP"].nil? end def self.load!(compile_cache: true) return unless enabled? - require "bootsnap" + require ENV.fetch("HOMEBREW_BOOTSNAP_GEM_PATH") ::Bootsnap.setup( cache_dir:, ignore_directories:, + # In development environments the bootsnap compilation cache is + # generated on the fly when source files are loaded. + # https://github.com/Shopify/bootsnap?tab=readme-ov-file#precompilation + development_mode: true, load_path_cache: true, compile_cache_iseq: compile_cache, compile_cache_yaml: compile_cache, diff --git a/Library/Homebrew/utils/gems.rb b/Library/Homebrew/utils/gems.rb index 7559aff210..e21cf76a2e 100644 --- a/Library/Homebrew/utils/gems.rb +++ b/Library/Homebrew/utils/gems.rb @@ -5,7 +5,7 @@ # work as the first item in `brew.rb` so we can load gems with Bundler when # needed before anything else is loaded (e.g. `json`). -require "English" +Homebrew::FastBootRequire.from_rubylibdir("English") module Homebrew # Keep in sync with the `Gemfile.lock`'s BUNDLED WITH. diff --git a/Library/Homebrew/utils/ruby.sh b/Library/Homebrew/utils/ruby.sh index dcf45c9ff3..4919918961 100644 --- a/Library/Homebrew/utils/ruby.sh +++ b/Library/Homebrew/utils/ruby.sh @@ -127,6 +127,10 @@ If there's no Homebrew Portable Ruby available for your processor: if [[ -x "${vendor_ruby_path}" ]] then HOMEBREW_RUBY_PATH="${vendor_ruby_path}" + HOMEBREW_BOOTSNAP_GEM_PATH="$( + shopt -s nullglob + echo "${vendor_ruby_root}"/lib/ruby/gems/*/gems/bootsnap-*/lib/bootsnap 2>/dev/null + )" TERMINFO_DIRS="${vendor_ruby_terminfo}" if [[ "${vendor_ruby_current_version}" != "${HOMEBREW_PORTABLE_RUBY_VERSION}" ]] then @@ -146,7 +150,7 @@ If there's no Homebrew Portable Ruby available for your processor: fi fi - export HOMEBREW_RUBY_PATH + export HOMEBREW_RUBY_PATH HOMEBREW_BOOTSNAP_GEM_PATH [[ -n "${HOMEBREW_LINUX}" && -n "${TERMINFO_DIRS}" ]] && export TERMINFO_DIRS } diff --git a/docs/Manpage.md b/docs/Manpage.md index 8ba357ed83..ad1f0f591b 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -3843,11 +3843,6 @@ command execution e.g. `$(cat file)`. *Default:* `$BAT_THEME`. -`HOMEBREW_BOOTSNAP` - -: If set, use Bootsnap to speed up repeated `brew` calls. A no-op on Linux when - not using Homebrew's vendored, relocatable Ruby. - `HOMEBREW_BOTTLE_DOMAIN` : Use this URL as the download mirror for bottles. If bottles at that URL are diff --git a/manpages/brew.1 b/manpages/brew.1 index d9d58c2e06..fdf4c98a05 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -2473,9 +2473,6 @@ Use this as the \fBbat\fP theme for syntax highlighting\. \fIDefault:\fP \fB$BAT_THEME\fP\&\. .RE .TP -\fBHOMEBREW_BOOTSNAP\fP -If set, use Bootsnap to speed up repeated \fBbrew\fP calls\. A no\-op on Linux when not using Homebrew\[u2019]s vendored, relocatable Ruby\. -.TP \fBHOMEBREW_BOTTLE_DOMAIN\fP Use this URL as the download mirror for bottles\. If bottles at that URL are temporarily unavailable, the default bottle domain will be used as a fallback mirror\. For example, \fBexport HOMEBREW_BOTTLE_DOMAIN=http://localhost:8080\fP will cause all bottles to download from the prefix \fBhttp://localhost:8080/\fP\&\. If bottles are not available at \fB$HOMEBREW_BOTTLE_DOMAIN\fP they will be downloaded from the default bottle domain\. .RS