Document Tab.for_keg and use Keg#tab where possible.

This commit is contained in:
Markus Reiter 2024-04-28 03:23:21 +02:00
parent c26d10e4cb
commit 0b56d0be4a
No known key found for this signature in database
GPG Key ID: 245293B51702655B
25 changed files with 77 additions and 50 deletions

View File

@ -703,7 +703,7 @@ module Homebrew
require "uninstall" 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) Uninstall.uninstall_kegs(kegs_by_rack)
# The installed formula cache will be invalid after uninstalling. # The installed formula cache will be invalid after uninstalling.

View File

@ -371,11 +371,11 @@ module Homebrew
# Return keg if it is the only installed keg # Return keg if it is the only installed keg
return kegs if kegs.length == 1 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? if stable_kegs.blank?
return kegs.max_by do |keg| 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
end end

View File

@ -293,9 +293,9 @@ module Homebrew
end end
kegs = formula.installed_kegs kegs = formula.installed_kegs
heads, versioned = kegs.partition { |k| k.version.head? } heads, versioned = kegs.partition { |keg| keg.version.head? }
kegs = [ 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), *versioned.sort_by(&:scheme_and_version),
] ]
if kegs.empty? if kegs.empty?
@ -304,7 +304,7 @@ module Homebrew
puts "Installed" puts "Installed"
kegs.each do |keg| kegs.each do |keg|
puts "#{keg} (#{keg.abv})#{" *" if keg.linked?}" puts "#{keg} (#{keg.abv})#{" *" if keg.linked?}"
tab = Tab.for_keg(keg).to_s tab = keg.tab.to_s
puts " #{tab}" unless tab.empty? puts " #{tab}" unless tab.empty?
end end
end end

View File

@ -40,11 +40,11 @@ module Homebrew
private private
def installed_on_request?(formula) def installed_on_request?(formula)
Tab.for_keg(formula.any_installed_keg).installed_on_request formula.any_installed_keg.tab.installed_on_request
end end
def installed_as_dependency?(formula) 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 end
end end

View File

@ -162,7 +162,7 @@ module Homebrew
next unless formula.any_version_installed? next unless formula.any_version_installed?
keg = formula.installed_kegs.last 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) # force a `brew upgrade` from the linuxbrew-core version to the homebrew-core version (even if lower)
tab.source["versions"]["version_scheme"] = -1 tab.source["versions"]["version_scheme"] = -1
tab.write tab.write
@ -631,7 +631,7 @@ class Reporter
next unless (dir = HOMEBREW_CELLAR/name).exist? # skip if formula is not installed. 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. next if tabs.first.tap != tap # skip if installed formula is not from this tap.
new_tap = Tap.fetch(new_tap_name) new_tap = Tap.fetch(new_tap_name)

View File

@ -491,7 +491,7 @@ module Homebrew
Tab.clear_cache Tab.clear_cache
Dependency.clear_cache Dependency.clear_cache
Requirement.clear_cache Requirement.clear_cache
tab = Tab.for_keg(keg) tab = keg.tab
original_tab = tab.dup original_tab = tab.dup
tab.poured_from_bottle = false tab.poured_from_bottle = false
tab.time = nil tab.time = nil

View File

