Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

autoremove: ignore build deps when built from src #13839

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion Library/Homebrew/cleanup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ def prune_prefix_symlinks_and_directories
end

def self.autoremove(dry_run: false)
require "utils/autoremove"
require "cask/caskroom"

# If this runs after install, uninstall, reinstall or upgrade,
Expand All @@ -593,7 +594,7 @@ def self.autoremove(dry_run: false)
end
casks = Cask::Caskroom.casks

removable_formulae = Formula.unused_formulae_with_no_dependents(formulae, casks)
removable_formulae = Utils::Autoremove.removable_formulae(formulae, casks)

return if removable_formulae.blank?

Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/cmd/leaves.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def installed_as_dependency?(formula)
def leaves
args = leaves_args.parse

leaves_list = Formula.formulae_with_no_formula_dependents(Formula.installed)
leaves_list = Formula.installed - Formula.installed.flat_map(&:runtime_formula_dependencies)

leaves_list.select!(&method(:installed_on_request?)) if args.installed_on_request?
leaves_list.select!(&method(:installed_as_dependency?)) if args.installed_as_dependency?
Expand Down
40 changes: 0 additions & 40 deletions Library/Homebrew/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1844,46 +1844,6 @@ def self.installed
end.uniq(&:name)
end

# An array of all installed {Formula} with {Cask} dependents.
# @private
def self.formulae_with_cask_dependents(casks)
casks.flat_map { |cask| cask.depends_on[:formula] }
.compact
.map { |f| Formula[f] }
.flat_map { |f| [f, *f.runtime_formula_dependencies].compact }
end

# An array of all installed {Formula} without {Formula} dependents
# @private
def self.formulae_with_no_formula_dependents(formulae)
return [] if formulae.blank?

formulae - formulae.flat_map(&:runtime_formula_dependencies)
end

# Recursive function that returns an array of {Formula} without
# {Formula} dependents that weren't installed on request.
# @private
def self.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
end

if unused_formulae.present?
unused_formulae += unused_formulae_with_no_formula_dependents(formulae - unused_formulae)
end

unused_formulae
end

# An array of {Formula} without {Formula} or {Cask}
# dependents that weren't installed on request.
# @private
def self.unused_formulae_with_no_dependents(formulae, casks)
unused_formulae = unused_formulae_with_no_formula_dependents(formulae)
unused_formulae - formulae_with_cask_dependents(casks)
end

def self.installed_with_alias_path(alias_path)
return [] if alias_path.nil?

Expand Down
130 changes: 0 additions & 130 deletions Library/Homebrew/test/formula_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -450,136 +450,6 @@
end
end

shared_context "with formulae for dependency testing" do
let(:formula_with_deps) do
formula "zero" do
url "zero-1.0"
end
end

let(:formula_is_dep1) do
formula "one" do
url "one-1.1"
end
end

let(:formula_is_dep2) do
formula "two" do
url "two-1.1"
end
end

let(:formulae) do
[
formula_with_deps,
formula_is_dep1,
formula_is_dep2,
]
end

before do
allow(formula_with_deps).to receive(:runtime_formula_dependencies).and_return([formula_is_dep1,
formula_is_dep2])
allow(formula_is_dep1).to receive(:runtime_formula_dependencies).and_return([formula_is_dep2])
end
end

describe "::formulae_with_no_formula_dependents" do
include_context "with formulae for dependency testing"

it "filters out dependencies" do
expect(described_class.formulae_with_no_formula_dependents(formulae))
.to eq([formula_with_deps])
end
end

describe "::unused_formulae_with_no_formula_dependents" do
include_context "with formulae for dependency testing"

let(:tab_from_keg) { double }

before do
allow(Tab).to receive(:for_keg).and_return(tab_from_keg)
end

specify "installed on request" do
allow(tab_from_keg).to receive(:installed_on_request).and_return(true)
expect(described_class.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.unused_formulae_with_no_formula_dependents(formulae))
.to eq(formulae)
end
end

shared_context "with formulae and casks for dependency testing" do
include_context "with formulae for dependency testing"

require "cask/cask_loader"

let(:cask_one_dep) do
Cask::CaskLoader.load(+<<-RUBY)
cask "red" do
depends_on formula: "two"
end
RUBY
end

let(:cask_multiple_deps) do
Cask::CaskLoader.load(+<<-RUBY)
cask "blue" do
depends_on formula: "zero"
end
RUBY
end

let(:cask_no_deps1) do
Cask::CaskLoader.load(+<<-RUBY)
cask "green" do
end
RUBY
end

let(:cask_no_deps2) do
Cask::CaskLoader.load(+<<-RUBY)
cask "purple" do
end
RUBY
end

