2015-08-03 13:09:07 +01:00
|
|
|
require "formula"
|
|
|
|
require "keg"
|
|
|
|
require "bottles"
|
2015-08-16 21:01:52 +01:00
|
|
|
require "thread"
|
2010-09-11 20:22:54 +01:00
|
|
|
|
2014-06-18 22:41:47 -05:00
|
|
|
module Homebrew
|
2010-09-11 20:22:54 +01:00
|
|
|
def cleanup
|
|
|
|
if ARGV.named.empty?
|
2013-05-15 12:45:39 -05:00
|
|
|
cleanup_cellar
|
|
|
|
cleanup_cache
|
2014-05-14 12:12:30 -05:00
|
|
|
cleanup_logs
|
2013-06-21 15:42:14 -05:00
|
|
|
unless ARGV.dry_run?
|
|
|
|
cleanup_lockfiles
|
|
|
|
rm_DS_Store
|
|
|
|
end
|
2010-09-11 20:22:54 +01:00
|
|
|
else
|
2015-05-17 20:09:49 +08:00
|
|
|
ARGV.resolved_formulae.each { |f| cleanup_formula(f) }
|
2013-05-15 12:45:35 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-05-14 12:12:30 -05:00
|
|
|
def cleanup_logs
|
2014-05-17 16:59:14 -07:00
|
|
|
return unless HOMEBREW_LOGS.directory?
|
2014-05-14 12:12:30 -05:00
|
|
|
HOMEBREW_LOGS.subdirs.each do |dir|
|
2015-08-16 16:57:12 +01:00
|
|
|
cleanup_path(dir) { dir.rmtree } if prune?(dir, :days_default => 14)
|
2014-05-14 12:12:30 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-05-15 12:45:39 -05:00
|
|
|
def cleanup_cellar
|
2015-08-13 20:41:16 +08:00
|
|
|
Formula.installed.each do |formula|
|
|
|
|
cleanup_formula formula
|
2010-09-11 20:22:54 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def cleanup_formula(f)
|
2013-05-15 12:45:36 -05:00
|
|
|
if f.installed?
|
2015-11-29 15:26:08 +08:00
|
|
|
eligible_kegs = f.installed_kegs.select { |k| f.pkg_version > k.version }
|
2015-04-17 22:58:02 -04:00
|
|
|
if eligible_kegs.any? && eligible_for_cleanup?(f)
|
2015-04-17 22:58:02 -04:00
|
|
|
eligible_kegs.each { |keg| cleanup_keg(keg) }
|
|
|
|
else
|
|
|
|
eligible_kegs.each { |keg| opoo "Skipping (old) keg-only: #{keg}" }
|
2010-09-11 20:22:54 +01:00
|
|
|
end
|
2015-11-29 15:26:08 +08:00
|
|
|
elsif f.installed_prefixes.any?
|
2010-09-11 20:22:54 +01:00
|
|
|
# If the cellar only has one version installed, don't complain
|
|
|
|
# that we can't tell which one to keep.
|
2015-05-27 20:48:32 +08:00
|
|
|
opoo "Skipping #{f.full_name}: most recent version #{f.pkg_version} not installed"
|
2010-09-11 20:22:54 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def cleanup_keg(keg)
|
2013-05-15 12:45:38 -05:00
|
|
|
if keg.linked?
|
|
|
|
opoo "Skipping (old) #{keg} due to it being linked"
|
|
|
|
else
|
2015-04-07 21:14:32 -04:00
|
|
|
cleanup_path(keg) { keg.uninstall }
|
2013-05-15 12:45:38 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-05-15 12:45:36 -05:00
|
|
|
def cleanup_cache
|
2014-10-19 12:18:44 +01:00
|
|
|
return unless HOMEBREW_CACHE.directory?
|
2015-08-13 10:22:28 +01:00
|
|
|
HOMEBREW_CACHE.children.each do |path|
|
2015-09-26 00:24:23 -07:00
|
|
|
if path.to_s.end_with? ".incomplete"
|
|
|
|
cleanup_path(path) { path.unlink }
|
|
|
|
next
|
|
|
|
end
|
2015-08-16 16:57:12 +01:00
|
|
|
if prune?(path)
|
2015-08-13 10:22:28 +01:00
|
|
|
if path.file?
|
|
|
|
cleanup_path(path) { path.unlink }
|
|
|
|
elsif path.directory? && path.to_s.include?("--")
|
2015-08-13 10:34:45 +01:00
|
|
|
cleanup_path(path) { FileUtils.rm_rf path }
|
2015-08-13 10:22:28 +01:00
|
|
|
end
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
next unless path.file?
|
|
|
|
file = path
|
2015-04-11 16:37:47 +01:00
|
|
|
|
2015-06-04 21:41:07 +08:00
|
|
|
if Pathname::BOTTLE_EXTNAME_RX === file.to_s
|
|
|
|
version = bottle_resolve_version(file) rescue file.version
|
|
|
|
else
|
|
|
|
version = file.version
|
|
|
|
end
|
|
|
|
next unless version
|
2013-06-06 12:34:47 -05:00
|
|
|
next unless (name = file.basename.to_s[/(.*)-(?:#{Regexp.escape(version)})/, 1])
|
2013-05-15 12:45:39 -05:00
|
|
|
|
2015-08-13 10:31:37 +01:00
|
|
|
next unless HOMEBREW_CELLAR.directory?
|
|
|
|
|
2013-05-15 12:45:39 -05:00
|
|
|
begin
|
2015-05-17 20:09:49 +08:00
|
|
|
f = Formulary.from_rack(HOMEBREW_CELLAR/name)
|
2015-10-07 17:34:29 +08:00
|
|
|
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
|
2013-05-15 12:45:39 -05:00
|
|
|
next
|
2012-03-06 20:12:42 +00:00
|
|
|
end
|
2013-05-15 12:45:39 -05:00
|
|
|
|
2015-07-22 22:26:52 +08:00
|
|
|
file_is_stale = if PkgVersion === version
|
2015-08-03 13:09:07 +01:00
|
|
|
f.pkg_version > version
|
|
|
|
else
|
|
|
|
f.version > version
|
|
|
|
end
|
2015-07-22 22:26:52 +08:00
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
if file_is_stale || ARGV.switch?("s") && !f.installed? || bottle_file_outdated?(f, file)
|
2015-04-07 21:14:32 -04:00
|
|
|
cleanup_path(file) { file.unlink }
|
2013-05-15 12:45:39 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-04-07 21:14:32 -04:00
|
|
|
def cleanup_path(path)
|
2013-05-15 12:45:39 -05:00
|
|
|
if ARGV.dry_run?
|
2015-04-07 21:14:32 -04:00
|
|
|
puts "Would remove: #{path} (#{path.abv})"
|
2013-05-15 12:45:39 -05:00
|
|
|
else
|
2015-04-07 21:14:32 -04:00
|
|
|
puts "Removing: #{path}... (#{path.abv})"
|
|
|
|
yield
|
2012-03-06 20:12:42 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-21 15:42:14 -05:00
|
|
|
def cleanup_lockfiles
|
2013-06-21 18:37:52 -05:00
|
|
|
return unless HOMEBREW_CACHE_FORMULA.directory?
|
2013-06-21 15:42:14 -05:00
|
|
|
candidates = HOMEBREW_CACHE_FORMULA.children
|
2015-08-03 13:09:07 +01:00
|
|
|
lockfiles = candidates.select { |f| f.file? && f.extname == ".brewing" }
|
2015-08-17 17:08:23 +02:00
|
|
|
lockfiles.each do |file|
|
|
|
|
next unless file.readable?
|
2015-08-03 13:09:07 +01:00
|
|
|
file.open.flock(File::LOCK_EX | File::LOCK_NB) && file.unlink
|
2013-06-21 15:42:14 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-08-10 16:06:24 -04:00
|
|
|
def rm_DS_Store
|
2015-08-15 18:12:27 +08:00
|
|
|
paths = Queue.new
|
|
|
|
%w[Cellar Frameworks Library bin etc include lib opt sbin share var].
|
2015-08-17 17:08:23 +02:00
|
|
|
map { |p| HOMEBREW_PREFIX/p }.each { |p| paths << p if p.exist? }
|
2015-08-15 18:12:27 +08:00
|
|
|
workers = (0...Hardware::CPU.cores).map do
|
|
|
|
Thread.new do
|
|
|
|
begin
|
|
|
|
while p = paths.pop(true)
|
|
|
|
quiet_system "find", p, "-name", ".DS_Store", "-delete"
|
|
|
|
end
|
|
|
|
rescue ThreadError # ignore empty queue error
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
workers.map(&:join)
|
2012-08-10 16:06:24 -04:00
|
|
|
end
|
|
|
|
|
2015-08-22 13:15:33 +08:00
|
|
|
def prune?(path, options = {})
|
2015-08-16 16:57:12 +01:00
|
|
|
@time ||= Time.now
|
|
|
|
|
|
|
|
path_modified_time = path.mtime
|
|
|
|
days_default = options[:days_default]
|
2015-08-15 15:19:36 +02:00
|
|
|
|
2015-08-16 16:57:12 +01:00
|
|
|
prune = ARGV.value "prune"
|
|
|
|
|
|
|
|
return true if prune == "all"
|
|
|
|
|
|
|
|
prune_time = if prune
|
|
|
|
@time - 60 * 60 * 24 * prune.to_i
|
|
|
|
elsif days_default
|
|
|
|
@time - 60 * 60 * 24 * days_default.to_i
|
2015-08-15 15:19:36 +02:00
|
|
|
end
|
2015-08-16 16:57:12 +01:00
|
|
|
|
|
|
|
return false unless prune_time
|
|
|
|
|
2015-08-16 17:50:33 +01:00
|
|
|
path_modified_time < prune_time
|
2015-08-15 15:19:36 +02:00
|
|
|
end
|
|
|
|
|
2015-04-17 22:58:02 -04:00
|
|
|
def eligible_for_cleanup?(formula)
|
2012-08-10 16:33:47 -04:00
|
|
|
# It used to be the case that keg-only kegs could not be cleaned up, because
|
|
|
|
# older brews were built against the full path to the keg-only keg. Then we
|
|
|
|
# introduced the opt symlink, and built against that instead. So provided
|
|
|
|
# no brew exists that was built against an old-style keg-only keg, we can
|
|
|
|
# remove it.
|
2015-08-03 13:09:07 +01:00
|
|
|
if !formula.keg_only? || ARGV.force?
|
2012-08-10 16:33:47 -04:00
|
|
|
true
|
2015-04-17 22:58:02 -04:00
|
|
|
elsif formula.opt_prefix.directory?
|
2012-08-10 16:33:47 -04:00
|
|
|
# SHA records were added to INSTALL_RECEIPTS the same day as opt symlinks
|
2015-06-14 14:44:41 +08:00
|
|
|
Formula.installed.select do |f|
|
|
|
|
f.deps.any? do |d|
|
|
|
|
d.to_formula.full_name == formula.full_name rescue d.name == formula.name
|
|
|
|
end
|
2015-11-29 15:26:08 +08:00
|
|
|
end.all? { |f| f.installed_prefixes.all? { |keg| Tab.for_keg(keg).HEAD } }
|
2012-08-10 16:33:47 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|