OSDN Git Service

fdb30ad8d313ff808d8a84b0623b052ddced3bfc
[splhack/AndroidRuby.git] / lib / ruby-1.9.1-rc1 / lib / rubygems / source_info_cache.rb
1 require 'fileutils'
2
3 require 'rubygems'
4 require 'rubygems/source_info_cache_entry'
5 require 'rubygems/user_interaction'
6
7 ##
8 # SourceInfoCache stores a copy of the gem index for each gem source.
9 #
10 # There are two possible cache locations, the system cache and the user cache:
11 # * The system cache is preferred if it is writable or can be created.
12 # * The user cache is used otherwise
13 #
14 # Once a cache is selected, it will be used for all operations.
15 # SourceInfoCache will not switch between cache files dynamically.
16 #
17 # Cache data is a Hash mapping a source URI to a SourceInfoCacheEntry.
18 #
19 #--
20 # To keep things straight, this is how the cache objects all fit together:
21 #
22 #   Gem::SourceInfoCache
23 #     @cache_data = {
24 #       source_uri => Gem::SourceInfoCacheEntry
25 #         @size = source index size
26 #         @source_index = Gem::SourceIndex
27 #       ...
28 #     }
29
30 class Gem::SourceInfoCache
31
32   include Gem::UserInteraction
33
34   ##
35   # The singleton Gem::SourceInfoCache.  If +all+ is true, a full refresh will
36   # be performed if the singleton instance is being initialized.
37
38   def self.cache(all = false)
39     return @cache if @cache
40     @cache = new
41     @cache.refresh all if Gem.configuration.update_sources
42     @cache
43   end
44
45   def self.cache_data
46     cache.cache_data
47   end
48
49   ##
50   # The name of the system cache file.
51
52   def self.latest_system_cache_file
53     File.join File.dirname(system_cache_file),
54               "latest_#{File.basename system_cache_file}"
55   end
56
57   ##
58   # The name of the latest user cache file.
59
60   def self.latest_user_cache_file
61     File.join File.dirname(user_cache_file),
62               "latest_#{File.basename user_cache_file}"
63   end
64
65   ##
66   # Reset all singletons, discarding any changes.
67
68   def self.reset
69     @cache = nil
70     @system_cache_file = nil
71     @user_cache_file = nil
72   end
73
74   ##
75   # Search all source indexes.  See Gem::SourceInfoCache#search.
76
77   def self.search(*args)
78     cache.search(*args)
79   end
80
81   ##
82   # Search all source indexes returning the source_uri.  See
83   # Gem::SourceInfoCache#search_with_source.
84
85   def self.search_with_source(*args)
86     cache.search_with_source(*args)
87   end
88
89   ##
90   # The name of the system cache file. (class method)
91
92   def self.system_cache_file
93     @system_cache_file ||= Gem.default_system_source_cache_dir
94   end
95
96   ##
97   # The name of the user cache file.
98
99   def self.user_cache_file
100     @user_cache_file ||=
101       ENV['GEMCACHE'] || Gem.default_user_source_cache_dir
102   end
103
104   def initialize # :nodoc:
105     @cache_data = nil
106     @cache_file = nil
107     @dirty = false
108     @only_latest = true
109   end
110
111   ##
112   # The most recent cache data.
113
114   def cache_data
115     return @cache_data if @cache_data
116     cache_file # HACK writable check
117
118     @only_latest = true
119
120     @cache_data = read_cache_data latest_cache_file
121
122     @cache_data
123   end
124
125   ##
126   # The name of the cache file.
127
128   def cache_file
129     return @cache_file if @cache_file
130     @cache_file = (try_file(system_cache_file) or
131       try_file(user_cache_file) or
132       raise "unable to locate a writable cache file")
133   end
134
135   ##
136   # Write the cache to a local file (if it is dirty).
137
138   def flush
139     write_cache if @dirty
140     @dirty = false
141   end
142
143   def latest_cache_data
144     latest_cache_data = {}
145
146     cache_data.each do |repo, sice|
147       latest = sice.source_index.latest_specs
148
149       new_si = Gem::SourceIndex.new
150       new_si.add_specs(*latest)
151
152       latest_sice = Gem::SourceInfoCacheEntry.new new_si, sice.size
153       latest_cache_data[repo] = latest_sice
154     end
155
156     latest_cache_data
157   end
158
159   ##
160   # The name of the latest cache file.
161
162   def latest_cache_file
163     File.join File.dirname(cache_file), "latest_#{File.basename cache_file}"
164   end
165
166   ##
167   # The name of the latest system cache file.
168
169   def latest_system_cache_file
170     self.class.latest_system_cache_file
171   end
172
173   ##
174   # The name of the latest user cache file.
175
176   def latest_user_cache_file
177     self.class.latest_user_cache_file
178   end
179
180   ##
181   # Merges the complete cache file into this Gem::SourceInfoCache.
182
183   def read_all_cache_data
184     if @only_latest then
185       @only_latest = false
186       all_data = read_cache_data cache_file
187
188       cache_data.update all_data do |source_uri, latest_sice, all_sice|
189         all_sice.source_index.gems.update latest_sice.source_index.gems
190
191         Gem::SourceInfoCacheEntry.new all_sice.source_index, latest_sice.size
192       end
193
194       begin
195         refresh true
196       rescue Gem::RemoteFetcher::FetchError
197       end
198     end
199   end
200
201   ##
202   # Reads cached data from +file+.
203
204   def read_cache_data(file)
205     # Marshal loads 30-40% faster from a String, and 2MB on 20061116 is small
206     data = open file, 'rb' do |fp| fp.read end
207     cache_data = Marshal.load data
208
209     cache_data.each do |url, sice|
210       next unless sice.is_a?(Hash)
211       update
212
213       cache = sice['cache']
214       size  = sice['size']
215
216       if cache.is_a?(Gem::SourceIndex) and size.is_a?(Numeric) then
217         new_sice = Gem::SourceInfoCacheEntry.new cache, size
218         cache_data[url] = new_sice
219       else # irreperable, force refetch.
220         reset_cache_for url, cache_data
221       end
222     end
223
224     cache_data
225   rescue Errno::ENOENT
226     {}
227   rescue => e
228     if Gem.configuration.really_verbose then
229       say "Exception during cache_data handling: #{e.class} - #{e}"
230       say "Cache file was: #{file}"
231       say "\t#{e.backtrace.join "\n\t"}"
232     end
233
234     {}
235   end
236
237   ##
238   # Refreshes each source in the cache from its repository.  If +all+ is
239   # false, only latest gems are updated.
240
241   def refresh(all)
242     Gem.sources.each do |source_uri|
243       cache_entry = cache_data[source_uri]
244       if cache_entry.nil? then
245         cache_entry = Gem::SourceInfoCacheEntry.new nil, 0
246         cache_data[source_uri] = cache_entry
247       end
248
249       update if cache_entry.refresh source_uri, all
250     end
251
252     flush
253   end
254
255   def reset_cache_for(url, cache_data)
256     say "Reseting cache for #{url}" if Gem.configuration.really_verbose
257
258     sice = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
259     sice.refresh url, false # HACK may be unnecessary, see ::cache and #refresh
260
261     cache_data[url] = sice
262     cache_data
263   end
264
265   def reset_cache_data
266     @cache_data = nil
267     @only_latest = true
268   end
269
270   ##
271   # Force cache file to be reset, useful for integration testing of rubygems
272
273   def reset_cache_file
274     @cache_file = nil
275   end
276
277   ##
278   # Searches all source indexes.  See Gem::SourceIndex#search for details on
279   # +pattern+ and +platform_only+.  If +all+ is set to true, the full index
280   # will be loaded before searching.
281
282   def search(pattern, platform_only = false, all = false)
283     read_all_cache_data if all
284
285     cache_data.map do |source_uri, sic_entry|
286       next unless Gem.sources.include? source_uri
287       # TODO - Remove this gunk after 2008/11
288       unless pattern.kind_of?(Gem::Dependency)
289         pattern = Gem::Dependency.new(pattern, Gem::Requirement.default) 
290       end
291       sic_entry.source_index.search pattern, platform_only
292     end.flatten.compact
293   end
294
295   # Searches all source indexes for +pattern+.  If +only_platform+ is true,
296   # only gems matching Gem.platforms will be selected.  Returns an Array of
297   # pairs containing the Gem::Specification found and the source_uri it was
298   # found at.
299   def search_with_source(pattern, only_platform = false, all = false)
300     read_all_cache_data if all
301
302     results = []
303
304     cache_data.map do |source_uri, sic_entry|
305       next unless Gem.sources.include? source_uri
306
307       # TODO - Remove this gunk after 2008/11
308       unless pattern.kind_of?(Gem::Dependency)
309         pattern = Gem::Dependency.new(pattern, Gem::Requirement.default) 
310       end
311
312       sic_entry.source_index.search(pattern, only_platform).each do |spec|
313         results << [spec, source_uri]
314       end
315     end
316
317     results
318   end
319
320   ##
321   # Set the source info cache data directly.  This is mainly used for unit
322   # testing when we don't want to read a file system to grab the cached source
323   # index information.  The +hash+ should map a source URL into a
324   # SourceInfoCacheEntry.
325
326   def set_cache_data(hash)
327     @cache_data = hash
328     update
329   end
330
331   ##
332   # The name of the system cache file.
333
334   def system_cache_file
335     self.class.system_cache_file
336   end
337
338   ##
339   # Determine if +path+ is a candidate for a cache file.  Returns +path+ if
340   # it is, nil if not.
341
342   def try_file(path)
343     return path if File.writable? path
344     return nil if File.exist? path
345
346     dir = File.dirname path
347
348     unless File.exist? dir then
349       begin
350         FileUtils.mkdir_p dir
351       rescue RuntimeError, SystemCallError
352         return nil
353       end
354     end
355
356     return path if File.writable? dir
357
358     nil
359   end
360
361   ##
362   # Mark the cache as updated (i.e. dirty).
363
364   def update
365     @dirty = true
366   end
367
368   ##
369   # The name of the user cache file.
370
371   def user_cache_file
372     self.class.user_cache_file
373   end
374
375   ##
376   # Write data to the proper cache files.
377
378   def write_cache
379     if not File.exist?(cache_file) or not @only_latest then
380       open cache_file, 'wb' do |io|
381         io.write Marshal.dump(cache_data)
382       end
383     end
384
385     open latest_cache_file, 'wb' do |io|
386       io.write Marshal.dump(latest_cache_data)
387     end
388   end
389
390   reset
391
392 end
393