@ -832,7 +832,7 @@ module Homebrew
kegs = Keg.all kegs = Keg.all
deleted_formulae = kegs.filter_map do |keg| 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 tap_keg_name = tap ? "#{tap}/#{keg.name}" : keg.name
loadable = [ loadable = [

View File

@ -23,7 +23,7 @@ module Homebrew
next unless recursive_runtime_dependencies.map(&:name).include? "gcc" next unless recursive_runtime_dependencies.map(&:name).include? "gcc"
keg = formula.installed_kegs.last keg = formula.installed_kegs.last
tab = Tab.for_keg(keg) tab = keg.tab
# Force reinstallation upon `brew upgrade` to fix the bottle RPATH. # Force reinstallation upon `brew upgrade` to fix the bottle RPATH.
tab.source["versions"]["version_scheme"] = -1 tab.source["versions"]["version_scheme"] = -1
tab.write tab.write

View File

@ -80,7 +80,7 @@ module FormulaCellarChecks
#{checker.display_test_output} #{checker.display_test_output}
EOS EOS
tab = Tab.for_keg(keg) tab = keg.tab
if tab.poured_from_bottle if tab.poured_from_bottle
output += <<~EOS output += <<~EOS
Rebuild this from source with: Rebuild this from source with:

View File

@ -657,7 +657,7 @@ class Formula
end end
head_versions.max_by do |pn_pkgversion| 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
end end
@ -751,6 +751,7 @@ class Formula
end end
# All currently installed kegs. # All currently installed kegs.
sig { returns(T::Array[Keg]) }
def installed_kegs def installed_kegs
installed_prefixes.map { |dir| Keg.new(dir) } installed_prefixes.map { |dir| Keg.new(dir) }
end end
@ -1408,7 +1409,7 @@ class Formula
rescue NotAKegError, Errno::ENOENT rescue NotAKegError, Errno::ENOENT
# file doesn't belong to any keg. # file doesn't belong to any keg.
else 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. # this keg doesn't below to any core/tap formula, most likely coming from a DIY install.
return false if tab_tap.nil? return false if tab_tap.nil?
@ -1850,12 +1851,15 @@ class Formula
# universal binaries in a {Formula}'s {Keg}. # universal binaries in a {Formula}'s {Keg}.
sig { params(targets: T.nilable(T.any(Pathname, String))).void } sig { params(targets: T.nilable(T.any(Pathname, String))).void }
def deuniversalize_machos(*targets) def deuniversalize_machos(*targets)
targets = nil if targets.blank? if targets.none?
targets ||= any_installed_keg.mach_o_files.select do |file| targets = any_installed_keg&.mach_o_files&.select do |file|
file.arch == :universal && file.archs.include?(Hardware::CPU.arch) file.arch == :universal && file.archs.include?(Hardware::CPU.arch)
end 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 end
sig { params(file: Pathname, arch: T.nilable(Symbol)).void } 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. # Returns a Keg for the opt_prefix or installed_prefix if they exist.
# If not, return `nil`. # If not, return `nil`.
sig { returns(T.nilable(Keg)) }
def any_installed_keg def any_installed_keg
Formula.cache[:any_installed_keg] ||= {} Formula.cache[:any_installed_keg] ||= {}
Formula.cache[:any_installed_keg][full_name] ||= if (installed_prefix = any_installed_prefix) Formula.cache[:any_installed_keg][full_name] ||= if (installed_prefix = any_installed_prefix)
@ -2328,7 +2333,7 @@ class Formula
hsh.merge!(dependencies_hash) hsh.merge!(dependencies_hash)
hsh["installed"] = installed_kegs.sort_by(&:scheme_and_version).map do |keg| 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, "version" => keg.version.to_s,
"used_options" => tab.used_options.as_flags, "used_options" => tab.used_options.as_flags,
@ -2882,12 +2887,13 @@ class Formula
eligible_for_cleanup = [] eligible_for_cleanup = []
if latest_version_installed? if latest_version_installed?
eligible_kegs = if head? && (head_prefix = latest_head_prefix) eligible_kegs = if head? && (head_prefix = latest_head_prefix)
head, stable = installed_kegs.partition { |k| k.version.head? } head, stable = installed_kegs.partition { |keg| keg.version.head? }
# Remove newest head and stable kegs
head - [Keg.new(head_prefix)] + stable.sort_by(&:scheme_and_version).slice(0...-1) # Remove newest head and stable kegs.
head - [Keg.new(head_prefix)] + T.must(stable.sort_by(&:scheme_and_version).slice(0...-1))
else else
installed_kegs.select do |keg| installed_kegs.select do |keg|
tab = Tab.for_keg(keg) tab = keg.tab
if version_scheme > tab.version_scheme if version_scheme > tab.version_scheme
true true
elsif version_scheme == tab.version_scheme elsif version_scheme == tab.version_scheme

View File

@ -471,7 +471,7 @@ on_request: installed_on_request?, options:)
Pathname(brew_prefix/"#{formula.name}.rb").atomic_write(s) Pathname(brew_prefix/"#{formula.name}.rb").atomic_write(s)
keg = Keg.new(formula.prefix) keg = Keg.new(formula.prefix)
tab = Tab.for_keg(keg) tab = keg.tab
tab.installed_as_dependency = installed_as_dependency? tab.installed_as_dependency = installed_as_dependency?
tab.installed_on_request = installed_on_request? tab.installed_on_request = installed_on_request?
tab.write tab.write
@ -707,7 +707,7 @@ on_request: installed_on_request?, options:)
if df.linked_keg.directory? if df.linked_keg.directory?
linked_keg = Keg.new(df.linked_keg.resolved_path) 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_had_linked_keg = true
keg_was_linked = linked_keg.linked? keg_was_linked = linked_keg.linked?
linked_keg.unlink linked_keg.unlink
@ -715,7 +715,7 @@ on_request: installed_on_request?, options:)
if df.latest_version_installed? if df.latest_version_installed?
installed_keg = Keg.new(df.prefix) installed_keg = Keg.new(df.prefix)
tab ||= Tab.for_keg(installed_keg) tab ||= installed_keg.tab
tmp_keg = Pathname.new("#{installed_keg}.tmp") tmp_keg = Pathname.new("#{installed_keg}.tmp")
installed_keg.rename(tmp_keg) installed_keg.rename(tmp_keg)
end end
@ -822,7 +822,7 @@ on_request: installed_on_request?, options:)
end end
# Update tab with actual runtime dependencies # Update tab with actual runtime dependencies
tab = Tab.for_keg(keg) tab = keg.tab
Tab.clear_cache Tab.clear_cache
f_runtime_deps = formula.runtime_dependencies(read_from_tab: false) f_runtime_deps = formula.runtime_dependencies(read_from_tab: false)
tab.runtime_dependencies = Tab.runtime_deps_hash(formula, f_runtime_deps) tab.runtime_dependencies = Tab.runtime_deps_hash(formula, f_runtime_deps)

