Re-enable spec/library for full CI runs.
[rbx.git] / lib / rubygems / uninstaller.rb
blobc5ae47b7eb51821c67686f9c8737fbe10497e3ed
1 #--
2 # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3 # All rights reserved.
4 # See LICENSE.txt for permissions.
5 #++
7 require 'fileutils'
8 require 'rubygems'
9 require 'rubygems/dependency_list'
10 require 'rubygems/doc_manager'
11 require 'rubygems/user_interaction'
14 # An Uninstaller.
16 class Gem::Uninstaller
18   include Gem::UserInteraction
20   ##
21   # Constructs an Uninstaller instance
22   #
23   # gem:: [String] The Gem name to uninstall
25   def initialize(gem, options = {})
26     @gem = gem
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] 
34   end
36   ##
37   # Performs the uninstall of the Gem.  This removes the spec, the
38   # Gem directory, and the cached .gem file,
40   def uninstall
41     list = Gem.source_index.search(/^#{@gem}$/, @version)
43     if list.empty? then
44       raise Gem::InstallError, "Unknown gem #{@gem}-#{@version}"
45     elsif list.size > 1 && @force_all
46       remove_all(list.dup) 
47       remove_executables(list.last)
48     elsif list.size > 1 
49       say 
50       gem_names = list.collect {|gem| gem.full_name} + ["All versions"]
51       gem_name, index =
52         choose_from_list("Select gem to uninstall:", gem_names)
53       if index == list.size
54         remove_all(list.dup) 
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)
60       else
61         say "Error: must enter a number [1-#{list.size+1}]"
62       end
63     else
64       remove(list[0], list.dup)
65       remove_executables(list.last)
66     end
67   end
68   
69   ##
70   # Removes installed executables and batch files (windows only) for
71   # +gemspec+.
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
81       }
83       executables = gemspec.executables.clone
85       list.each do |spec|
86         spec.executables.each do |exe_name|
87           executables.delete(exe_name)
88         end
89       end
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
97                else
98                  @force_executables
99                end
101       unless answer then
102         say "Executables and scripts will remain installed."
103       else
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")
110         end
111       end
112     end
113   end
114   
115   ##
116   # Removes all gems in +list+.
117   #
118   # NOTE: removes uninstalled gems from +list+.
120   def remove_all(list)
121     list.dup.each { |spec| remove spec, list }
122   end
124   ##
125   # spec:: the spec of the gem to be uninstalled
126   # list:: the list of all such gems
127   #
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)"
135     end
137     unless path_ok? spec then
138       e = Gem::GemNotInHomeException.new \
139             "Gem is not installed in directory #{@gem_home}"
140       e.spec = spec
142       raise e
143     end
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"
158     end
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"
167     end
169     FileUtils.rm_rf gem
171     Gem::DocManager.new(spec).uninstall_doc
173     say "Successfully uninstalled #{spec.full_name}"
175     list.delete spec
176   end
178   def path_ok?(spec)
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
183   end
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)
191   end
193   def ask_if_ok(spec)
194     msg = ['']
195     msg << 'You have requested to uninstall the gem:'
196     msg << "\t#{spec.full_name}"
197     spec.dependent_gems.each do |gem,dep,satlist|
198       msg <<
199         ("#{gem.name}-#{gem.version} depends on " +
200         "[#{dep.name} (#{dep.version_requirements})]")
201     end
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)
205   end