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