Skip to content

Commit

Permalink
uninstall: refuse when dependants still installed
Browse files Browse the repository at this point in the history
Closes Homebrew#934.
  • Loading branch information
alyssais committed Oct 25, 2016
1 parent 3f3fa4d commit 6f0aabe
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 7 deletions.
9 changes: 9 additions & 0 deletions Library/Homebrew/cmd/uninstall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ def uninstall

if !ARGV.force?
ARGV.kegs.each do |keg|
dependants = keg.installed_dependants
if dependants.any?
dependants_output = dependants.map { |k| "#{k.name} #{k.version}" }.join(", ")
conjugation = dependants.count == 1 ? "is" : "are"
ofail "Refusing to uninstall #{keg} because it is required by #{dependants_output}, which #{conjugation} currently installed."
puts "Remove it anyway with `brew uninstall --force #{keg.name}`."
next
end

keg.lock do
puts "Uninstalling #{keg}... (#{keg.abv})"
keg.unlink
Expand Down
7 changes: 7 additions & 0 deletions Library/Homebrew/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,13 @@ def self.each
end
end

# Clear caches of .racks and .installed.
# @private
def self.clear_cache
@racks = nil
@installed = nil
end

# An array of all racks currently installed.
# @private
def self.racks
Expand Down
15 changes: 15 additions & 0 deletions Library/Homebrew/keg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,21 @@ def version
PkgVersion.parse(path.basename.to_s)
end

def formula
Formulary.from_keg(self)
end

def installed_dependants
Formula.installed.flat_map(&:installed_kegs).select do |keg|
Tab.for_keg(keg).runtime_dependencies.any? do |dep|
# Resolve formula rather than directly comparing names
# in case of conflicts between formulae from different taps.
dep_formula = Formulary.factory(dep["full_name"])
dep_formula == formula && dep["version"] == version.to_s
end
end
end

def find(*args, &block)
path.find(*args, &block)
end
Expand Down
69 changes: 62 additions & 7 deletions Library/Homebrew/test/test_keg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,22 @@
class LinkTests < Homebrew::TestCase
include FileUtils

def setup
keg = HOMEBREW_CELLAR.join("foo", "1.0")
keg.join("bin").mkpath
def setup_test_keg(name, version)
path = HOMEBREW_CELLAR.join(name, version)
path.join("bin").mkpath

%w[hiworld helloworld goodbye_cruel_world].each do |file|
touch keg.join("bin", file)
touch path.join("bin", file)
end

@keg = Keg.new(keg)
keg = Keg.new(path)
@kegs ||= []
@kegs << keg
keg
end

def setup
@keg = setup_test_keg("foo", "1.0")
@dst = HOMEBREW_PREFIX.join("bin", "helloworld")
@nonexistent = Pathname.new("/some/nonexistent/path")

Expand All @@ -27,8 +34,10 @@ def setup
end

def teardown
@keg.unlink
@keg.uninstall
@kegs.each do |keg|
keg.unlink
keg.uninstall
end

$stdout = @old_stdout

Expand Down Expand Up @@ -305,3 +314,49 @@ def test_removes_broken_symlinks_that_conflict_with_directories
keg.uninstall
end
end

class InstalledDependantsTests < LinkTests
def stub_formula_name(name)
stub_formula_loader formula(name) { url "foo-1.0" }
end

def setup_test_keg(name, version)
stub_formula_name(name)
keg = super
Formula.clear_cache
keg
end

def setup
super
@dependant = setup_test_keg("bar", "1.0")
end

def dependencies(deps)
tab = Tab.for_keg(@dependant)
tab.tabfile = @dependant.join("INSTALL_RECEIPT.json")
tab.runtime_dependencies = deps
tab.write
end

def test_no_dependencies
dependencies []
assert_empty @keg.installed_dependants
end

def test_same_name_different_version
dependencies [{ "full_name" => "foo", "version" => "1.1" }]
assert_empty @keg.installed_dependants
end

def test_different_name_same_version
stub_formula_name("baz")
dependencies [{ "full_name" => "baz", "version" => @keg.version.to_s }]
assert_empty @keg.installed_dependants
end

def test_same_name_and_version
dependencies [{ "full_name" => "foo", "version" => "1.0" }]
assert_equal [@dependant], @keg.installed_dependants
end
end

0 comments on commit 6f0aabe

Please sign in to comment.