-
-
Notifications
You must be signed in to change notification settings - Fork 10.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Keg.find_some_installed_dependents: move to installed_dependents.rb
- Loading branch information
1 parent
fcb1aa8
commit f7f9433
Showing
5 changed files
with
255 additions
and
223 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# typed: false | ||
# frozen_string_literal: true | ||
|
||
require "cask_dependent" | ||
|
||
# Helper functions for installed dependents. | ||
# | ||
# @api private | ||
module InstalledDependents | ||
extend T::Sig | ||
|
||
module_function | ||
|
||
# Given an array of kegs, this method will try to find some other kegs | ||
# or casks that depend on them. If it does, it returns: | ||
# | ||
# - some kegs in the passed array that have installed dependents | ||
# - some installed dependents of those kegs. | ||
# | ||
# If it doesn't, it returns nil. | ||
# | ||
# Note that nil will be returned if the only installed dependents of the | ||
# passed kegs are other kegs in the array or casks present in the casks | ||
# parameter. | ||
# | ||
# For efficiency, we don't bother trying to get complete data. | ||
def find_some_installed_dependents(kegs, casks: []) | ||
keg_names = kegs.select(&:optlinked?).map(&:name) | ||
keg_formulae = [] | ||
kegs_by_source = kegs.group_by do |keg| | ||
# First, attempt to resolve the keg to a formula | ||
# to get up-to-date name and tap information. | ||
f = keg.to_formula | ||
keg_formulae << f | ||
[f.name, f.tap] | ||
rescue | ||
# If the formula for the keg can't be found, | ||
# fall back to the information in the tab. | ||
[keg.name, keg.tab.tap] | ||
end | ||
|
||
all_required_kegs = Set.new | ||
all_dependents = [] | ||
|
||
# Don't include dependencies of kegs that were in the given array. | ||
dependents_to_check = (Formula.installed - keg_formulae) + (Cask::Caskroom.casks - casks) | ||
|
||
dependents_to_check.each do |dependent| | ||
required = case dependent | ||
when Formula | ||
dependent.missing_dependencies(hide: keg_names) | ||
when Cask::Cask | ||
CaskDependent.new(dependent).runtime_dependencies.map(&:to_formula) | ||
end | ||
|
||
required_kegs = required.map do |f| | ||
f_kegs = kegs_by_source[[f.name, f.tap]] | ||
next unless f_kegs | ||
|
||
f_kegs.max_by(&:version) | ||
end.compact | ||
|
||
next if required_kegs.empty? | ||
|
||
all_required_kegs += required_kegs | ||
all_dependents << dependent.to_s | ||
end | ||
|
||
return if all_required_kegs.empty? | ||
return if all_dependents.empty? | ||
|
||
[all_required_kegs.to_a, all_dependents.sort] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
# typed: false | ||
# frozen_string_literal: true | ||
|
||
require "installed_dependents" | ||
|
||
describe InstalledDependents do | ||
include FileUtils | ||
|
||
def setup_test_keg(name, version) | ||
path = HOMEBREW_CELLAR/name/version | ||
(path/"bin").mkpath | ||
|
||
%w[hiworld helloworld goodbye_cruel_world].each do |file| | ||
touch path/"bin"/file | ||
end | ||
|
||
Keg.new(path) | ||
end | ||
|
||
let!(:keg) { setup_test_keg("foo", "1.0") } | ||
|
||
describe "::find_some_installed_dependents" do | ||
def stub_formula_name(name) | ||
f = formula(name) { url "foo-1.0" } | ||
stub_formula_loader f | ||
stub_formula_loader f, "homebrew/core/#{f}" | ||
f | ||
end | ||
|
||
def setup_test_keg(name, version) | ||
f = stub_formula_name(name) | ||
keg = super | ||
Tab.create(f, DevelopmentTools.default_compiler, :libcxx).write | ||
keg | ||
end | ||
|
||
before do | ||
keg.link | ||
end | ||
|
||
def alter_tab(keg = dependent) | ||
tab = Tab.for_keg(keg) | ||
yield tab | ||
tab.write | ||
end | ||
|
||
# 1.1.6 is the earliest version of Homebrew that generates correct runtime | ||
# dependency lists in {Tab}s. | ||
def dependencies(deps, homebrew_version: "1.1.6") | ||
alter_tab do |tab| | ||
tab.homebrew_version = homebrew_version | ||
tab.tabfile = dependent/Tab::FILENAME | ||
tab.runtime_dependencies = deps | ||
end | ||
end | ||
|
||
def unreliable_dependencies(deps) | ||
# 1.1.5 is (hopefully!) the last version of Homebrew that generates | ||
# incorrect runtime dependency lists in {Tab}s. | ||
dependencies(deps, homebrew_version: "1.1.5") | ||
end | ||
|
||
let(:dependent) { setup_test_keg("bar", "1.0") } | ||
|
||
specify "a dependency with no Tap in Tab" do | ||
tap_dep = setup_test_keg("baz", "1.0") | ||
|
||
# allow tap_dep to be linked too | ||
FileUtils.rm_r tap_dep/"bin" | ||
tap_dep.link | ||
|
||
alter_tab(keg) { |t| t.source["tap"] = nil } | ||
|
||
dependencies nil | ||
Formula["bar"].class.depends_on "foo" | ||
Formula["bar"].class.depends_on "baz" | ||
|
||
result = described_class.find_some_installed_dependents([keg, tap_dep]) | ||
expect(result).to eq([[keg, tap_dep], ["bar"]]) | ||
end | ||
|
||
specify "no dependencies anywhere" do | ||
dependencies nil | ||
expect(described_class.find_some_installed_dependents([keg])).to be nil | ||
end | ||
|
||
specify "missing Formula dependency" do | ||
dependencies nil | ||
Formula["bar"].class.depends_on "foo" | ||
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar"]]) | ||
end | ||
|
||
specify "uninstalling dependent and dependency" do | ||
dependencies nil | ||
Formula["bar"].class.depends_on "foo" | ||
expect(described_class.find_some_installed_dependents([keg, dependent])).to be nil | ||
end | ||
|
||
specify "renamed dependency" do | ||
dependencies nil | ||
|
||
stub_formula_loader Formula["foo"], "homebrew/core/foo-old" | ||
renamed_path = HOMEBREW_CELLAR/"foo-old" | ||
(HOMEBREW_CELLAR/"foo").rename(renamed_path) | ||
renamed_keg = Keg.new(renamed_path/"1.0") | ||
|
||
Formula["bar"].class.depends_on "foo" | ||
|
||
result = described_class.find_some_installed_dependents([renamed_keg]) | ||
expect(result).to eq([[renamed_keg], ["bar"]]) | ||
end | ||
|
||
specify "empty dependencies in Tab" do | ||
dependencies [] | ||
expect(described_class.find_some_installed_dependents([keg])).to be nil | ||
end | ||
|
||
specify "same name but different version in Tab" do | ||
dependencies [{ "full_name" => "foo", "version" => "1.1" }] | ||
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar"]]) | ||
end | ||
|
||
specify "different name and same version in Tab" do | ||
stub_formula_name("baz") | ||
dependencies [{ "full_name" => "baz", "version" => keg.version.to_s }] | ||
expect(described_class.find_some_installed_dependents([keg])).to be nil | ||
end | ||
|
||
specify "same name and version in Tab" do | ||
dependencies [{ "full_name" => "foo", "version" => "1.0" }] | ||
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar"]]) | ||
end | ||
|
||
specify "fallback for old versions" do | ||
unreliable_dependencies [{ "full_name" => "baz", "version" => "1.0" }] | ||
Formula["bar"].class.depends_on "foo" | ||
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar"]]) | ||
end | ||
|
||
specify "non-opt-linked" do | ||
keg.remove_opt_record | ||
dependencies [{ "full_name" => "foo", "version" => "1.0" }] | ||
expect(described_class.find_some_installed_dependents([keg])).to be nil | ||
end | ||
|
||
specify "keg-only" do | ||
keg.unlink | ||
Formula["foo"].class.keg_only "a good reason" | ||
dependencies [{ "full_name" => "foo", "version" => "1.1" }] # different version | ||
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar"]]) | ||
end | ||
|
||
def stub_cask_name(name, version, dependency) | ||
c = Cask::CaskLoader.load(+<<-RUBY) | ||
cask "#{name}" do | ||
version "#{version}" | ||
url "c-1" | ||
depends_on formula: "#{dependency}" | ||
end | ||
RUBY | ||
|
||
stub_cask_loader c | ||
c | ||
end | ||
|
||
def setup_test_cask(name, version, dependency) | ||
c = stub_cask_name(name, version, dependency) | ||
Cask::Caskroom.path.join(name, c.version).mkpath | ||
c | ||
end | ||
|
||
specify "identify dependent casks" do | ||
setup_test_cask("qux", "1.0.0", "foo") | ||
dependents = described_class.find_some_installed_dependents([keg]).last | ||
expect(dependents.include?("qux")).to eq(true) | ||
end | ||
end | ||
end |
Oops, something went wrong.