From dc71b7c8f6c16cd9a4d6a910326d9914101130a2 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Mon, 9 Jun 2025 19:06:16 +0100 Subject: [PATCH] Cleanup `extend/` directory usage. - move some things out of `extend` that don't really fit there e.g. `Module`s that are included but not doing any overriding/monkeypatching - move some code into `extend/os` to fix all remaining `rubocop:todo Homebrew/MoveToExtendOS`s - remove some unneeded `bundle` skipper code that doesn't really make sense given our current bottling strategy - extract some `Pathname` extensions to `extend/pathname` for separate files - move a `ENV` `Kernel` extension into `kernel.rb` - `odeprecate` a seemingly unused backwards compatibility method - move `readline_nonblock` from a monkeypatch to a `ReadlineNonblock.read` method as its only used in one place - fix up a link in documentation --- Library/Homebrew/api/cask.rb | 2 +- Library/Homebrew/api/formula.rb | 2 +- Library/Homebrew/{extend => }/api_hashable.rb | 0 Library/Homebrew/bundle.rb | 3 + Library/Homebrew/bundle/commands/exec.rb | 10 +- Library/Homebrew/bundle/skipper.rb | 13 -- Library/Homebrew/{extend => }/cachable.rb | 0 Library/Homebrew/cask/cask.rb | 2 +- Library/Homebrew/cask/dsl.rb | 2 +- Library/Homebrew/cask/dsl/base.rb | 2 +- Library/Homebrew/dependency_collector.rb | 2 +- Library/Homebrew/dev-cmd/bottle.rb | 30 +--- Library/Homebrew/dev-cmd/tests.rb | 60 ++++--- Library/Homebrew/extend/ENV.rb | 10 -- Library/Homebrew/extend/kernel.rb | 8 + Library/Homebrew/extend/os/dev-cmd/tests.rb | 5 + .../Homebrew/extend/os/linux/bundle/bundle.rb | 10 ++ .../Homebrew/extend/os/linux/dev-cmd/tests.rb | 23 +++ .../Homebrew/extend/os/mac/dev-cmd/bottle.rb | 20 +++ .../Homebrew/extend/os/mac/dev-cmd/tests.rb | 23 +++ Library/Homebrew/extend/os/mac/diagnostic.rb | 4 +- Library/Homebrew/extend/os/mac/readall.rb | 2 +- Library/Homebrew/extend/os/mac/tap.rb | 17 ++ Library/Homebrew/extend/os/tap.rb | 4 + Library/Homebrew/extend/pathname.rb | 163 +----------------- .../extend/pathname/disk_usage_extension.rb | 74 ++++++++ .../pathname/observer_pathname_extension.rb | 91 ++++++++++ Library/Homebrew/extend/time.rb | 6 +- Library/Homebrew/formula.rb | 4 +- Library/Homebrew/formulary.rb | 2 +- Library/Homebrew/global.rb | 2 +- Library/Homebrew/keg.rb | 2 +- Library/Homebrew/{extend => }/on_system.rb | 0 Library/Homebrew/{extend => }/on_system.rbi | 0 .../{extend/io.rb => readline_nonblock.rb} | 10 +- Library/Homebrew/resource.rb | 2 +- Library/Homebrew/sbom.rb | 2 +- Library/Homebrew/service.rb | 2 +- Library/Homebrew/software_spec.rb | 2 +- Library/Homebrew/system_command.rb | 4 +- Library/Homebrew/tab.rb | 2 +- Library/Homebrew/tap.rb | 15 +- Library/Homebrew/utils/analytics.rb | 2 +- docs/Typechecking.md | 2 +- 44 files changed, 368 insertions(+), 273 deletions(-) rename Library/Homebrew/{extend => }/api_hashable.rb (100%) rename Library/Homebrew/{extend => }/cachable.rb (100%) create mode 100644 Library/Homebrew/extend/os/dev-cmd/tests.rb create mode 100644 Library/Homebrew/extend/os/linux/dev-cmd/tests.rb create mode 100644 Library/Homebrew/extend/os/mac/dev-cmd/tests.rb create mode 100644 Library/Homebrew/extend/os/mac/tap.rb create mode 100644 Library/Homebrew/extend/os/tap.rb create mode 100644 Library/Homebrew/extend/pathname/disk_usage_extension.rb create mode 100644 Library/Homebrew/extend/pathname/observer_pathname_extension.rb rename Library/Homebrew/{extend => }/on_system.rb (100%) rename Library/Homebrew/{extend => }/on_system.rbi (100%) rename Library/Homebrew/{extend/io.rb => readline_nonblock.rb} (60%) diff --git a/Library/Homebrew/api/cask.rb b/Library/Homebrew/api/cask.rb index a06aa9b495..5de578f0ed 100644 --- a/Library/Homebrew/api/cask.rb +++ b/Library/Homebrew/api/cask.rb @@ -1,7 +1,7 @@ # typed: true # rubocop:todo Sorbet/StrictSigil # frozen_string_literal: true -require "extend/cachable" +require "cachable" require "api/download" module Homebrew diff --git a/Library/Homebrew/api/formula.rb b/Library/Homebrew/api/formula.rb index fe02c4bc08..5ef30a0f0a 100644 --- a/Library/Homebrew/api/formula.rb +++ b/Library/Homebrew/api/formula.rb @@ -1,7 +1,7 @@ # typed: strict # frozen_string_literal: true -require "extend/cachable" +require "cachable" require "api/download" module Homebrew diff --git a/Library/Homebrew/extend/api_hashable.rb b/Library/Homebrew/api_hashable.rb similarity index 100% rename from Library/Homebrew/extend/api_hashable.rb rename to Library/Homebrew/api_hashable.rb diff --git a/Library/Homebrew/bundle.rb b/Library/Homebrew/bundle.rb index e5676f1094..43f8de77a5 100644 --- a/Library/Homebrew/bundle.rb +++ b/Library/Homebrew/bundle.rb @@ -130,6 +130,9 @@ module Homebrew @formula_versions_from_env[formula_env_name] end + sig { void } + def prepend_pkgconf_path_if_needed!; end + sig { void } def reset! @mas_installed = T.let(nil, T.nilable(T::Boolean)) diff --git a/Library/Homebrew/bundle/commands/exec.rb b/Library/Homebrew/bundle/commands/exec.rb index d7babf8afc..588de100e2 100644 --- a/Library/Homebrew/bundle/commands/exec.rb +++ b/Library/Homebrew/bundle/commands/exec.rb @@ -64,14 +64,8 @@ module Homebrew ENV.prepend_path "PATH", Pathname.new(dep_root)/"shims" end - # Setup pkg-config, if present, to help locate packages - # Only need this on Linux as Homebrew provides a shim on macOS - # 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 + # Setup pkgconf, if needed, to help locate packages + Bundle.prepend_pkgconf_path_if_needed! # For commands which aren't either absolute or relative # Add the command directory to PATH, since it may get blown away by superenv diff --git a/Library/Homebrew/bundle/skipper.rb b/Library/Homebrew/bundle/skipper.rb index f2788907a2..8c49f042b3 100644 --- a/Library/Homebrew/bundle/skipper.rb +++ b/Library/Homebrew/bundle/skipper.rb @@ -11,19 +11,6 @@ module Homebrew def skip?(entry, silent: false) 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| prefix = "#{tap}/" entry.name.start_with?(prefix) || entry.options[:full_name]&.start_with?(prefix) diff --git a/Library/Homebrew/extend/cachable.rb b/Library/Homebrew/cachable.rb similarity index 100% rename from Library/Homebrew/extend/cachable.rb rename to Library/Homebrew/cachable.rb diff --git a/Library/Homebrew/cask/cask.rb b/Library/Homebrew/cask/cask.rb index ec26010e90..b59b876072 100644 --- a/Library/Homebrew/cask/cask.rb +++ b/Library/Homebrew/cask/cask.rb @@ -8,7 +8,7 @@ require "cask/dsl" require "cask/metadata" require "cask/tab" require "utils/bottles" -require "extend/api_hashable" +require "api_hashable" module Cask # An instance of a cask. diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index ca35eebe39..2bd371b6e2 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -26,7 +26,7 @@ require "cask/dsl/version" require "cask/url" require "cask/utils" -require "extend/on_system" +require "on_system" module Cask # Class representing the domain-specific language used for casks. diff --git a/Library/Homebrew/cask/dsl/base.rb b/Library/Homebrew/cask/dsl/base.rb index 900889a59e..e9932dae84 100644 --- a/Library/Homebrew/cask/dsl/base.rb +++ b/Library/Homebrew/cask/dsl/base.rb @@ -2,7 +2,7 @@ # frozen_string_literal: true require "cask/utils" -require "extend/on_system" +require "on_system" module Cask class DSL diff --git a/Library/Homebrew/dependency_collector.rb b/Library/Homebrew/dependency_collector.rb index 6420504515..cddae5fcdc 100644 --- a/Library/Homebrew/dependency_collector.rb +++ b/Library/Homebrew/dependency_collector.rb @@ -5,7 +5,7 @@ require "dependency" require "dependencies" require "requirement" require "requirements" -require "extend/cachable" +require "cachable" # A dependency is a formula that another formula needs to install. # A requirement is something other than a formula that another formula diff --git a/Library/Homebrew/dev-cmd/bottle.rb b/Library/Homebrew/dev-cmd/bottle.rb index 04307077bc..cb69bb464c 100644 --- a/Library/Homebrew/dev-cmd/bottle.rb +++ b/Library/Homebrew/dev-cmd/bottle.rb @@ -227,7 +227,7 @@ module Homebrew private 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) } 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 end - sig { params(formula: T.untyped).returns(T::Array[T.untyped]) } + sig { params(formula: Formula).returns(T::Array[Regexp]) } 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. any_go_deps = formula.deps.any? do |dep| Version.formula_optionally_versioned_regex(:go).match?(dep.name) end - if any_go_deps - go_regex = Version.formula_optionally_versioned_regex(:go, full: false) - ignores << %r{#{cellar_regex}/#{go_regex}/[\d.]+/libexec} - end + return [] unless any_go_deps - # 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 + cellar_regex = Regexp.escape(HOMEBREW_CELLAR) + go_regex = Version.formula_optionally_versioned_regex(:go, full: false) + Array(%r{#{cellar_regex}/#{go_regex}/[\d.]+/libexec}) end sig { params(formula: Formula).void } diff --git a/Library/Homebrew/dev-cmd/tests.rb b/Library/Homebrew/dev-cmd/tests.rb index c14a44b92b..450098d268 100644 --- a/Library/Homebrew/dev-cmd/tests.rb +++ b/Library/Homebrew/dev-cmd/tests.rb @@ -120,31 +120,14 @@ module Homebrew ] bundle_args << "--fail-fast" if args.fail_fast? 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_intel" unless Hardware::CPU.intel? - bundle_args << "--tag" << "~needs_network" unless args.online? - bundle_args << "--tag" << "~needs_ci" unless ENV["CI"] + bundle_args = os_bundle_args(bundle_args) + files = os_files(files) + puts "Randomized with seed #{seed}" ENV["HOMEBREW_DEBUG"] = "1" if args.debug? # Used in spec_helper.rb to require the "debug" gem. @@ -171,6 +154,41 @@ module Homebrew 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]) } def changed_test_files changed_files = Utils.popen_read("git", "diff", "--name-only", "master") @@ -250,3 +268,5 @@ module Homebrew end end end + +require "extend/os/dev-cmd/tests" diff --git a/Library/Homebrew/extend/ENV.rb b/Library/Homebrew/extend/ENV.rb index b7a9afb644..4a449993f7 100644 --- a/Library/Homebrew/extend/ENV.rb +++ b/Library/Homebrew/extend/ENV.rb @@ -7,16 +7,6 @@ require "extend/ENV/shared" require "extend/ENV/std" 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 - # # @!parse # # `ENV` is not actually a class, but this makes YARD happy diff --git a/Library/Homebrew/extend/kernel.rb b/Library/Homebrew/extend/kernel.rb index abc1aaff79..948c0b3fba 100644 --- a/Library/Homebrew/extend/kernel.rb +++ b/Library/Homebrew/extend/kernel.rb @@ -5,6 +5,14 @@ # TODO: move these out of `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) return false if path.nil? diff --git a/Library/Homebrew/extend/os/dev-cmd/tests.rb b/Library/Homebrew/extend/os/dev-cmd/tests.rb new file mode 100644 index 0000000000..556052a63f --- /dev/null +++ b/Library/Homebrew/extend/os/dev-cmd/tests.rb @@ -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? diff --git a/Library/Homebrew/extend/os/linux/bundle/bundle.rb b/Library/Homebrew/extend/os/linux/bundle/bundle.rb index 71c2e41625..1acbb70541 100644 --- a/Library/Homebrew/extend/os/linux/bundle/bundle.rb +++ b/Library/Homebrew/extend/os/linux/bundle/bundle.rb @@ -9,6 +9,16 @@ module OS def mas_installed? false 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 diff --git a/Library/Homebrew/extend/os/linux/dev-cmd/tests.rb b/Library/Homebrew/extend/os/linux/dev-cmd/tests.rb new file mode 100644 index 0000000000..e7f3d30d65 --- /dev/null +++ b/Library/Homebrew/extend/os/linux/dev-cmd/tests.rb @@ -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) diff --git a/Library/Homebrew/extend/os/mac/dev-cmd/bottle.rb b/Library/Homebrew/extend/os/mac/dev-cmd/bottle.rb index 9d07b43695..e75eeab739 100644 --- a/Library/Homebrew/extend/os/mac/dev-cmd/bottle.rb +++ b/Library/Homebrew/extend/os/mac/dev-cmd/bottle.rb @@ -18,6 +18,26 @@ module OS def gnu_tar(gnu_tar_formula) "#{gnu_tar_formula.opt_bin}/gtar" 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 diff --git a/Library/Homebrew/extend/os/mac/dev-cmd/tests.rb b/Library/Homebrew/extend/os/mac/dev-cmd/tests.rb new file mode 100644 index 0000000000..112ad3b6c8 --- /dev/null +++ b/Library/Homebrew/extend/os/mac/dev-cmd/tests.rb @@ -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) diff --git a/Library/Homebrew/extend/os/mac/diagnostic.rb b/Library/Homebrew/extend/os/mac/diagnostic.rb index 311b68167e..c3d92eb374 100644 --- a/Library/Homebrew/extend/os/mac/diagnostic.rb +++ b/Library/Homebrew/extend/os/mac/diagnostic.rb @@ -425,8 +425,8 @@ module OS end def check_deprecated_caskroom_taps - tapped_caskroom_taps = Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" } - .map(&:name) + tapped_caskroom_taps = ::Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" } + .map(&:name) return if tapped_caskroom_taps.empty? <<~EOS diff --git a/Library/Homebrew/extend/os/mac/readall.rb b/Library/Homebrew/extend/os/mac/readall.rb index 23f71e403c..90e89c17d5 100644 --- a/Library/Homebrew/extend/os/mac/readall.rb +++ b/Library/Homebrew/extend/os/mac/readall.rb @@ -8,7 +8,7 @@ module OS 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) return true if os_name == :linux diff --git a/Library/Homebrew/extend/os/mac/tap.rb b/Library/Homebrew/extend/os/mac/tap.rb new file mode 100644 index 0000000000..2213a4f757 --- /dev/null +++ b/Library/Homebrew/extend/os/mac/tap.rb @@ -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) diff --git a/Library/Homebrew/extend/os/tap.rb b/Library/Homebrew/extend/os/tap.rb new file mode 100644 index 0000000000..e6662ff704 --- /dev/null +++ b/Library/Homebrew/extend/os/tap.rb @@ -0,0 +1,4 @@ +# typed: strict +# frozen_string_literal: true + +require "extend/os/mac/tap" if OS.mac? diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index 91337f81d1..2e28057a2c 100644 --- a/Library/Homebrew/extend/pathname.rb +++ b/Library/Homebrew/extend/pathname.rb @@ -1,79 +1,9 @@ # 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 - 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. # @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 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 diff --git a/Library/Homebrew/extend/pathname/disk_usage_extension.rb b/Library/Homebrew/extend/pathname/disk_usage_extension.rb new file mode 100644 index 0000000000..4781ebfeb5 --- /dev/null +++ b/Library/Homebrew/extend/pathname/disk_usage_extension.rb @@ -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 diff --git a/Library/Homebrew/extend/pathname/observer_pathname_extension.rb b/Library/Homebrew/extend/pathname/observer_pathname_extension.rb new file mode 100644 index 0000000000..2ef2035cb7 --- /dev/null +++ b/Library/Homebrew/extend/pathname/observer_pathname_extension.rb @@ -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 diff --git a/Library/Homebrew/extend/time.rb b/Library/Homebrew/extend/time.rb index 75a79b864b..2d266db5ff 100644 --- a/Library/Homebrew/extend/time.rb +++ b/Library/Homebrew/extend/time.rb @@ -5,5 +5,9 @@ require "time" class Time # 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 diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 740e258a32..bf570f6c1e 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -38,9 +38,9 @@ require "tab" require "mktemp" require "find" require "utils/spdx" -require "extend/on_system" +require "on_system" require "api" -require "extend/api_hashable" +require "api_hashable" # A formula provides instructions and metadata for Homebrew to install a piece # of software. Every Homebrew formula is a {Formula}. diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 2f191fb787..5a48b25d8b 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -2,7 +2,7 @@ # frozen_string_literal: true require "digest/sha2" -require "extend/cachable" +require "cachable" require "tab" require "utils" require "utils/bottles" diff --git a/Library/Homebrew/global.rb b/Library/Homebrew/global.rb index ee4f5fa916..ff9d0f79b3 100644 --- a/Library/Homebrew/global.rb +++ b/Library/Homebrew/global.rb @@ -138,7 +138,7 @@ require "extend/kernel" require "os" require "extend/array" -require "extend/cachable" +require "cachable" require "extend/enumerable" require "extend/string" require "extend/pathname" diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index 40f96d48ba..275cc8fed0 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -4,7 +4,7 @@ require "keg_relocate" require "language/python" require "lock_file" -require "extend/cachable" +require "cachable" # Installation prefix of a formula. class Keg diff --git a/Library/Homebrew/extend/on_system.rb b/Library/Homebrew/on_system.rb similarity index 100% rename from Library/Homebrew/extend/on_system.rb rename to Library/Homebrew/on_system.rb diff --git a/Library/Homebrew/extend/on_system.rbi b/Library/Homebrew/on_system.rbi similarity index 100% rename from Library/Homebrew/extend/on_system.rbi rename to Library/Homebrew/on_system.rbi diff --git a/Library/Homebrew/extend/io.rb b/Library/Homebrew/readline_nonblock.rb similarity index 60% rename from Library/Homebrew/extend/io.rb rename to Library/Homebrew/readline_nonblock.rb index a6eb7d4b70..7888b46399 100644 --- a/Library/Homebrew/extend/io.rb +++ b/Library/Homebrew/readline_nonblock.rb @@ -1,17 +1,17 @@ # typed: strict # frozen_string_literal: true -class IO - sig { params(sep: String).returns(String) } - def readline_nonblock(sep = $INPUT_RECORD_SEPARATOR) +class ReadlineNonblock + sig { params(io: IO).returns(String) } + def self.read(io) line = +"" buffer = +"" begin loop do - break if buffer == sep + break if buffer == $INPUT_RECORD_SEPARATOR - read_nonblock(1, buffer) + io.read_nonblock(1, buffer) line.concat(buffer) end diff --git a/Library/Homebrew/resource.rb b/Library/Homebrew/resource.rb index 2b37c2ec43..a774f659df 100644 --- a/Library/Homebrew/resource.rb +++ b/Library/Homebrew/resource.rb @@ -4,7 +4,7 @@ require "downloadable" require "mktemp" require "livecheck" -require "extend/on_system" +require "on_system" # Resource is the fundamental representation of an external resource. The # primary formula download, along with other declared resources, are instances diff --git a/Library/Homebrew/sbom.rb b/Library/Homebrew/sbom.rb index 8873cdf751..4df1d7e815 100644 --- a/Library/Homebrew/sbom.rb +++ b/Library/Homebrew/sbom.rb @@ -4,7 +4,7 @@ require "cxxstdlib" require "json" require "development_tools" -require "extend/cachable" +require "cachable" require "utils/curl" # Rather than calling `new` directly, use one of the class methods like {SBOM.create}. diff --git a/Library/Homebrew/service.rb b/Library/Homebrew/service.rb index 2ae807da14..b52c1bb083 100644 --- a/Library/Homebrew/service.rb +++ b/Library/Homebrew/service.rb @@ -2,7 +2,7 @@ # frozen_string_literal: true require "ipaddr" -require "extend/on_system" +require "on_system" require "utils/service" module Homebrew diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index 7463429257..a6172182cb 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -12,7 +12,7 @@ require "utils/bottles" require "patch" require "compilers" require "macos_version" -require "extend/on_system" +require "on_system" class SoftwareSpec include Downloadable diff --git a/Library/Homebrew/system_command.rb b/Library/Homebrew/system_command.rb index 737eea4322..b70e2d7583 100644 --- a/Library/Homebrew/system_command.rb +++ b/Library/Homebrew/system_command.rb @@ -6,7 +6,7 @@ require "shellwords" require "uri" require "context" -require "extend/io" +require "readline_nonblock" require "utils/timer" # Class for running sub-processes and capturing their output and exit status. @@ -361,7 +361,7 @@ class SystemCommand readable_sources.each do |source| loop do - line = source.readline_nonblock || "" + line = ReadlineNonblock.read(source) yield(sources.fetch(source), line) end rescue EOFError diff --git a/Library/Homebrew/tab.rb b/Library/Homebrew/tab.rb index 70f7118234..dc57bb4d20 100644 --- a/Library/Homebrew/tab.rb +++ b/Library/Homebrew/tab.rb @@ -5,7 +5,7 @@ require "cxxstdlib" require "options" require "json" require "development_tools" -require "extend/cachable" +require "cachable" # Rather than calling `new` directly, use one of the class methods like {Tab.create}. class AbstractTab diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index bf83e7ee29..545d91cd76 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -1079,15 +1079,12 @@ class Tap # All locally installed and core taps. Core taps might not be installed locally when using the API. sig { returns(T::Array[Tap]) } def self.all - cache[:all] ||= begin - 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 + cache[:all] ||= installed | core_taps + end - installed | core_taps - end + sig { returns(T::Array[Tap]) } + def self.core_taps + [CoreTap.instance].freeze end # Enumerate all available {Tap}s. @@ -1517,3 +1514,5 @@ class TapConfig Homebrew::Settings.delete key, repo: tap.path end end + +require "extend/os/tap" diff --git a/Library/Homebrew/utils/analytics.rb b/Library/Homebrew/utils/analytics.rb index 54674b019f..003290f824 100644 --- a/Library/Homebrew/utils/analytics.rb +++ b/Library/Homebrew/utils/analytics.rb @@ -4,7 +4,7 @@ require "context" require "erb" require "settings" -require "extend/cachable" +require "cachable" module Utils # Helper module for fetching and reporting analytics data. diff --git a/docs/Typechecking.md b/docs/Typechecking.md index 216b79f396..ea291945a8 100644 --- a/docs/Typechecking.md +++ b/docs/Typechecking.md @@ -34,7 +34,7 @@ For more information on how to express more complex types, refer to the official ### 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.