View File

@ -1038,7 +1038,7 @@ module Formulary
force_bottle: T.unsafe(nil), force_bottle: T.unsafe(nil),
flags: T.unsafe(nil) flags: T.unsafe(nil)
) )
tab = Tab.for_keg(keg) tab = keg.tab
tap = tab.tap tap = tab.tap
spec ||= tab.spec spec ||= tab.spec

View File

@ -211,7 +211,7 @@ module Homebrew
return false unless formula.opt_prefix.directory? return false unless formula.opt_prefix.directory?
keg = Keg.new(formula.opt_prefix.resolved_path) keg = Keg.new(formula.opt_prefix.resolved_path)
tab = Tab.for_keg(keg) tab = keg.tab
unless tab.installed_on_request unless tab.installed_on_request
tab.installed_on_request = true tab.installed_on_request = true
tab.write tab.write

View File

@ -158,6 +158,7 @@ class Keg
:to_path, :hash, :abv, :disk_usage, :file_count, :directory?, :exist?, :/, :to_path, :hash, :abv, :disk_usage, :file_count, :directory?, :exist?, :/,
:join, :rename, :find :join, :rename, :find
sig { params(path: Pathname).void }
def initialize(path) def initialize(path)
path = path.resolved_path if path.to_s.start_with?("#{HOMEBREW_PREFIX}/opt/") 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 raise "#{path} is not a valid keg" if path.parent.parent.realpath != HOMEBREW_CELLAR.realpath
@ -508,6 +509,7 @@ class Keg
end end
end end
sig { returns(Tab) }
def tab def tab
Tab.for_keg(self) Tab.for_keg(self)
end end

View File

