diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index 0e5102ddb8..b6cc9f836a 100644 --- a/Library/Homebrew/cleanup.rb +++ b/Library/Homebrew/cleanup.rb @@ -558,7 +558,7 @@ module Homebrew return unless bootsnap.directory? bootsnap.each_child do |subdir| - cleanup_path(subdir) { FileUtils.rm_r(subdir) } if subdir.basename.to_s != Homebrew.bootsnap_key + cleanup_path(subdir) { FileUtils.rm_r(subdir) } if subdir.basename.to_s != Homebrew::Bootsnap.key end end diff --git a/Library/Homebrew/standalone/init.rbi b/Library/Homebrew/standalone/init.rbi new file mode 100644 index 0000000000..95b05bc303 --- /dev/null +++ b/Library/Homebrew/standalone/init.rbi @@ -0,0 +1,3 @@ +# typed: strict + +HOMEBREW_USING_PORTABLE_RUBY = T.let(false, T::Boolean) diff --git a/Library/Homebrew/startup/bootsnap.rb b/Library/Homebrew/startup/bootsnap.rb index 01a9a662ea..375600fb10 100644 --- a/Library/Homebrew/startup/bootsnap.rb +++ b/Library/Homebrew/startup/bootsnap.rb @@ -1,46 +1,67 @@ # typed: true # frozen_string_literal: true -homebrew_bootsnap_enabled = HOMEBREW_USING_PORTABLE_RUBY && - ENV["HOMEBREW_NO_BOOTSNAP"].nil? && - !ENV["HOMEBREW_BOOTSNAP"].nil? - module Homebrew - def self.bootsnap_key - @bootsnap_key ||= begin - require "digest/sha2" + module Bootsnap + def self.key + @key ||= begin + require "digest/sha2" - checksum = Digest::SHA256.new - checksum << RUBY_VERSION - checksum << RUBY_PLATFORM - checksum << Dir.children(File.join(Gem.paths.path, "gems")).join(",") + checksum = Digest::SHA256.new + checksum << RUBY_VERSION + checksum << RUBY_PLATFORM + checksum << Dir.children(File.join(Gem.paths.path, "gems")).join(",") - checksum.hexdigest + checksum.hexdigest + end + end + + private_class_method def self.cache_dir + cache = ENV.fetch("HOMEBREW_CACHE", nil) || ENV.fetch("HOMEBREW_DEFAULT_CACHE", nil) + raise "Needs HOMEBREW_CACHE or HOMEBREW_DEFAULT_CACHE!" if cache.nil? || cache.empty? + + File.join(cache, "bootsnap", key) + end + + private_class_method def self.ignore_directories + # We never do `require "vendor/bundle/ruby/..."` or `require "vendor/portable-ruby/..."`, + # so let's slim the cache a bit by excluding them. + # Note that gems within `bundle/ruby` will still be cached - these are when directory walking down from above. + [ + (HOMEBREW_LIBRARY_PATH/"vendor/bundle/ruby").to_s, + (HOMEBREW_LIBRARY_PATH/"vendor/portable-ruby").to_s, + ] + end + + private_class_method def self.enabled? + HOMEBREW_USING_PORTABLE_RUBY && ENV["HOMEBREW_NO_BOOTSNAP"].nil? && !ENV["HOMEBREW_BOOTSNAP"].nil? + end + + def self.load!(compile_cache: true) + return unless enabled? + + require "bootsnap" + + ::Bootsnap.setup( + cache_dir:, + ignore_directories:, + load_path_cache: true, + compile_cache_iseq: compile_cache, + compile_cache_yaml: compile_cache, + compile_cache_json: compile_cache, + ) + end + + def self.reset! + return unless enabled? + + ::Bootsnap.unload_cache! + @key = nil + + # The compile cache doesn't get unloaded so we don't need to load it again! + load!(compile_cache: false) end end end -if homebrew_bootsnap_enabled - require "bootsnap" - - cache = ENV.fetch("HOMEBREW_CACHE", nil) || ENV.fetch("HOMEBREW_DEFAULT_CACHE", nil) - raise "Needs HOMEBREW_CACHE or HOMEBREW_DEFAULT_CACHE!" if cache.nil? || cache.empty? - - cache = File.join(cache, "bootsnap", Homebrew.bootsnap_key) - - # We never do `require "vendor/bundle/ruby/..."` or `require "vendor/portable-ruby/..."`, - # so let's slim the cache a bit by excluding them. - # Note that gems within `bundle/ruby` will still be cached - these are when directory walking down from above. - ignore_directories = [ - (HOMEBREW_LIBRARY_PATH/"vendor/bundle/ruby").to_s, - (HOMEBREW_LIBRARY_PATH/"vendor/portable-ruby").to_s, - ] - - Bootsnap.setup( - cache_dir: cache, - ignore_directories:, - load_path_cache: true, - compile_cache_iseq: true, - compile_cache_yaml: true, - ) -end +Homebrew::Bootsnap.load! diff --git a/Library/Homebrew/startup/bootsnap.rbi b/Library/Homebrew/startup/bootsnap.rbi index 3f42e1837d..d71df2d278 100644 --- a/Library/Homebrew/startup/bootsnap.rbi +++ b/Library/Homebrew/startup/bootsnap.rbi @@ -1,8 +1,25 @@ # typed: strict module Homebrew - sig { returns(String) } - def self.bootsnap_key; end + module Bootsnap + sig { returns(String) } + def self.key; end + + sig { returns(String) } + private_class_method def self.cache_dir; end + + sig { returns(T::Array[String]) } + private_class_method def self.ignore_directories; end + + sig { returns(T::Boolean) } + private_class_method def self.enabled?; end + + sig { params(compile_cache: T::Boolean).void } + def self.load!(compile_cache: true); end + + sig { void } + def self.reset!; end + end end module Bootsnap @@ -29,5 +46,9 @@ module Bootsnap compile_cache_iseq: true, compile_cache_yaml: true, compile_cache_json: true - ); end + ) + end + + sig { void } + def self.unload_cache!; end end diff --git a/Library/Homebrew/utils/gems.rb b/Library/Homebrew/utils/gems.rb index d4c2fa20dd..7559aff210 100644 --- a/Library/Homebrew/utils/gems.rb +++ b/Library/Homebrew/utils/gems.rb @@ -303,6 +303,7 @@ module Homebrew exec bundle, "install", out: :err end) if $CHILD_STATUS.success? + Homebrew::Bootsnap.reset! if defined?(Homebrew::Bootsnap) # Gem install can run before Bootsnap loads true else message = <<~EOS