1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
require 'rubygems/command'
require 'rubygems/dependency_list'
require 'rubygems/uninstaller'
class Gem::Commands::CleanupCommand < Gem::Command
def initialize
super 'cleanup',
'Clean up old versions of installed gems',
:force => false, :install_dir => Gem.dir
add_option('-n', '-d', '--dryrun',
'Do not uninstall gems') do |value, options|
options[:dryrun] = true
end
@candidate_gems = nil
@default_gems = []
@full = nil
@gems_to_cleanup = nil
@original_home = nil
@original_path = nil
@primary_gems = nil
end
def arguments # :nodoc:
"GEMNAME name of gem to cleanup"
end
def defaults_str # :nodoc:
"--no-dryrun"
end
def description # :nodoc:
<<-EOF
The cleanup command removes old versions of gems from GEM_HOME that are not
required to meet a dependency. If a gem is installed elsewhere in GEM_PATH
the cleanup command won't delete it.
If no gems are named all gems in GEM_HOME are cleaned.
EOF
end
def usage # :nodoc:
"#{program_name} [GEMNAME ...]"
end
def execute
say "Cleaning up installed gems..."
if options[:args].empty? then
done = false
last_set = nil
until done do
clean_gems
this_set = @gems_to_cleanup.map { |spec| spec.full_name }.sort
done = this_set.empty? || last_set == this_set
last_set = this_set
end
else
clean_gems
end
say "Clean Up Complete"
verbose do
skipped = @default_gems.map { |spec| spec.full_name }
"Skipped default gems: #{skipped.join ', '}"
end
end
def clean_gems
get_primary_gems
get_candidate_gems
get_gems_to_cleanup
@full = Gem::DependencyList.from_specs
deplist = Gem::DependencyList.new
@gems_to_cleanup.each do |spec| deplist.add spec end
deps = deplist.strongly_connected_components.flatten
@original_home = Gem.dir
@original_path = Gem.path
deps.reverse_each do |spec|
uninstall_dep spec
end
Gem::Specification.reset
end
def get_candidate_gems
@candidate_gems = unless options[:args].empty? then
options[:args].map do |gem_name|
Gem::Specification.find_all_by_name gem_name
end.flatten
else
Gem::Specification.to_a
end
end
def get_gems_to_cleanup
gems_to_cleanup = @candidate_gems.select { |spec|
@primary_gems[spec.name].version != spec.version
}
default_gems, gems_to_cleanup = gems_to_cleanup.partition { |spec|
spec.default_gem?
}
@default_gems += default_gems
@default_gems.uniq!
@gems_to_cleanup = gems_to_cleanup.uniq
end
def get_primary_gems
@primary_gems = {}
Gem::Specification.each do |spec|
if @primary_gems[spec.name].nil? or
@primary_gems[spec.name].version < spec.version then
@primary_gems[spec.name] = spec
end
end
end
def uninstall_dep spec
return unless @full.ok_to_remove?(spec.full_name)
if options[:dryrun] then
say "Dry Run Mode: Would uninstall #{spec.full_name}"
return
end
say "Attempting to uninstall #{spec.full_name}"
uninstall_options = {
:executables => false,
:version => "= #{spec.version}",
}
uninstall_options[:user_install] = Gem.user_dir == spec.base_dir
uninstaller = Gem::Uninstaller.new spec.name, uninstall_options
begin
uninstaller.uninstall
rescue Gem::DependencyRemovalException, Gem::InstallError,
Gem::GemNotInHomeException, Gem::FilePermissionError => e
say "Unable to uninstall #{spec.full_name}:"
say "\t#{e.class}: #{e.message}"
end
ensure
# Restore path Gem::Uninstaller may have changed
Gem.use_paths @original_home, *@original_path
end
end
|