@ -143,7 +143,7 @@ class LinkageChecker
end end
begin begin
owner = Keg.for Pathname.new(dylib) owner = Keg.for(Pathname(dylib))
rescue NotAKegError rescue NotAKegError
@system_dylibs << dylib @system_dylibs << dylib
rescue Errno::ENOENT rescue Errno::ENOENT
@ -159,7 +159,7 @@ class LinkageChecker
@broken_dylibs << dylib @broken_dylibs << dylib
end end
else else
tap = Tab.for_keg(owner).tap tap = owner.tab.tap
f = if tap.nil? || tap.core_tap? f = if tap.nil? || tap.core_tap?
owner.name owner.name
else else

View File

@ -128,7 +128,7 @@ class Migrator
@old_cellar = HOMEBREW_CELLAR/oldname @old_cellar = HOMEBREW_CELLAR/oldname
raise MigratorNoOldpathError, oldname unless old_cellar.exist? 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 @old_tap = old_tabs.first.tap
raise MigratorDifferentTapsError.new(formula, oldname, old_tap) if !force && !from_same_tap_user? 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 # After migration every `INSTALL_RECEIPT.json` has the wrong path to the formula
# so we must update `INSTALL_RECEIPT`s. # so we must update `INSTALL_RECEIPT`s.
def update_tabs 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| new_tabs.each do |tab|
tab.source["path"] = formula.path.to_s if tab.source["path"] tab.source["path"] = formula.path.to_s if tab.source["path"]
tab.write tab.write

View File

@ -25,7 +25,7 @@ module Homebrew
) )
if formula.opt_prefix.directory? if formula.opt_prefix.directory?
keg = Keg.new(formula.opt_prefix.resolved_path) keg = Keg.new(formula.opt_prefix.resolved_path)
tab = Tab.for_keg(keg) tab = keg.tab
keg_had_linked_opt = true keg_had_linked_opt = true
keg_was_linked = keg.linked? keg_was_linked = keg.linked?
backup keg backup keg

View File

@ -128,7 +128,11 @@ class Tab
new(attributes) new(attributes)
end end
# Get the {Tab} for the given {Keg},
# or a fake one if the formula is not installed.
#
# @api internal # @api internal
sig { params(keg: T.any(Keg, Pathname)).returns(T.attached_class) }
def self.for_keg(keg) def self.for_keg(keg)
path = keg/FILENAME path = keg/FILENAME

View File

@ -34,7 +34,7 @@ RSpec.describe FormulaInstaller do
expect(formula).to be_latest_version_installed expect(formula).to be_latest_version_installed
begin begin
expect(Tab.for_keg(keg)).to be_poured_from_bottle expect(keg.tab).to be_poured_from_bottle
yield formula yield formula
ensure ensure

View File

@ -30,7 +30,7 @@ RSpec.describe FormulaInstaller do
begin begin
Tab.clear_cache 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? yield formula if block_given?
ensure ensure

View File

@ -51,7 +51,7 @@ RSpec.describe InstalledDependents do
end end
def alter_tab(keg) def alter_tab(keg)
tab = Tab.for_keg(keg) tab = keg.tab
yield tab yield tab
tab.write tab.write
end end

View File

