From 0b56d0be4aea7b495a8b9908353eda0b0f89c549 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 28 Apr 2024 03:23:21 +0200 Subject: [PATCH] Document `Tab.for_keg` and use `Keg#tab` where possible. --- Library/Homebrew/cleanup.rb | 2 +- Library/Homebrew/cli/named_args.rb | 4 +-- Library/Homebrew/cmd/info.rb | 6 ++-- Library/Homebrew/cmd/leaves.rb | 4 +-- Library/Homebrew/cmd/update-report.rb | 4 +-- Library/Homebrew/dev-cmd/bottle.rb | 2 +- Library/Homebrew/diagnostic.rb | 2 +- .../extend/os/linux/cmd/update-report.rb | 2 +- .../extend/os/mac/formula_cellar_checks.rb | 2 +- Library/Homebrew/formula.rb | 28 +++++++++++-------- Library/Homebrew/formula_installer.rb | 8 +++--- Library/Homebrew/formulary.rb | 2 +- Library/Homebrew/install.rb | 2 +- Library/Homebrew/keg.rb | 2 ++ Library/Homebrew/linkage_checker.rb | 4 +-- Library/Homebrew/migrator.rb | 4 +-- Library/Homebrew/reinstall.rb | 2 +- Library/Homebrew/tab.rb | 4 +++ .../test/formula_installer_bottle_spec.rb | 2 +- .../Homebrew/test/formula_installer_spec.rb | 2 +- .../test/installed_dependents_spec.rb | 2 +- .../Homebrew/test/utils/autoremove_spec.rb | 27 ++++++++++++++---- Library/Homebrew/upgrade.rb | 2 +- Library/Homebrew/utils/autoremove.rb | 4 +-- Library/Homebrew/utils/bottles.rb | 4 +-- 25 files changed, 77 insertions(+), 50 deletions(-) diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index c5b2a437d7..ae44d69f9a 100644 --- a/Library/Homebrew/cleanup.rb +++ b/Library/Homebrew/cleanup.rb @@ -703,7 +703,7 @@ module Homebrew require "uninstall" - kegs_by_rack = removable_formulae.map(&:any_installed_keg).group_by(&:rack) + kegs_by_rack = removable_formulae.filter_map(&:any_installed_keg).group_by(&:rack) Uninstall.uninstall_kegs(kegs_by_rack) # The installed formula cache will be invalid after uninstalling. diff --git a/Library/Homebrew/cli/named_args.rb b/Library/Homebrew/cli/named_args.rb index 5bb1e5441f..834e073aa3 100644 --- a/Library/Homebrew/cli/named_args.rb +++ b/Library/Homebrew/cli/named_args.rb @@ -371,11 +371,11 @@ module Homebrew # Return keg if it is the only installed keg return kegs if kegs.length == 1 - stable_kegs = kegs.reject { |k| k.version.head? } + stable_kegs = kegs.reject { |keg| keg.version.head? } if stable_kegs.blank? return kegs.max_by do |keg| - [Tab.for_keg(keg).source_modified_time, keg.version.revision] + [keg.tab.source_modified_time, keg.version.revision] end end diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb index 336f8e5978..a9f864b3a2 100644 --- a/Library/Homebrew/cmd/info.rb +++ b/Library/Homebrew/cmd/info.rb @@ -293,9 +293,9 @@ module Homebrew end kegs = formula.installed_kegs - heads, versioned = kegs.partition { |k| k.version.head? } + heads, versioned = kegs.partition { |keg| keg.version.head? } kegs = [ - *heads.sort_by { |k| -Tab.for_keg(k).time.to_i }, + *heads.sort_by { |keg| -keg.tab.time.to_i }, *versioned.sort_by(&:scheme_and_version), ] if kegs.empty? @@ -304,7 +304,7 @@ module Homebrew puts "Installed" kegs.each do |keg| puts "#{keg} (#{keg.abv})#{" *" if keg.linked?}" - tab = Tab.for_keg(keg).to_s + tab = keg.tab.to_s puts " #{tab}" unless tab.empty? end end diff --git a/Library/Homebrew/cmd/leaves.rb b/Library/Homebrew/cmd/leaves.rb index 4534a678fb..d7f1292e52 100644 --- a/Library/Homebrew/cmd/leaves.rb +++ b/Library/Homebrew/cmd/leaves.rb @@ -40,11 +40,11 @@ module Homebrew private def installed_on_request?(formula) - Tab.for_keg(formula.any_installed_keg).installed_on_request + formula.any_installed_keg.tab.installed_on_request end def installed_as_dependency?(formula) - Tab.for_keg(formula.any_installed_keg).installed_as_dependency + formula.any_installed_keg.installed_as_dependency end end end diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index 7113aad483..f81fc26603 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -162,7 +162,7 @@ module Homebrew next unless formula.any_version_installed? keg = formula.installed_kegs.last - tab = Tab.for_keg(keg) + tab = keg.tab # force a `brew upgrade` from the linuxbrew-core version to the homebrew-core version (even if lower) tab.source["versions"]["version_scheme"] = -1 tab.write @@ -631,7 +631,7 @@ class Reporter next unless (dir = HOMEBREW_CELLAR/name).exist? # skip if formula is not installed. - tabs = dir.subdirs.map { |d| Tab.for_keg(Keg.new(d)) } + tabs = dir.subdirs.map { |d| Keg.new(d).tab } next if tabs.first.tap != tap # skip if installed formula is not from this tap. new_tap = Tap.fetch(new_tap_name) diff --git a/Library/Homebrew/dev-cmd/bottle.rb b/Library/Homebrew/dev-cmd/bottle.rb index 1d748ec507..c4338e4743 100644 --- a/Library/Homebrew/dev-cmd/bottle.rb +++ b/Library/Homebrew/dev-cmd/bottle.rb @@ -491,7 +491,7 @@ module Homebrew Tab.clear_cache Dependency.clear_cache Requirement.clear_cache - tab = Tab.for_keg(keg) + tab = keg.tab original_tab = tab.dup tab.poured_from_bottle = false tab.time = nil diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb index a14d89d501..c764977dd8 100644 --- a/Library/Homebrew/diagnostic.rb +++ b/Library/Homebrew/diagnostic.rb @@ -832,7 +832,7 @@ module Homebrew kegs = Keg.all deleted_formulae = kegs.filter_map do |keg| - tap = Tab.for_keg(keg).tap + tap = keg.tab.tap tap_keg_name = tap ? "#{tap}/#{keg.name}" : keg.name loadable = [ diff --git a/Library/Homebrew/extend/os/linux/cmd/update-report.rb b/Library/Homebrew/extend/os/linux/cmd/update-report.rb index fd0ad1aeeb..2d448f0ec1 100644 --- a/Library/Homebrew/extend/os/linux/cmd/update-report.rb +++ b/Library/Homebrew/extend/os/linux/cmd/update-report.rb @@ -23,7 +23,7 @@ module Homebrew next unless recursive_runtime_dependencies.map(&:name).include? "gcc" keg = formula.installed_kegs.last - tab = Tab.for_keg(keg) + tab = keg.tab # Force reinstallation upon `brew upgrade` to fix the bottle RPATH. tab.source["versions"]["version_scheme"] = -1 tab.write diff --git a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb index fbd5b84252..e4ffde8f9f 100644 --- a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb +++ b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb @@ -80,7 +80,7 @@ module FormulaCellarChecks #{checker.display_test_output} EOS - tab = Tab.for_keg(keg) + tab = keg.tab if tab.poured_from_bottle output += <<~EOS Rebuild this from source with: diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 24cc6f75a2..55d0ba5584 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -657,7 +657,7 @@ class Formula end head_versions.max_by do |pn_pkgversion| - [Tab.for_keg(prefix(pn_pkgversion)).source_modified_time, pn_pkgversion.revision] + [Keg.new(prefix(pn_pkgversion)).tab.source_modified_time, pn_pkgversion.revision] end end @@ -751,6 +751,7 @@ class Formula end # All currently installed kegs. + sig { returns(T::Array[Keg]) } def installed_kegs installed_prefixes.map { |dir| Keg.new(dir) } end @@ -1408,7 +1409,7 @@ class Formula rescue NotAKegError, Errno::ENOENT # file doesn't belong to any keg. else - tab_tap = Tab.for_keg(keg).tap + tab_tap = keg.tab.tap # this keg doesn't below to any core/tap formula, most likely coming from a DIY install. return false if tab_tap.nil? @@ -1850,12 +1851,15 @@ class Formula # universal binaries in a {Formula}'s {Keg}. sig { params(targets: T.nilable(T.any(Pathname, String))).void } def deuniversalize_machos(*targets) - targets = nil if targets.blank? - targets ||= any_installed_keg.mach_o_files.select do |file| - file.arch == :universal && file.archs.include?(Hardware::CPU.arch) + if targets.none? + targets = any_installed_keg&.mach_o_files&.select do |file| + file.arch == :universal && file.archs.include?(Hardware::CPU.arch) + end end - targets.each { |t| extract_macho_slice_from(Pathname.new(t), Hardware::CPU.arch) } + targets&.each do |target| + extract_macho_slice_from(Pathname(target), Hardware::CPU.arch) + end end sig { params(file: Pathname, arch: T.nilable(Symbol)).void } @@ -2147,6 +2151,7 @@ class Formula # Returns a Keg for the opt_prefix or installed_prefix if they exist. # If not, return `nil`. + sig { returns(T.nilable(Keg)) } def any_installed_keg Formula.cache[:any_installed_keg] ||= {} Formula.cache[:any_installed_keg][full_name] ||= if (installed_prefix = any_installed_prefix) @@ -2328,7 +2333,7 @@ class Formula hsh.merge!(dependencies_hash) hsh["installed"] = installed_kegs.sort_by(&:scheme_and_version).map do |keg| - tab = Tab.for_keg keg + tab = keg.tab { "version" => keg.version.to_s, "used_options" => tab.used_options.as_flags, @@ -2882,12 +2887,13 @@ class Formula eligible_for_cleanup = [] if latest_version_installed? eligible_kegs = if head? && (head_prefix = latest_head_prefix) - head, stable = installed_kegs.partition { |k| k.version.head? } - # Remove newest head and stable kegs - head - [Keg.new(head_prefix)] + stable.sort_by(&:scheme_and_version).slice(0...-1) + head, stable = installed_kegs.partition { |keg| keg.version.head? } + + # Remove newest head and stable kegs. + head - [Keg.new(head_prefix)] + T.must(stable.sort_by(&:scheme_and_version).slice(0...-1)) else installed_kegs.select do |keg| - tab = Tab.for_keg(keg) + tab = keg.tab if version_scheme > tab.version_scheme true elsif version_scheme == tab.version_scheme diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 9e76fe79e7..e038dd8f50 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -471,7 +471,7 @@ on_request: installed_on_request?, options:) Pathname(brew_prefix/"#{formula.name}.rb").atomic_write(s) keg = Keg.new(formula.prefix) - tab = Tab.for_keg(keg) + tab = keg.tab tab.installed_as_dependency = installed_as_dependency? tab.installed_on_request = installed_on_request? tab.write @@ -707,7 +707,7 @@ on_request: installed_on_request?, options:) if df.linked_keg.directory? linked_keg = Keg.new(df.linked_keg.resolved_path) - tab = Tab.for_keg(linked_keg) + tab = linked_keg.tab keg_had_linked_keg = true keg_was_linked = linked_keg.linked? linked_keg.unlink @@ -715,7 +715,7 @@ on_request: installed_on_request?, options:) if df.latest_version_installed? installed_keg = Keg.new(df.prefix) - tab ||= Tab.for_keg(installed_keg) + tab ||= installed_keg.tab tmp_keg = Pathname.new("#{installed_keg}.tmp") installed_keg.rename(tmp_keg) end @@ -822,7 +822,7 @@ on_request: installed_on_request?, options:) end # Update tab with actual runtime dependencies - tab = Tab.for_keg(keg) + tab = keg.tab Tab.clear_cache f_runtime_deps = formula.runtime_dependencies(read_from_tab: false) tab.runtime_dependencies = Tab.runtime_deps_hash(formula, f_runtime_deps) diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 53df1d8374..6e2946500c 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -1038,7 +1038,7 @@ module Formulary force_bottle: T.unsafe(nil), flags: T.unsafe(nil) ) - tab = Tab.for_keg(keg) + tab = keg.tab tap = tab.tap spec ||= tab.spec diff --git a/Library/Homebrew/install.rb b/Library/Homebrew/install.rb index 1f194c10c1..2e8293785d 100644 --- a/Library/Homebrew/install.rb +++ b/Library/Homebrew/install.rb @@ -211,7 +211,7 @@ module Homebrew return false unless formula.opt_prefix.directory? keg = Keg.new(formula.opt_prefix.resolved_path) - tab = Tab.for_keg(keg) + tab = keg.tab unless tab.installed_on_request tab.installed_on_request = true tab.write diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index f26c18dc19..f0b8f79623 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -158,6 +158,7 @@ class Keg :to_path, :hash, :abv, :disk_usage, :file_count, :directory?, :exist?, :/, :join, :rename, :find + sig { params(path: Pathname).void } def initialize(path) path = path.resolved_path if path.to_s.start_with?("#{HOMEBREW_PREFIX}/opt/") raise "#{path} is not a valid keg" if path.parent.parent.realpath != HOMEBREW_CELLAR.realpath @@ -508,6 +509,7 @@ class Keg end end + sig { returns(Tab) } def tab Tab.for_keg(self) end diff --git a/Library/Homebrew/linkage_checker.rb b/Library/Homebrew/linkage_checker.rb index dec594f987..16975e7581 100644 --- a/Library/Homebrew/linkage_checker.rb +++ b/Library/Homebrew/linkage_checker.rb @@ -143,7 +143,7 @@ class LinkageChecker end begin - owner = Keg.for Pathname.new(dylib) + owner = Keg.for(Pathname(dylib)) rescue NotAKegError @system_dylibs << dylib rescue Errno::ENOENT @@ -159,7 +159,7 @@ class LinkageChecker @broken_dylibs << dylib end else - tap = Tab.for_keg(owner).tap + tap = owner.tab.tap f = if tap.nil? || tap.core_tap? owner.name else diff --git a/Library/Homebrew/migrator.rb b/Library/Homebrew/migrator.rb index 9c6c0cb06c..a5465581b3 100644 --- a/Library/Homebrew/migrator.rb +++ b/Library/Homebrew/migrator.rb @@ -128,7 +128,7 @@ class Migrator @old_cellar = HOMEBREW_CELLAR/oldname raise MigratorNoOldpathError, oldname unless old_cellar.exist? - @old_tabs = old_cellar.subdirs.map { |d| Tab.for_keg(Keg.new(d)) } + @old_tabs = old_cellar.subdirs.map { |d| Keg.new(d).tab } @old_tap = old_tabs.first.tap raise MigratorDifferentTapsError.new(formula, oldname, old_tap) if !force && !from_same_tap_user? @@ -373,7 +373,7 @@ class Migrator # After migration every `INSTALL_RECEIPT.json` has the wrong path to the formula # so we must update `INSTALL_RECEIPT`s. def update_tabs - new_tabs = new_cellar.subdirs.map { |d| Tab.for_keg(Keg.new(d)) } + new_tabs = new_cellar.subdirs.map { |d| Keg.new(d).tab } new_tabs.each do |tab| tab.source["path"] = formula.path.to_s if tab.source["path"] tab.write diff --git a/Library/Homebrew/reinstall.rb b/Library/Homebrew/reinstall.rb index 82a79f1d5c..39ad82d292 100644 --- a/Library/Homebrew/reinstall.rb +++ b/Library/Homebrew/reinstall.rb @@ -25,7 +25,7 @@ module Homebrew ) if formula.opt_prefix.directory? keg = Keg.new(formula.opt_prefix.resolved_path) - tab = Tab.for_keg(keg) + tab = keg.tab keg_had_linked_opt = true keg_was_linked = keg.linked? backup keg diff --git a/Library/Homebrew/tab.rb b/Library/Homebrew/tab.rb index 5de9d00778..f55da44815 100644 --- a/Library/Homebrew/tab.rb +++ b/Library/Homebrew/tab.rb @@ -128,7 +128,11 @@ class Tab new(attributes) end + # Get the {Tab} for the given {Keg}, + # or a fake one if the formula is not installed. + # # @api internal + sig { params(keg: T.any(Keg, Pathname)).returns(T.attached_class) } def self.for_keg(keg) path = keg/FILENAME diff --git a/Library/Homebrew/test/formula_installer_bottle_spec.rb b/Library/Homebrew/test/formula_installer_bottle_spec.rb index e2197a7a43..0f4e4eee6a 100644 --- a/Library/Homebrew/test/formula_installer_bottle_spec.rb +++ b/Library/Homebrew/test/formula_installer_bottle_spec.rb @@ -34,7 +34,7 @@ RSpec.describe FormulaInstaller do expect(formula).to be_latest_version_installed begin - expect(Tab.for_keg(keg)).to be_poured_from_bottle + expect(keg.tab).to be_poured_from_bottle yield formula ensure diff --git a/Library/Homebrew/test/formula_installer_spec.rb b/Library/Homebrew/test/formula_installer_spec.rb index b09c85e7cf..e8be7f7cbb 100644 --- a/Library/Homebrew/test/formula_installer_spec.rb +++ b/Library/Homebrew/test/formula_installer_spec.rb @@ -30,7 +30,7 @@ RSpec.describe FormulaInstaller do begin Tab.clear_cache - expect(Tab.for_keg(keg)).not_to be_poured_from_bottle + expect(keg.tab).not_to be_poured_from_bottle yield formula if block_given? ensure diff --git a/Library/Homebrew/test/installed_dependents_spec.rb b/Library/Homebrew/test/installed_dependents_spec.rb index 5df0b532c1..4bcbd383c4 100644 --- a/Library/Homebrew/test/installed_dependents_spec.rb +++ b/Library/Homebrew/test/installed_dependents_spec.rb @@ -51,7 +51,7 @@ RSpec.describe InstalledDependents do end def alter_tab(keg) - tab = Tab.for_keg(keg) + tab = keg.tab yield tab tab.write end diff --git a/Library/Homebrew/test/utils/autoremove_spec.rb b/Library/Homebrew/test/utils/autoremove_spec.rb index 0bdbcbe2ce..da67a09c0d 100644 --- a/Library/Homebrew/test/utils/autoremove_spec.rb +++ b/Library/Homebrew/test/utils/autoremove_spec.rb @@ -39,14 +39,25 @@ RSpec.describe Utils::Autoremove do ] end - let(:tab_from_keg) { double } + let(:tab_from_keg) { instance_double(Tab) } before do - allow(formula_with_deps).to receive(:runtime_formula_dependencies).and_return([first_formula_dep, - second_formula_dep]) - allow(first_formula_dep).to receive(:runtime_formula_dependencies).and_return([second_formula_dep]) - - allow(Tab).to receive(:for_keg).and_return(tab_from_keg) + allow(formula_with_deps).to receive_messages( + runtime_formula_dependencies: [first_formula_dep, second_formula_dep], + any_installed_keg: instance_double(Keg, tab: tab_from_keg), + ) + allow(first_formula_dep).to receive_messages( + runtime_formula_dependencies: [second_formula_dep], + any_installed_keg: instance_double(Keg, tab: tab_from_keg), + ) + allow(second_formula_dep).to receive_messages( + runtime_formula_dependencies: [], + any_installed_keg: instance_double(Keg, tab: tab_from_keg), + ) + allow(formula_is_build_dep).to receive_messages( + runtime_formula_dependencies: [], + any_installed_keg: instance_double(Keg, tab: tab_from_keg), + ) end end @@ -60,6 +71,7 @@ RSpec.describe Utils::Autoremove do context "when formulae are bottles" do it "filters out runtime dependencies" do allow(tab_from_keg).to receive(:poured_from_bottle).and_return(true) + expect(described_class.send(:formulae_with_no_formula_dependents, formulae)) .to eq([formula_with_deps, formula_is_build_dep]) end @@ -68,6 +80,7 @@ RSpec.describe Utils::Autoremove do context "when formulae are built from source" do it "filters out runtime and build dependencies" do allow(tab_from_keg).to receive(:poured_from_bottle).and_return(false) + expect(described_class.send(:formulae_with_no_formula_dependents, formulae)) .to eq([formula_with_deps]) end @@ -83,12 +96,14 @@ RSpec.describe Utils::Autoremove do specify "installed on request" do allow(tab_from_keg).to receive(:installed_on_request).and_return(true) + expect(described_class.send(:unused_formulae_with_no_formula_dependents, formulae)) .to eq([]) end specify "not installed on request" do allow(tab_from_keg).to receive(:installed_on_request).and_return(false) + expect(described_class.send(:unused_formulae_with_no_formula_dependents, formulae)) .to match_array(formulae) end diff --git a/Library/Homebrew/upgrade.rb b/Library/Homebrew/upgrade.rb index 068a3b66d2..142f8d7c2b 100644 --- a/Library/Homebrew/upgrade.rb +++ b/Library/Homebrew/upgrade.rb @@ -161,7 +161,7 @@ module Homebrew if formula.opt_prefix.directory? keg = Keg.new(formula.opt_prefix.resolved_path) - tab = Tab.for_keg(keg) + tab = keg.tab end build_options = BuildOptions.new(Options.create(flags), formula.options) diff --git a/Library/Homebrew/utils/autoremove.rb b/Library/Homebrew/utils/autoremove.rb index 8ee06034b1..5b14c8d505 100644 --- a/Library/Homebrew/utils/autoremove.rb +++ b/Library/Homebrew/utils/autoremove.rb @@ -40,7 +40,7 @@ module Utils dependents += formula.runtime_formula_dependencies # Ignore build dependencies when the formula is a bottle - next if Tab.for_keg(formula.any_installed_keg).poured_from_bottle + next if formula.any_installed_keg&.tab&.poured_from_bottle formula.deps.select(&:build?).each do |dep| dependents << dep.to_formula @@ -57,7 +57,7 @@ module Utils sig { params(formulae: T::Array[Formula]).returns(T::Array[Formula]) } def unused_formulae_with_no_formula_dependents(formulae) unused_formulae = formulae_with_no_formula_dependents(formulae).reject do |f| - Tab.for_keg(f.any_installed_keg).installed_on_request + f.any_installed_keg&.tab&.installed_on_request end unless unused_formulae.empty? diff --git a/Library/Homebrew/utils/bottles.rb b/Library/Homebrew/utils/bottles.rb index 09b16ac1ef..1518299a96 100644 --- a/Library/Homebrew/utils/bottles.rb +++ b/Library/Homebrew/utils/bottles.rb @@ -30,7 +30,7 @@ module Utils def built_as?(formula) return false unless formula.latest_version_installed? - tab = Tab.for_keg(formula.latest_installed_prefix) + tab = Keg.new(formula.latest_installed_prefix).tab tab.built_as_bottle end @@ -117,7 +117,7 @@ module Utils tab_json = bottle_hash[formula.full_name]["bottle"]["tags"][tag]["tab"].to_json Tab.from_file_content(tab_json, tabfile) else - tab = Tab.for_keg(keg) + tab = keg.tab tab.runtime_dependencies = begin f_runtime_deps = formula.runtime_dependencies(read_from_tab: false)