2 # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
4 # See LICENSE.txt for permissions.
9 require 'rubygems/dependency_list'
10 require 'rubygems/doc_manager'
11 require 'rubygems/user_interaction'
16 class Gem::Uninstaller
18 include Gem::UserInteraction
21 # Constructs an Uninstaller instance
23 # gem:: [String] The Gem name to uninstall
25 def initialize(gem, options = {})
27 @version = options[:version] || Gem::Requirement.default
28 gem_home = options[:install_dir] || Gem.dir
29 @gem_home = File.expand_path gem_home
30 @force_executables = options[:executables]
31 @force_all = options[:all]
32 @force_ignore = options[:ignore]
33 @bin_dir = options[:bin_dir]
37 # Performs the uninstall of the Gem. This removes the spec, the
38 # Gem directory, and the cached .gem file,
41 list = Gem.source_index.search(/^#{@gem}$/, @version)
44 raise Gem::InstallError, "Unknown gem #{@gem}-#{@version}"
45 elsif list.size > 1 && @force_all
47 remove_executables(list.last)
50 gem_names = list.collect {|gem| gem.full_name} + ["All versions"]
52 choose_from_list("Select gem to uninstall:", gem_names)
55 remove_executables(list.last)
56 elsif index >= 0 && index < list.size
57 to_remove = list[index]
58 remove(to_remove, list)
59 remove_executables(to_remove)
61 say "Error: must enter a number [1-#{list.size+1}]"
64 remove(list[0], list.dup)
65 remove_executables(list.last)
70 # Removes installed executables and batch files (windows only) for
73 def remove_executables(gemspec)
74 return if gemspec.nil?
76 if gemspec.executables.size > 0 then
77 bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home)
79 list = Gem.source_index.search(gemspec.name).delete_if { |spec|
80 spec.version == gemspec.version
83 executables = gemspec.executables.clone
86 spec.executables.each do |exe_name|
87 executables.delete(exe_name)
91 return if executables.size == 0
93 answer = if @force_executables.nil? then
94 ask_yes_no("Remove executables:\n" \
95 "\t#{gemspec.executables.join(", ")}\n\nin addition to the gem?",
96 true) # " # appease ruby-mode - don't ask
102 say "Executables and scripts will remain installed."
104 raise Gem::FilePermissionError, bindir unless File.writable? bindir
106 gemspec.executables.each do |exe_name|
107 say "Removing #{exe_name}"
108 FileUtils.rm_f File.join(bindir, exe_name)
109 FileUtils.rm_f File.join(bindir, "#{exe_name}.bat")
116 # Removes all gems in +list+.
118 # NOTE: removes uninstalled gems from +list+.
121 list.dup.each { |spec| remove spec, list }
125 # spec:: the spec of the gem to be uninstalled
126 # list:: the list of all such gems
128 # Warning: this method modifies the +list+ parameter. Once it has
129 # uninstalled a gem, it is removed from that list.
131 def remove(spec, list)
132 unless dependencies_ok? spec then
133 raise Gem::DependencyRemovalException,
134 "Uninstallation aborted due to dependent gem(s)"
137 unless path_ok? spec then
138 e = Gem::GemNotInHomeException.new \
139 "Gem is not installed in directory #{@gem_home}"
145 raise Gem::FilePermissionError, spec.installation_path unless
146 File.writable?(spec.installation_path)
148 FileUtils.rm_rf spec.full_gem_path
150 original_platform_name = [
151 spec.name, spec.version, spec.original_platform].join '-'
153 spec_dir = File.join spec.installation_path, 'specifications'
154 gemspec = File.join spec_dir, "#{spec.full_name}.gemspec"
156 unless File.exist? gemspec then
157 gemspec = File.join spec_dir, "#{original_platform_name}.gemspec"
160 FileUtils.rm_rf gemspec
162 cache_dir = File.join spec.installation_path, 'cache'
163 gem = File.join cache_dir, "#{spec.full_name}.gem"
165 unless File.exist? gem then
166 gem = File.join cache_dir, "#{original_platform_name}.gem"
171 Gem::DocManager.new(spec).uninstall_doc
173 say "Successfully uninstalled #{spec.full_name}"
179 full_path = File.join @gem_home, 'gems', spec.full_name
180 original_path = File.join @gem_home, 'gems', spec.original_name
182 full_path == spec.full_gem_path || original_path == spec.full_gem_path
185 def dependencies_ok?(spec)
186 return true if @force_ignore
188 source_index = Gem::SourceIndex.from_installed_gems
189 deplist = Gem::DependencyList.from_source_index source_index
190 deplist.ok_to_remove?(spec.full_name) || ask_if_ok(spec)
195 msg << 'You have requested to uninstall the gem:'
196 msg << "\t#{spec.full_name}"
197 spec.dependent_gems.each do |gem,dep,satlist|
199 ("#{gem.name}-#{gem.version} depends on " +
200 "[#{dep.name} (#{dep.version_requirements})]")
202 msg << 'If you remove this gems, one or more dependencies will not be met.'
203 msg << 'Continue with Uninstall?'
204 return ask_yes_no(msg.join("\n"), true)