Merge pull request #20061 from Homebrew/extend_cleanup

Cleanup `extend/` directory usage.
This commit is contained in:
Mike McQuaid 2025-06-09 19:37:14 +00:00 committed by GitHub
commit cfdc6fd3fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 368 additions and 273 deletions

View File

@ -1,7 +1,7 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
require "extend/cachable" require "cachable"
require "api/download" require "api/download"
module Homebrew module Homebrew

View File

@ -1,7 +1,7 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "extend/cachable" require "cachable"
require "api/download" require "api/download"
module Homebrew module Homebrew

View File

@ -130,6 +130,9 @@ module Homebrew
@formula_versions_from_env[formula_env_name] @formula_versions_from_env[formula_env_name]
end end
sig { void }
def prepend_pkgconf_path_if_needed!; end
sig { void } sig { void }
def reset! def reset!
@mas_installed = T.let(nil, T.nilable(T::Boolean)) @mas_installed = T.let(nil, T.nilable(T::Boolean))

View File

@ -64,14 +64,8 @@ module Homebrew
ENV.prepend_path "PATH", Pathname.new(dep_root)/"shims" ENV.prepend_path "PATH", Pathname.new(dep_root)/"shims"
end end
# Setup pkg-config, if present, to help locate packages # Setup pkgconf, if needed, to help locate packages
# Only need this on Linux as Homebrew provides a shim on macOS Bundle.prepend_pkgconf_path_if_needed!
# TODO: use extend/OS here
# rubocop:todo Homebrew/MoveToExtendOS
if OS.linux? && (pkgconf = Formulary.factory("pkgconf")) && pkgconf.any_version_installed?
ENV.prepend_path "PATH", pkgconf.opt_bin.to_s
end
# rubocop:enable Homebrew/MoveToExtendOS
# For commands which aren't either absolute or relative # For commands which aren't either absolute or relative
# Add the command directory to PATH, since it may get blown away by superenv # Add the command directory to PATH, since it may get blown away by superenv

View File

@ -11,19 +11,6 @@ module Homebrew
def skip?(entry, silent: false) def skip?(entry, silent: false)
require "bundle/brew_dumper" require "bundle/brew_dumper"
# TODO: use extend/OS here
# rubocop:todo Homebrew/MoveToExtendOS
if (Hardware::CPU.arm? || OS.linux?) &&
Homebrew.default_prefix? &&
entry.type == :brew && entry.name.exclude?("/") &&
(formula = BrewDumper.formulae_by_full_name(entry.name)) &&
formula[:official_tap] &&
!formula[:bottled]
reason = Hardware::CPU.arm? ? "Apple Silicon" : "Linux"
puts Formatter.warning "Skipping #{entry.name} (no bottle for #{reason})" unless silent
return true
end
# rubocop:enable Homebrew/MoveToExtendOS
return true if @failed_taps&.any? do |tap| return true if @failed_taps&.any? do |tap|
prefix = "#{tap}/" prefix = "#{tap}/"
entry.name.start_with?(prefix) || entry.options[:full_name]&.start_with?(prefix) entry.name.start_with?(prefix) || entry.options[:full_name]&.start_with?(prefix)

View File

@ -8,7 +8,7 @@ require "cask/dsl"
require "cask/metadata" require "cask/metadata"
require "cask/tab" require "cask/tab"
require "utils/bottles" require "utils/bottles"
require "extend/api_hashable" require "api_hashable"
module Cask module Cask
# An instance of a cask. # An instance of a cask.

View File

@ -26,7 +26,7 @@ require "cask/dsl/version"
require "cask/url" require "cask/url"
require "cask/utils" require "cask/utils"
require "extend/on_system" require "on_system"
module Cask module Cask
# Class representing the domain-specific language used for casks. # Class representing the domain-specific language used for casks.

View File

@ -2,7 +2,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "cask/utils" require "cask/utils"
require "extend/on_system" require "on_system"
module Cask module Cask
class DSL class DSL

View File

@ -5,7 +5,7 @@ require "dependency"
require "dependencies" require "dependencies"
require "requirement" require "requirement"
require "requirements" require "requirements"
require "extend/cachable" require "cachable"
# A dependency is a formula that another formula needs to install. # A dependency is a formula that another formula needs to install.
# A requirement is something other than a formula that another formula # A requirement is something other than a formula that another formula