@ -39,14 +39,25 @@ RSpec.describe Utils::Autoremove do
] ]
end end
let(:tab_from_keg) { double } let(:tab_from_keg) { instance_double(Tab) }
before do before do
allow(formula_with_deps).to receive(:runtime_formula_dependencies).and_return([first_formula_dep, allow(formula_with_deps).to receive_messages(
second_formula_dep]) runtime_formula_dependencies: [first_formula_dep, second_formula_dep],
allow(first_formula_dep).to receive(:runtime_formula_dependencies).and_return([second_formula_dep]) any_installed_keg: instance_double(Keg, tab: tab_from_keg),
)
allow(Tab).to receive(:for_keg).and_return(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
end end
@ -60,6 +71,7 @@ RSpec.describe Utils::Autoremove do
context "when formulae are bottles" do context "when formulae are bottles" do
it "filters out runtime dependencies" do it "filters out runtime dependencies" do
allow(tab_from_keg).to receive(:poured_from_bottle).and_return(true) allow(tab_from_keg).to receive(:poured_from_bottle).and_return(true)
expect(described_class.send(:formulae_with_no_formula_dependents, formulae)) expect(described_class.send(:formulae_with_no_formula_dependents, formulae))
.to eq([formula_with_deps, formula_is_build_dep]) .to eq([formula_with_deps, formula_is_build_dep])
end end
@ -68,6 +80,7 @@ RSpec.describe Utils::Autoremove do
context "when formulae are built from source" do context "when formulae are built from source" do
it "filters out runtime and build dependencies" do it "filters out runtime and build dependencies" do
allow(tab_from_keg).to receive(:poured_from_bottle).and_return(false) allow(tab_from_keg).to receive(:poured_from_bottle).and_return(false)
expect(described_class.send(:formulae_with_no_formula_dependents, formulae)) expect(described_class.send(:formulae_with_no_formula_dependents, formulae))
.to eq([formula_with_deps]) .to eq([formula_with_deps])
end end
@ -83,12 +96,14 @@ RSpec.describe Utils::Autoremove do
specify "installed on request" do specify "installed on request" do
allow(tab_from_keg).to receive(:installed_on_request).and_return(true) allow(tab_from_keg).to receive(:installed_on_request).and_return(true)
expect(described_class.send(:unused_formulae_with_no_formula_dependents, formulae)) expect(described_class.send(:unused_formulae_with_no_formula_dependents, formulae))
.to eq([]) .to eq([])
end end
specify "not installed on request" do specify "not installed on request" do
allow(tab_from_keg).to receive(:installed_on_request).and_return(false) allow(tab_from_keg).to receive(:installed_on_request).and_return(false)
expect(described_class.send(:unused_formulae_with_no_formula_dependents, formulae)) expect(described_class.send(:unused_formulae_with_no_formula_dependents, formulae))
.to match_array(formulae) .to match_array(formulae)
end end

View File

@ -161,7 +161,7 @@ module Homebrew
if formula.opt_prefix.directory? if formula.opt_prefix.directory?
keg = Keg.new(formula.opt_prefix.resolved_path) keg = Keg.new(formula.opt_prefix.resolved_path)
tab = Tab.for_keg(keg) tab = keg.tab
end end
build_options = BuildOptions.new(Options.create(flags), formula.options) build_options = BuildOptions.new(Options.create(flags), formula.options)

View File

@ -40,7 +40,7 @@ module Utils
dependents += formula.runtime_formula_dependencies dependents += formula.runtime_formula_dependencies
# Ignore build dependencies when the formula is a bottle # 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| formula.deps.select(&:build?).each do |dep|
dependents << dep.to_formula dependents << dep.to_formula
@ -57,7 +57,7 @@ module Utils
sig { params(formulae: T::Array[Formula]).returns(T::Array[Formula]) } sig { params(formulae: T::Array[Formula]).returns(T::Array[Formula]) }
def unused_formulae_with_no_formula_dependents(formulae) def unused_formulae_with_no_formula_dependents(formulae)
unused_formulae = formulae_with_no_formula_dependents(formulae).reject do |f| 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 end
unless unused_formulae.empty? unless unused_formulae.empty?

View File

@ -30,7 +30,7 @@ module Utils
def built_as?(formula) def built_as?(formula)
return false unless formula.latest_version_installed? 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 tab.built_as_bottle
end end
@ -117,7 +117,7 @@ module Utils
tab_json = bottle_hash[formula.full_name]["bottle"]["tags"][tag]["tab"].to_json tab_json = bottle_hash[formula.full_name]["bottle"]["tags"][tag]["tab"].to_json
Tab.from_file_content(tab_json, tabfile) Tab.from_file_content(tab_json, tabfile)
else else
tab = Tab.for_keg(keg) tab = keg.tab
tab.runtime_dependencies = begin tab.runtime_dependencies = begin
f_runtime_deps = formula.runtime_dependencies(read_from_tab: false) f_runtime_deps = formula.runtime_dependencies(read_from_tab: false)