let(:casks_no_deps) { [cask_no_deps1, cask_no_deps2] }
let(:casks_one_dep) { [cask_no_deps1, cask_no_deps2, cask_one_dep] }
let(:casks_multiple_deps) { [cask_no_deps1, cask_no_deps2, cask_multiple_deps] }

before do
allow(described_class).to receive("[]").with("zero").and_return(formula_with_deps)
allow(described_class).to receive("[]").with("one").and_return(formula_is_dep1)
allow(described_class).to receive("[]").with("two").and_return(formula_is_dep2)
end
end

describe "::formulae_with_cask_dependents" do
include_context "with formulae and casks for dependency testing"

specify "no dependents" do
expect(described_class.formulae_with_cask_dependents(casks_no_deps))
.to eq([])
end

specify "one dependent" do
expect(described_class.formulae_with_cask_dependents(casks_one_dep))
.to eq([formula_is_dep2])
end

specify "multiple dependents" do
expect(described_class.formulae_with_cask_dependents(casks_multiple_deps))
.to eq(formulae)
end
end

describe "::inreplace" do
specify "raises build error on failure" do
f = formula do
Expand Down
162 changes: 162 additions & 0 deletions Library/Homebrew/test/utils/autoremove_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# typed: false
# frozen_string_literal: true

require "utils/autoremove"

describe Utils::Autoremove do
shared_context "with formulae for dependency testing" do
let(:formula_with_deps) do
formula "zero" do
url "zero-1.0"

depends_on "three" => :build
end
end

let(:formula_is_dep1) do
formula "one" do
url "one-1.1"
end
end

let(:formula_is_dep2) do
formula "two" do
url "two-1.1"
end
end

let(:formula_is_build_dep) do
formula "three" do
url "three-1.1"
end
end

let(:formulae) do
[
formula_with_deps,
formula_is_dep1,
formula_is_dep2,
formula_is_build_dep,
]
end

let(:tab_from_keg) { double }

before do
allow(formula_with_deps).to receive(:runtime_formula_dependencies).and_return([formula_is_dep1,
formula_is_dep2])
allow(formula_is_dep1).to receive(:runtime_formula_dependencies).and_return([formula_is_dep2])

allow(Tab).to receive(:for_keg).and_return(tab_from_keg)
end
end

describe "::formulae_with_no_formula_dependents" do
include_context "with formulae for dependency testing"

before do
allow(Formulary).to receive(:factory).with("three").and_return(formula_is_build_dep)
end

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
end

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
end
end

describe "::unused_formulae_with_no_formula_dependents" do
include_context "with formulae for dependency testing"

before do
allow(tab_from_keg).to receive(:poured_from_bottle).and_return(true)
end

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
end

shared_context "with formulae and casks for dependency testing" do
include_context "with formulae for dependency testing"

require "cask/cask_loader"

let(:cask_one_dep) do
Cask::CaskLoader.load(+<<-RUBY)
cask "red" do
depends_on formula: "two"
end
RUBY
end

let(:cask_multiple_deps) do
Cask::CaskLoader.load(+<<-RUBY)
cask "blue" do
depends_on formula: "zero"
end
RUBY
end

let(:cask_no_deps1) do
Cask::CaskLoader.load(+<<-RUBY)
cask "green" do
end
RUBY
end

let(:cask_no_deps2) do
Cask::CaskLoader.load(+<<-RUBY)
cask "purple" do
end
RUBY
end

let(:casks_no_deps) { [cask_no_deps1, cask_no_deps2] }
let(:casks_one_dep) { [cask_no_deps1, cask_no_deps2, cask_one_dep] }
let(:casks_multiple_deps) { [cask_no_deps1, cask_no_deps2, cask_multiple_deps] }

before do
allow(Formula).to receive("[]").with("zero").and_return(formula_with_deps)
allow(Formula).to receive("[]").with("one").and_return(formula_is_dep1)
allow(Formula).to receive("[]").with("two").and_return(formula_is_dep2)
end
end

describe "::formulae_with_cask_dependents" do
include_context "with formulae and casks for dependency testing"

specify "no dependents" do
expect(described_class.send(:formulae_with_cask_dependents, casks_no_deps))
.to eq([])
end

specify "one dependent" do
expect(described_class.send(:formulae_with_cask_dependents, casks_one_dep))
.to eq([formula_is_dep2])
end

specify "multiple dependents" do
expect(described_class.send(:formulae_with_cask_dependents, casks_multiple_deps))
.to match_array([formula_with_deps, formula_is_dep1, formula_is_dep2])
end
end
end