View File

@ -227,7 +227,7 @@ module Homebrew
private private
sig { sig {
params(string: String, keg: Keg, ignores: T::Array[String], params(string: String, keg: Keg, ignores: T::Array[Regexp],
formula_and_runtime_deps_names: T.nilable(T::Array[String])).returns(T::Boolean) formula_and_runtime_deps_names: T.nilable(T::Array[String])).returns(T::Boolean)
} }
def keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil) def keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil)
@ -373,35 +373,17 @@ module Homebrew
[gnu_tar(gnu_tar_formula), reproducible_gnutar_args(mtime)].freeze [gnu_tar(gnu_tar_formula), reproducible_gnutar_args(mtime)].freeze
end end
sig { params(formula: T.untyped).returns(T::Array[T.untyped]) } sig { params(formula: Formula).returns(T::Array[Regexp]) }
def formula_ignores(formula) def formula_ignores(formula)
ignores = []
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
prefix_regex = Regexp.escape(HOMEBREW_PREFIX)
# Ignore matches to go keg, because all go binaries are statically linked. # Ignore matches to go keg, because all go binaries are statically linked.
any_go_deps = formula.deps.any? do |dep| any_go_deps = formula.deps.any? do |dep|
Version.formula_optionally_versioned_regex(:go).match?(dep.name) Version.formula_optionally_versioned_regex(:go).match?(dep.name)
end end
if any_go_deps return [] unless any_go_deps
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
go_regex = Version.formula_optionally_versioned_regex(:go, full: false) go_regex = Version.formula_optionally_versioned_regex(:go, full: false)
ignores << %r{#{cellar_regex}/#{go_regex}/[\d.]+/libexec} Array(%r{#{cellar_regex}/#{go_regex}/[\d.]+/libexec})
end
# TODO: Refactor and move to extend/os
# rubocop:disable Homebrew/MoveToExtendOS
ignores << case formula.name
# On Linux, GCC installation can be moved so long as the whole directory tree is moved together:
# https://gcc-help.gcc.gnu.narkive.com/GnwuCA7l/moving-gcc-from-the-installation-path-is-it-allowed.
when Version.formula_optionally_versioned_regex(:gcc)
Regexp.union(%r{#{cellar_regex}/gcc}, %r{#{prefix_regex}/opt/gcc}) if OS.linux?
# binutils is relocatable for the same reason: https://github.com/Homebrew/brew/pull/11899#issuecomment-906804451.
when Version.formula_optionally_versioned_regex(:binutils)
%r{#{cellar_regex}/binutils} if OS.linux?
end
# rubocop:enable Homebrew/MoveToExtendOS
ignores.compact
end end
sig { params(formula: Formula).void } sig { params(formula: Formula).void }

View File

@ -120,31 +120,14 @@ module Homebrew
] ]
bundle_args << "--fail-fast" if args.fail_fast? bundle_args << "--fail-fast" if args.fail_fast?
bundle_args << "--profile" << args.profile if args.profile bundle_args << "--profile" << args.profile if args.profile
# TODO: Refactor and move to extend/os
# rubocop:disable Homebrew/MoveToExtendOS
unless OS.mac?
bundle_args << "--tag" << "~needs_macos" << "--tag" << "~cask"
bundle_args << "--tag" << "~needs_homebrew_core" if ENV["CI"]
bundle_args << "--tag" << "~needs_svn" unless args.online?
files = files.grep_v(%r{^test/(os/mac|cask)(/.*|_spec\.rb)$})
end
unless OS.linux?
bundle_args << "--tag" << "~needs_linux" << "--tag" << "~needs_systemd"
files = files.grep_v(%r{^test/os/linux(/.*|_spec\.rb)$})
end
# rubocop:enable Homebrew/MoveToExtendOS
bundle_args << "--tag" << "~needs_arm" unless Hardware::CPU.arm? bundle_args << "--tag" << "~needs_arm" unless Hardware::CPU.arm?
bundle_args << "--tag" << "~needs_intel" unless Hardware::CPU.intel? bundle_args << "--tag" << "~needs_intel" unless Hardware::CPU.intel?
bundle_args << "--tag" << "~needs_network" unless args.online? bundle_args << "--tag" << "~needs_network" unless args.online?
bundle_args << "--tag" << "~needs_ci" unless ENV["CI"] bundle_args << "--tag" << "~needs_ci" unless ENV["CI"]
bundle_args = os_bundle_args(bundle_args)
files = os_files(files)
puts "Randomized with seed #{seed}" puts "Randomized with seed #{seed}"
ENV["HOMEBREW_DEBUG"] = "1" if args.debug? # Used in spec_helper.rb to require the "debug" gem. ENV["HOMEBREW_DEBUG"] = "1" if args.debug? # Used in spec_helper.rb to require the "debug" gem.
@ -171,6 +154,41 @@ module Homebrew
private private
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def os_bundle_args(bundle_args)
# for generic tests, remove macOS or Linux specific tests
non_linux_bundle_args(non_macos_bundle_args(bundle_args))
end
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def non_macos_bundle_args(bundle_args)
bundle_args << "--tag" << "~needs_homebrew_core" if ENV["CI"]
bundle_args << "--tag" << "~needs_svn" unless args.online?
bundle_args << "--tag" << "~needs_macos" << "--tag" << "~cask"
end
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def non_linux_bundle_args(bundle_args)
bundle_args << "--tag" << "~needs_linux" << "--tag" << "~needs_systemd"
end
sig { params(files: T::Array[String]).returns(T::Array[String]) }
def os_files(files)
# for generic tests, remove macOS or Linux specific files
non_linux_files(non_macos_files(files))
end
sig { params(files: T::Array[String]).returns(T::Array[String]) }
def non_macos_files(files)
files.grep_v(%r{^test/(os/mac|cask)(/.*|_spec\.rb)$})
end
sig { params(files: T::Array[String]).returns(T::Array[String]) }
def non_linux_files(files)
files.grep_v(%r{^test/os/linux(/.*|_spec\.rb)$})
end
sig { returns(T::Array[String]) } sig { returns(T::Array[String]) }
def changed_test_files def changed_test_files
changed_files = Utils.popen_read("git", "diff", "--name-only", "master") changed_files = Utils.popen_read("git", "diff", "--name-only", "master")
@ -250,3 +268,5 @@ module Homebrew
end end
end end
end end
require "extend/os/dev-cmd/tests"

View File

@ -7,16 +7,6 @@ require "extend/ENV/shared"
require "extend/ENV/std" require "extend/ENV/std"
require "extend/ENV/super" require "extend/ENV/super"
module Kernel
sig { params(env: T.nilable(String)).returns(T::Boolean) }
def superenv?(env)
return false if env == "std"
!Superenv.bin.nil?
end
private :superenv?
end
# <!-- vale off --> # <!-- vale off -->
# @!parse # @!parse
# # `ENV` is not actually a class, but this makes YARD happy # # `ENV` is not actually a class, but this makes YARD happy

View File

@ -5,6 +5,14 @@
# TODO: move these out of `Kernel`. # TODO: move these out of `Kernel`.
module Kernel module Kernel
sig { params(env: T.nilable(String)).returns(T::Boolean) }
def superenv?(env)
return false if env == "std"
!Superenv.bin.nil?
end
private :superenv?
def require?(path) def require?(path)
return false if path.nil? return false if path.nil?

View File

@ -0,0 +1,5 @@
# typed: strict
# frozen_string_literal: true
require "extend/os/linux/dev-cmd/tests" if OS.linux?
require "extend/os/mac/dev-cmd/tests" if OS.mac?

View File

@ -9,6 +9,16 @@ module OS
def mas_installed? def mas_installed?
false false
end end
# Setup pkg-config, if present, to help locate packages
# Only need this on Linux as Homebrew provides a shim on macOS
sig { void }
def prepend_pkgconf_path_if_needed!
pkgconf = Formulary.factory("pkgconf")
return unless pkgconf.any_version_installed?
ENV.prepend_path "PATH", pkgconf.opt_bin.to_s
end
end end
end end
end end

View File

@ -0,0 +1,23 @@
# typed: strict
# frozen_string_literal: true
module OS
module Linux
module DevCmd
module Tests
extend T::Helpers
requires_ancestor { Homebrew::DevCmd::Tests }
private
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def os_bundle_args(bundle_args)
non_macos_bundle_args(bundle_args)
end
end
end
end
end
Homebrew::DevCmd::Tests.prepend(OS::Linux::DevCmd::Tests)

View File

@ -18,6 +18,26 @@ module OS
def gnu_tar(gnu_tar_formula) def gnu_tar(gnu_tar_formula)
"#{gnu_tar_formula.opt_bin}/gtar" "#{gnu_tar_formula.opt_bin}/gtar"
end end
sig { params(formula: Formula).returns(T::Array[Regexp]) }
def formula_ignores(formula)
ignores = super
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
prefix_regex = Regexp.escape(HOMEBREW_PREFIX)
ignores << case formula.name
# On Linux, GCC installation can be moved so long as the whole directory tree is moved together:
# https://gcc-help.gcc.gnu.narkive.com/GnwuCA7l/moving-gcc-from-the-installation-path-is-it-allowed.
when Version.formula_optionally_versioned_regex(:gcc)
Regexp.union(%r{#{cellar_regex}/gcc}, %r{#{prefix_regex}/opt/gcc}) if OS.linux?
# binutils is relocatable for the same reason: https://github.com/Homebrew/brew/pull/11899#issuecomment-906804451.
when Version.formula_optionally_versioned_regex(:binutils)
%r{#{cellar_regex}/binutils} if OS.linux?
end
ignores.compact
end
end end
end end
end end

View File

@ -0,0 +1,23 @@
# typed: strict
# frozen_string_literal: true
module OS
module Mac
module DevCmd
module Tests
extend T::Helpers
requires_ancestor { Homebrew::DevCmd::Tests }
private
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def os_bundle_args(bundle_args)
non_linux_bundle_args(bundle_args)
end
end
end
end
end
Homebrew::DevCmd::Tests.prepend(OS::Mac::DevCmd::Tests)

View File

@ -425,7 +425,7 @@ module OS
end end
def check_deprecated_caskroom_taps def check_deprecated_caskroom_taps
tapped_caskroom_taps = Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" } tapped_caskroom_taps = ::Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" }
.map(&:name) .map(&:name)
return if tapped_caskroom_taps.empty? return if tapped_caskroom_taps.empty?

View File

@ -8,7 +8,7 @@ module OS
requires_ancestor { Kernel } requires_ancestor { Kernel }
sig { params(tap: Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol)).returns(T::Boolean) } sig { params(tap: ::Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol)).returns(T::Boolean) }
def valid_casks?(tap, os_name: nil, arch: ::Hardware::CPU.type) def valid_casks?(tap, os_name: nil, arch: ::Hardware::CPU.type)
return true if os_name == :linux return true if os_name == :linux

View File

@ -0,0 +1,17 @@
# typed: strict
# frozen_string_literal: true
module OS
module Mac
module Tap
module ClassMethods
sig { returns(T::Array[::Tap]) }
def core_taps
[CoreTap.instance, CoreCaskTap.instance].freeze
end
end
end
end
end
Tap.singleton_class.prepend(OS::Mac::Tap::ClassMethods)

View File

@ -0,0 +1,4 @@
# typed: strict
# frozen_string_literal: true
require "extend/os/mac/tap" if OS.mac?

View File

@ -1,79 +1,9 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
module DiskUsageExtension
extend T::Helpers
requires_ancestor { Pathname }
sig { returns(Integer) }
def disk_usage
return @disk_usage if defined?(@disk_usage)
compute_disk_usage
@disk_usage
end
sig { returns(Integer) }
def file_count
return @file_count if defined?(@file_count)
compute_disk_usage
@file_count
end
sig { returns(String) }
def abv
out = +""
compute_disk_usage
out << "#{number_readable(@file_count)} files, " if @file_count > 1
out << disk_usage_readable(@disk_usage).to_s
out.freeze
end
private
sig { void }
def compute_disk_usage
if symlink? && !exist?
@file_count = 1
@disk_usage = 0
return
end
path = if symlink?
resolved_path
else
self
end
if path.directory?
scanned_files = Set.new
@file_count = 0
@disk_usage = 0
path.find do |f|
if f.directory?
@disk_usage += f.lstat.size
else
@file_count += 1 if f.basename.to_s != ".DS_Store"
# use Pathname#lstat instead of Pathname#stat to get info of symlink itself.
stat = f.lstat
file_id = [stat.dev, stat.ino]
# count hardlinks only once.
unless scanned_files.include?(file_id)
@disk_usage += stat.size
scanned_files.add(file_id)
end
end
end
else
@file_count = 1
@disk_usage = path.lstat.size
end
end
end
require "system_command" require "system_command"
require "extend/pathname/disk_usage_extension"
require "extend/pathname/observer_pathname_extension"
# Homebrew extends Ruby's `Pathname` to make our code more readable. # 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 # @see https://ruby-doc.org/stdlib-2.6.3/libdoc/pathname/rdoc/Pathname.html Ruby's Pathname API
@ -524,92 +454,3 @@ class Pathname
end end
end end
require "extend/os/pathname" require "extend/os/pathname"
require "context"
module ObserverPathnameExtension
extend T::Helpers
requires_ancestor { Pathname }
class << self
include Context
sig { returns(Integer) }
attr_accessor :n, :d
sig { void }
def reset_counts!
@n = @d = 0
@put_verbose_trimmed_warning = false
end
sig { returns(Integer) }
def total
n + d
end
sig { returns([Integer, Integer]) }
def counts
[n, d]
end
MAXIMUM_VERBOSE_OUTPUT = 100
private_constant :MAXIMUM_VERBOSE_OUTPUT
sig { returns(T::Boolean) }
def verbose?
return super unless ENV["CI"]
return false unless super
if total < MAXIMUM_VERBOSE_OUTPUT
true
else
unless @put_verbose_trimmed_warning
puts "Only the first #{MAXIMUM_VERBOSE_OUTPUT} operations were output."
@put_verbose_trimmed_warning = true
end
false
end
end
end
sig { void }
def unlink
super
puts "rm #{self}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.n += 1
end
sig { void }
def mkpath
super
puts "mkdir -p #{self}" if ObserverPathnameExtension.verbose?
end
sig { void }
def rmdir
super
puts "rmdir #{self}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.d += 1
end
sig { params(src: Pathname).void }
def make_relative_symlink(src)
super
puts "ln -s #{src.relative_path_from(dirname)} #{basename}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.n += 1
end
sig { void }
def install_info
super
puts "info #{self}" if ObserverPathnameExtension.verbose?
end
sig { void }
def uninstall_info
super
puts "uninfo #{self}" if ObserverPathnameExtension.verbose?
end
end

View File

@ -0,0 +1,74 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
module DiskUsageExtension
extend T::Helpers
requires_ancestor { Pathname }
sig { returns(Integer) }
def disk_usage
return @disk_usage if defined?(@disk_usage)
compute_disk_usage
@disk_usage
end
sig { returns(Integer) }
def file_count
return @file_count if defined?(@file_count)
compute_disk_usage
@file_count
end
sig { returns(String) }
def abv
out = +""
compute_disk_usage
out << "#{number_readable(@file_count)} files, " if @file_count > 1
out << disk_usage_readable(@disk_usage).to_s
out.freeze
end
private
sig { void }
def compute_disk_usage
if symlink? && !exist?
@file_count = 1
@disk_usage = 0
return
end
path = if symlink?
resolved_path
else
self
end
if path.directory?
scanned_files = Set.new
@file_count = 0
@disk_usage = 0
path.find do |f|
if f.directory?
@disk_usage += f.lstat.size
else
@file_count += 1 if f.basename.to_s != ".DS_Store"
# use Pathname#lstat instead of Pathname#stat to get info of symlink itself.
stat = f.lstat
file_id = [stat.dev, stat.ino]
# count hardlinks only once.
unless scanned_files.include?(file_id)
@disk_usage += stat.size
scanned_files.add(file_id)
end
end
end
else
@file_count = 1
@disk_usage = path.lstat.size
end
end
end

View File

@ -0,0 +1,91 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "context"
module ObserverPathnameExtension
extend T::Helpers
requires_ancestor { Pathname }
class << self
include Context
sig { returns(Integer) }
attr_accessor :n, :d
sig { void }
def reset_counts!
@n = @d = 0
@put_verbose_trimmed_warning = false
end
sig { returns(Integer) }
def total
n + d
end
sig { returns([Integer, Integer]) }
def counts
[n, d]
end
MAXIMUM_VERBOSE_OUTPUT = 100
private_constant :MAXIMUM_VERBOSE_OUTPUT
sig { returns(T::Boolean) }
def verbose?
return super unless ENV["CI"]
return false unless super
if total < MAXIMUM_VERBOSE_OUTPUT
true
else
unless @put_verbose_trimmed_warning
puts "Only the first #{MAXIMUM_VERBOSE_OUTPUT} operations were output."
@put_verbose_trimmed_warning = true
end
false
end
end
end
sig { void }
def unlink
super
puts "rm #{self}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.n += 1
end
sig { void }
def mkpath
super
puts "mkdir -p #{self}" if ObserverPathnameExtension.verbose?
end
sig { void }
def rmdir
super
puts "rmdir #{self}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.d += 1
end
sig { params(src: Pathname).void }
def make_relative_symlink(src)
super
puts "ln -s #{src.relative_path_from(dirname)} #{basename}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.n += 1
end
sig { void }
def install_info
super
puts "info #{self}" if ObserverPathnameExtension.verbose?
end
sig { void }
def uninstall_info
super
puts "uninfo #{self}" if ObserverPathnameExtension.verbose?
end
end

View File

@ -5,5 +5,9 @@ require "time"
class Time class Time
# Backwards compatibility for formulae that used this ActiveSupport extension # Backwards compatibility for formulae that used this ActiveSupport extension
alias rfc3339 xmlschema sig { returns(String) }
def rfc3339
odeprecated "Time#rfc3339", "Time#xmlschema"
xmlschema
end
end end

View File

@ -38,9 +38,9 @@ require "tab"
require "mktemp" require "mktemp"
require "find" require "find"
require "utils/spdx" require "utils/spdx"
require "extend/on_system" require "on_system"
require "api" require "api"
require "extend/api_hashable" require "api_hashable"
# A formula provides instructions and metadata for Homebrew to install a piece # A formula provides instructions and metadata for Homebrew to install a piece
# of software. Every Homebrew formula is a {Formula}. # of software. Every Homebrew formula is a {Formula}.

View File

@ -2,7 +2,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "digest/sha2" require "digest/sha2"
require "extend/cachable" require "cachable"
require "tab" require "tab"
require "utils" require "utils"
require "utils/bottles" require "utils/bottles"

View File

@ -138,7 +138,7 @@ require "extend/kernel"
require "os" require "os"
require "extend/array" require "extend/array"
require "extend/cachable" require "cachable"
require "extend/enumerable" require "extend/enumerable"
require "extend/string" require "extend/string"
require "extend/pathname" require "extend/pathname"

View File

@ -4,7 +4,7 @@
require "keg_relocate" require "keg_relocate"
require "language/python" require "language/python"
require "lock_file" require "lock_file"
require "extend/cachable" require "cachable"
# Installation prefix of a formula. # Installation prefix of a formula.
class Keg class Keg

View File

@ -1,17 +1,17 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
class IO class ReadlineNonblock
sig { params(sep: String).returns(String) } sig { params(io: IO).returns(String) }
def readline_nonblock(sep = $INPUT_RECORD_SEPARATOR) def self.read(io)
line = +"" line = +""
buffer = +"" buffer = +""
begin begin
loop do loop do
break if buffer == sep break if buffer == $INPUT_RECORD_SEPARATOR
read_nonblock(1, buffer) io.read_nonblock(1, buffer)
line.concat(buffer) line.concat(buffer)
end end

View File

@ -4,7 +4,7 @@
require "downloadable" require "downloadable"
require "mktemp" require "mktemp"
require "livecheck" require "livecheck"
require "extend/on_system" require "on_system"
# Resource is the fundamental representation of an external resource. The # Resource is the fundamental representation of an external resource. The
# primary formula download, along with other declared resources, are instances # primary formula download, along with other declared resources, are instances

View File

@ -4,7 +4,7 @@
require "cxxstdlib" require "cxxstdlib"
require "json" require "json"
require "development_tools" require "development_tools"
require "extend/cachable" require "cachable"
require "utils/curl" require "utils/curl"
# Rather than calling `new` directly, use one of the class methods like {SBOM.create}. # Rather than calling `new` directly, use one of the class methods like {SBOM.create}.

View File

@ -2,7 +2,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "ipaddr" require "ipaddr"
require "extend/on_system" require "on_system"
require "utils/service" require "utils/service"
module Homebrew module Homebrew

View File

@ -12,7 +12,7 @@ require "utils/bottles"
require "patch" require "patch"
require "compilers" require "compilers"
require "macos_version" require "macos_version"
require "extend/on_system" require "on_system"
class SoftwareSpec class SoftwareSpec
include Downloadable include Downloadable

View File

@ -6,7 +6,7 @@ require "shellwords"
require "uri" require "uri"
require "context" require "context"
require "extend/io" require "readline_nonblock"
require "utils/timer" require "utils/timer"
# Class for running sub-processes and capturing their output and exit status. # Class for running sub-processes and capturing their output and exit status.
@ -361,7 +361,7 @@ class SystemCommand
readable_sources.each do |source| readable_sources.each do |source|
loop do loop do
line = source.readline_nonblock || "" line = ReadlineNonblock.read(source)
yield(sources.fetch(source), line) yield(sources.fetch(source), line)
end end
rescue EOFError rescue EOFError

View File

@ -5,7 +5,7 @@ require "cxxstdlib"
require "options" require "options"
require "json" require "json"
require "development_tools" require "development_tools"
require "extend/cachable" require "cachable"
# Rather than calling `new` directly, use one of the class methods like {Tab.create}. # Rather than calling `new` directly, use one of the class methods like {Tab.create}.
class AbstractTab class AbstractTab

View File

@ -1079,15 +1079,12 @@ class Tap
# All locally installed and core taps. Core taps might not be installed locally when using the API. # All locally installed and core taps. Core taps might not be installed locally when using the API.
sig { returns(T::Array[Tap]) } sig { returns(T::Array[Tap]) }
def self.all def self.all
cache[:all] ||= begin cache[:all] ||= installed | core_taps
core_taps = [
CoreTap.instance,
# The conditional is valid here because we only want the cask tap on macOS.
(CoreCaskTap.instance if OS.mac?), # rubocop:disable Homebrew/MoveToExtendOS
].compact
installed | core_taps
end end
sig { returns(T::Array[Tap]) }
def self.core_taps
[CoreTap.instance].freeze
end end
# Enumerate all available {Tap}s. # Enumerate all available {Tap}s.
@ -1517,3 +1514,5 @@ class TapConfig
Homebrew::Settings.delete key, repo: tap.path Homebrew::Settings.delete key, repo: tap.path
end end
end end
require "extend/os/tap"

View File

@ -4,7 +4,7 @@
require "context" require "context"
require "erb" require "erb"
require "settings" require "settings"
require "extend/cachable" require "cachable"
module Utils module Utils
# Helper module for fetching and reporting analytics data. # Helper module for fetching and reporting analytics data.

View File

@ -34,7 +34,7 @@ For more information on how to express more complex types, refer to the official
### Ruby interface files (`.rbi`) ### Ruby interface files (`.rbi`)
[RBI files](https://sorbet.org/docs/rbi) help Sorbet learn about constants, ancestors and methods defined in ways it doesn't understand natively. We can also create an RBI file to help Sorbet understand dynamic definitions. Some of these files are automatically generated (see the next section) and some are manually written, e.g. [`extend/on_system.rbi`](https://github.com/Homebrew/brew/blob/975fe8a83fd57a8d8e790ec6fb10c2f13f705d02/Library/Homebrew/extend/on_system.rbi). [RBI files](https://sorbet.org/docs/rbi) help Sorbet learn about constants, ancestors and methods defined in ways it doesn't understand natively. We can also create an RBI file to help Sorbet understand dynamic definitions. Some of these files are automatically generated (see the next section) and some are manually written, e.g. [`on_system.rbi`](https://github.com/Homebrew/brew/blob/HEAD/Library/Homebrew/on_system.rbi).
There are also a very small number of files that Homebrew loads before `sorbet-runtime`, such as `utils/gems.rb`. Those files cannot have type signatures alongside the code itself, so RBI files are used there instead to retain static type checking. There are also a very small number of files that Homebrew loads before `sorbet-runtime`, such as `utils/gems.rb`. Those files cannot have type signatures alongside the code itself, so RBI files are used there instead to retain static type checking.