~ubuntu-branches/ubuntu/quantal/ruby1.9.1/quantal

« back to all changes in this revision

Viewing changes to lib/rubygems/source_index.rb

  • Committer: Bazaar Package Importer
  • Author(s): Lucas Nussbaum
  • Date: 2011-09-24 19:16:17 UTC
  • mfrom: (1.1.8 upstream) (13.1.7 experimental)
  • Revision ID: james.westby@ubuntu.com-20110924191617-o1qz4rcmqjot8zuy
Tags: 1.9.3~rc1-1
* New upstream release: 1.9.3 RC1.
  + Includes load.c fixes. Closes: #639959.
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
# See LICENSE.txt for permissions.
5
5
#++
6
6
 
7
 
require 'rubygems/user_interaction'
8
7
require 'rubygems/specification'
9
 
 
10
 
# :stopdoc:
11
 
module Gem
12
 
  autoload :SpecFetcher, 'rubygems/spec_fetcher'
13
 
end
14
 
# :startdoc:
 
8
require 'rubygems/deprecate'
15
9
 
16
10
##
17
11
# The SourceIndex object indexes all the gems available from a
28
22
 
29
23
  include Enumerable
30
24
 
31
 
  include Gem::UserInteraction
32
 
 
33
25
  attr_reader :gems # :nodoc:
34
26
 
35
27
  ##
37
29
 
38
30
  attr_accessor :spec_dirs
39
31
 
40
 
  class << self
41
 
    include Gem::UserInteraction
42
 
 
43
 
    ##
44
 
    # Factory method to construct a source index instance for a given
45
 
    # path.
46
 
    #
47
 
    # deprecated::
48
 
    #   If supplied, from_installed_gems will act just like
49
 
    #   +from_gems_in+.  This argument is deprecated and is provided
50
 
    #   just for backwards compatibility, and should not generally
51
 
    #   be used.
52
 
    #
53
 
    # return::
54
 
    #   SourceIndex instance
55
 
 
56
 
    def from_installed_gems(*deprecated)
57
 
      if deprecated.empty?
58
 
        from_gems_in(*installed_spec_directories)
59
 
      else
60
 
        from_gems_in(*deprecated) # HACK warn
61
 
      end
62
 
    end
63
 
 
64
 
    ##
65
 
    # Returns a list of directories from Gem.path that contain specifications.
66
 
 
67
 
    def installed_spec_directories
68
 
      Gem.path.collect { |dir| File.join(dir, "specifications") }
69
 
    end
70
 
 
71
 
    ##
72
 
    # Creates a new SourceIndex from the ruby format gem specifications in
73
 
    # +spec_dirs+.
74
 
 
75
 
    def from_gems_in(*spec_dirs)
76
 
      source_index = new
77
 
      source_index.spec_dirs = spec_dirs
78
 
      source_index.refresh!
79
 
    end
80
 
 
81
 
    ##
82
 
    # Loads a ruby-format specification from +file_name+ and returns the
83
 
    # loaded spec.
84
 
 
85
 
    def load_specification(file_name)
86
 
      return nil unless file_name and File.exist? file_name
87
 
 
88
 
      spec_code = if defined? Encoding then
89
 
                    File.read file_name, :encoding => 'UTF-8'
90
 
                  else
91
 
                    File.read file_name
92
 
                  end.untaint
93
 
 
94
 
      begin
95
 
        gemspec = eval spec_code, binding, file_name
96
 
 
97
 
        if gemspec.is_a?(Gem::Specification)
98
 
          gemspec.loaded_from = file_name
99
 
          return gemspec
100
 
        end
101
 
        alert_warning "File '#{file_name}' does not evaluate to a gem specification"
102
 
      rescue SignalException, SystemExit
103
 
        raise
104
 
      rescue SyntaxError => e
105
 
        alert_warning e
106
 
        alert_warning spec_code
107
 
      rescue Exception => e
108
 
        alert_warning "#{e.inspect}\n#{spec_code}"
109
 
        alert_warning "Invalid .gemspec format in '#{file_name}'"
110
 
      end
111
 
 
112
 
      return nil
113
 
    end
114
 
 
 
32
  ##
 
33
  # Factory method to construct a source index instance for a given
 
34
  # path.
 
35
  #
 
36
  # deprecated::
 
37
  #   If supplied, from_installed_gems will act just like
 
38
  #   +from_gems_in+.  This argument is deprecated and is provided
 
39
  #   just for backwards compatibility, and should not generally
 
40
  #   be used.
 
41
  #
 
42
  # return::
 
43
  #   SourceIndex instance
 
44
 
 
45
  def self.from_installed_gems(*deprecated)
 
46
    if deprecated.empty?
 
47
      from_gems_in(*installed_spec_directories)
 
48
    else
 
49
      warn "NOTE: from_installed_gems(arg) is deprecated. From #{caller.first}"
 
50
      from_gems_in(*deprecated) # HACK warn
 
51
    end
 
52
  end
 
53
 
 
54
  ##
 
55
  # Returns a list of directories from Gem.path that contain specifications.
 
56
 
 
57
  def self.installed_spec_directories
 
58
    # TODO: move to Gem::Utils
 
59
    Gem.path.collect { |dir| File.join(dir, "specifications") }
 
60
  end
 
61
 
 
62
  ##
 
63
  # Creates a new SourceIndex from the ruby format gem specifications in
 
64
  # +spec_dirs+.
 
65
 
 
66
  def self.from_gems_in(*spec_dirs)
 
67
    new spec_dirs
 
68
  end
 
69
 
 
70
  ##
 
71
  # Loads a ruby-format specification from +file_name+ and returns the
 
72
  # loaded spec.
 
73
 
 
74
  def self.load_specification(file_name)
 
75
    Deprecate.skip_during do
 
76
      Gem::Specification.load Gem::Path.new(file_name)
 
77
    end
115
78
  end
116
79
 
117
80
  ##
118
81
  # Constructs a source index instance from the provided specifications, which
119
82
  # is a Hash of gem full names and Gem::Specifications.
120
 
  #--
121
 
  # TODO merge @gems and @prerelease_gems and provide a separate method
122
 
  # #prerelease_gems
123
83
 
124
 
  def initialize(specifications={})
 
84
  def initialize specs_or_dirs = []
125
85
    @gems = {}
126
 
    specifications.each{ |full_name, spec| add_spec spec }
127
86
    @spec_dirs = nil
 
87
 
 
88
    case specs_or_dirs
 
89
    when Hash then
 
90
      specs_or_dirs.each do |full_name, spec|
 
91
        add_spec spec
 
92
      end
 
93
    when Array, String then
 
94
      self.spec_dirs = Array(specs_or_dirs)
 
95
      refresh!
 
96
    else
 
97
      arg = specs_or_dirs.inspect
 
98
      warn "NOTE: SourceIndex.new(#{arg}) is deprecated; From #{caller.first}."
 
99
    end
128
100
  end
129
101
 
130
 
  # TODO: remove method
131
102
  def all_gems
132
 
    @gems
 
103
    gems
133
104
  end
134
105
 
135
106
  def prerelease_gems
136
 
    @gems.reject{ |name, gem| !gem.version.prerelease? }
 
107
    @gems.reject { |name, gem| !gem.version.prerelease? }
137
108
  end
138
109
 
139
110
  def released_gems
140
 
    @gems.reject{ |name, gem| gem.version.prerelease? }
 
111
    @gems.reject { |name, gem| gem.version.prerelease? }
141
112
  end
142
113
 
143
114
  ##
147
118
    @gems.clear
148
119
 
149
120
    spec_dirs.reverse_each do |spec_dir|
150
 
      spec_files = Dir.glob File.join(spec_dir, '*.gemspec')
 
121
      spec_files = Dir[File.join(spec_dir, "*.gemspec")]
151
122
 
152
123
      spec_files.each do |spec_file|
153
 
        gemspec = self.class.load_specification spec_file.untaint
 
124
        gemspec = Deprecate.skip_during do
 
125
          Gem::Specification.load spec_file
 
126
        end
154
127
        add_spec gemspec if gemspec
155
128
      end
156
129
    end
162
135
  # Returns an Array specifications for the latest released versions
163
136
  # of each gem in this index.
164
137
 
165
 
  def latest_specs
 
138
  def latest_specs(include_prerelease=false)
166
139
    result = Hash.new { |h,k| h[k] = [] }
167
140
    latest = {}
168
141
 
171
144
      curr_ver = spec.version
172
145
      prev_ver = latest.key?(name) ? latest[name].version : nil
173
146
 
174
 
      next if curr_ver.prerelease?
 
147
      next if !include_prerelease && curr_ver.prerelease?
175
148
      next unless prev_ver.nil? or curr_ver >= prev_ver or
176
149
                  latest[name].platform != Gem::Platform::RUBY
177
150
 
190
163
      result[name] << spec
191
164
    end
192
165
 
193
 
    # TODO: why is this a hash while @gems is an array? Seems like
194
 
    # structural similarity would be good.
195
166
    result.values.flatten
196
167
  end
197
168
 
222
193
  # Add gem specifications to the source index.
223
194
 
224
195
  def add_specs(*gem_specs)
225
 
    gem_specs.each do |spec|
226
 
      add_spec spec
 
196
    Deprecate.skip_during do
 
197
      gem_specs.each do |spec|
 
198
        add_spec spec
 
199
      end
227
200
    end
228
201
  end
229
202
 
275
248
  ##
276
249
  # Find a gem by an exact match on the short name.
277
250
 
278
 
  def find_name(gem_name, version_requirement = Gem::Requirement.default)
279
 
    dep = Gem::Dependency.new gem_name, version_requirement
280
 
    search dep
 
251
  def find_name(gem_name, requirement = Gem::Requirement.default)
 
252
    dep = Gem::Dependency.new gem_name, requirement
 
253
 
 
254
    Deprecate.skip_during do
 
255
      search dep
 
256
    end
281
257
  end
282
258
 
283
259
  ##
289
265
  # +gem_pattern+, and a Gem::Requirement for +platform_only+.  This
290
266
  # behavior is deprecated and will be removed.
291
267
 
292
 
  def search(gem_pattern, platform_only = false)
293
 
    version_requirement = nil
294
 
    only_platform = false
 
268
  def search(gem_pattern, platform_or_requirement = false)
 
269
    requirement = nil
 
270
    only_platform = false # FIX: WTF is this?!?
295
271
 
296
272
    # TODO - Remove support and warning for legacy arguments after 2008/11
297
273
    unless Gem::Dependency === gem_pattern
300
276
 
301
277
    case gem_pattern
302
278
    when Regexp then
303
 
      version_requirement = platform_only || Gem::Requirement.default
 
279
      requirement = platform_or_requirement || Gem::Requirement.default
304
280
    when Gem::Dependency then
305
 
      only_platform = platform_only
306
 
      version_requirement = gem_pattern.requirement
 
281
      only_platform = platform_or_requirement
 
282
      requirement = gem_pattern.requirement
 
283
 
307
284
      gem_pattern = if Regexp === gem_pattern.name then
308
285
                      gem_pattern.name
309
286
                    elsif gem_pattern.name.empty? then
312
289
                      /^#{Regexp.escape gem_pattern.name}$/
313
290
                    end
314
291
    else
315
 
      version_requirement = platform_only || Gem::Requirement.default
 
292
      requirement = platform_or_requirement || Gem::Requirement.default
316
293
      gem_pattern = /#{gem_pattern}/i
317
294
    end
318
295
 
319
 
    unless Gem::Requirement === version_requirement then
320
 
      version_requirement = Gem::Requirement.create version_requirement
 
296
    unless Gem::Requirement === requirement then
 
297
      requirement = Gem::Requirement.create requirement
321
298
    end
322
299
 
323
 
    specs = all_gems.values.select do |spec|
 
300
    specs = @gems.values.select do |spec|
324
301
      spec.name =~ gem_pattern and
325
 
        version_requirement.satisfied_by? spec.version
 
302
        requirement.satisfied_by? spec.version
326
303
    end
327
304
 
328
305
    if only_platform then
354
331
    latest_specs.each do |local|
355
332
      dependency = Gem::Dependency.new local.name, ">= #{local.version}"
356
333
 
357
 
      begin
358
 
        fetcher = Gem::SpecFetcher.fetcher
359
 
        remotes = fetcher.find_matching dependency
360
 
        remotes = remotes.map { |(name, version,_),_| version }
361
 
      rescue Gem::RemoteFetcher::FetchError => e
362
 
        raise unless fetcher.warn_legacy e do
363
 
          require 'rubygems/source_info_cache'
364
 
 
365
 
          specs = Gem::SourceInfoCache.search_with_source dependency, true
366
 
 
367
 
          remotes = specs.map { |spec,| spec.version }
368
 
        end
369
 
      end
 
334
      fetcher = Gem::SpecFetcher.fetcher
 
335
      remotes = fetcher.find_matching dependency
 
336
      remotes = remotes.map { |(_, version, _), _| version }
370
337
 
371
338
      latest = remotes.sort.last
372
339
 
376
343
    outdateds
377
344
  end
378
345
 
379
 
  ##
380
 
  # Updates this SourceIndex from +source_uri+.  If +all+ is false, only the
381
 
  # latest gems are fetched.
382
 
 
383
 
  def update(source_uri, all)
384
 
    source_uri = URI.parse source_uri unless URI::Generic === source_uri
385
 
    source_uri.path += '/' unless source_uri.path =~ /\/$/
386
 
 
387
 
    use_incremental = false
388
 
 
389
 
    begin
390
 
      gem_names = fetch_quick_index source_uri, all
391
 
      remove_extra gem_names
392
 
      missing_gems = find_missing gem_names
393
 
 
394
 
      return false if missing_gems.size.zero?
395
 
 
396
 
      say "Missing metadata for #{missing_gems.size} gems" if
397
 
      missing_gems.size > 0 and Gem.configuration.really_verbose
398
 
 
399
 
      use_incremental = missing_gems.size <= Gem.configuration.bulk_threshold
400
 
    rescue Gem::OperationNotSupportedError => ex
401
 
      alert_error "Falling back to bulk fetch: #{ex.message}" if
402
 
      Gem.configuration.really_verbose
403
 
      use_incremental = false
404
 
    end
405
 
 
406
 
    if use_incremental then
407
 
      update_with_missing(source_uri, missing_gems)
408
 
    else
409
 
      new_index = fetch_bulk_index(source_uri)
410
 
      @gems.replace(new_index.gems)
411
 
    end
412
 
 
413
 
    true
414
 
  end
415
 
 
416
346
  def ==(other) # :nodoc:
417
347
    self.class === other and @gems == other.gems
418
348
  end
420
350
  def dump
421
351
    Marshal.dump(self)
422
352
  end
423
 
 
424
 
  private
425
 
 
426
 
  def fetcher
427
 
    require 'rubygems/remote_fetcher'
428
 
 
429
 
    Gem::RemoteFetcher.fetcher
430
 
  end
431
 
 
432
 
  def fetch_index_from(source_uri)
433
 
    @fetch_error = nil
434
 
 
435
 
    indexes = %W[
436
 
        Marshal.#{Gem.marshal_version}.Z
437
 
        Marshal.#{Gem.marshal_version}
438
 
        yaml.Z
439
 
        yaml
440
 
      ]
441
 
 
442
 
    indexes.each do |name|
443
 
      spec_data = nil
444
 
      index = source_uri + name
445
 
      begin
446
 
        spec_data = fetcher.fetch_path index
447
 
        spec_data = unzip(spec_data) if name =~ /\.Z$/
448
 
 
449
 
        if name =~ /Marshal/ then
450
 
          return Marshal.load(spec_data)
451
 
        else
452
 
          return YAML.load(spec_data)
453
 
        end
454
 
      rescue => e
455
 
        if Gem.configuration.really_verbose then
456
 
          alert_error "Unable to fetch #{name}: #{e.message}"
457
 
        end
458
 
 
459
 
        @fetch_error = e
460
 
      end
461
 
    end
462
 
 
463
 
    nil
464
 
  end
465
 
 
466
 
  def fetch_bulk_index(source_uri)
467
 
    say "Bulk updating Gem source index for: #{source_uri}" if
468
 
      Gem.configuration.verbose
469
 
 
470
 
    index = fetch_index_from(source_uri)
471
 
    if index.nil? then
472
 
      raise Gem::RemoteSourceException,
473
 
              "Error fetching remote gem cache: #{@fetch_error}"
474
 
    end
475
 
    @fetch_error = nil
476
 
    index
477
 
  end
478
 
 
479
 
  ##
480
 
  # Get the quick index needed for incremental updates.
481
 
 
482
 
  def fetch_quick_index(source_uri, all)
483
 
    index = all ? 'index' : 'latest_index'
484
 
 
485
 
    zipped_index = fetcher.fetch_path source_uri + "quick/#{index}.rz"
486
 
 
487
 
    unzip(zipped_index).split("\n")
488
 
  rescue ::Exception => e
489
 
    unless all then
490
 
      say "Latest index not found, using quick index" if
491
 
        Gem.configuration.really_verbose
492
 
 
493
 
      fetch_quick_index source_uri, true
494
 
    else
495
 
      raise Gem::OperationNotSupportedError,
496
 
            "No quick index found: #{e.message}"
497
 
    end
498
 
  end
499
 
 
500
 
  ##
501
 
  # Make a list of full names for all the missing gemspecs.
502
 
 
503
 
  def find_missing(spec_names)
504
 
    unless defined? @originals then
505
 
      @originals = {}
506
 
      each do |full_name, spec|
507
 
        @originals[spec.original_name] = spec
508
 
      end
509
 
    end
510
 
 
511
 
    spec_names.find_all { |full_name|
512
 
      @originals[full_name].nil?
513
 
    }
514
 
  end
515
 
 
516
 
  def remove_extra(spec_names)
517
 
    dictionary = spec_names.inject({}) { |h, k| h[k] = true; h }
518
 
    each do |name, spec|
519
 
      remove_spec name unless dictionary.include? spec.original_name
520
 
    end
521
 
  end
522
 
 
523
 
  ##
524
 
  # Unzip the given string.
525
 
 
526
 
  def unzip(string)
527
 
    require 'zlib'
528
 
    Gem.inflate string
529
 
  end
530
 
 
531
 
  ##
532
 
  # Tries to fetch Marshal representation first, then YAML
533
 
 
534
 
  def fetch_single_spec(source_uri, spec_name)
535
 
    @fetch_error = nil
536
 
 
537
 
    begin
538
 
      marshal_uri = source_uri + "quick/Marshal.#{Gem.marshal_version}/#{spec_name}.gemspec.rz"
539
 
      zipped = fetcher.fetch_path marshal_uri
540
 
      return Marshal.load(unzip(zipped))
541
 
    rescue => ex
542
 
      @fetch_error = ex
543
 
 
544
 
      if Gem.configuration.really_verbose then
545
 
        say "unable to fetch marshal gemspec #{marshal_uri}: #{ex.class} - #{ex}"
546
 
      end
547
 
    end
548
 
 
549
 
    begin
550
 
      yaml_uri = source_uri + "quick/#{spec_name}.gemspec.rz"
551
 
      zipped = fetcher.fetch_path yaml_uri
552
 
      return YAML.load(unzip(zipped))
553
 
    rescue => ex
554
 
      @fetch_error = ex
555
 
      if Gem.configuration.really_verbose then
556
 
        say "unable to fetch YAML gemspec #{yaml_uri}: #{ex.class} - #{ex}"
557
 
      end
558
 
    end
559
 
 
560
 
    nil
561
 
  end
562
 
 
563
 
  ##
564
 
  # Update the cached source index with the missing names.
565
 
 
566
 
  def update_with_missing(source_uri, missing_names)
567
 
    progress = ui.progress_reporter(missing_names.size,
568
 
        "Updating metadata for #{missing_names.size} gems from #{source_uri}")
569
 
    missing_names.each do |spec_name|
570
 
      gemspec = fetch_single_spec(source_uri, spec_name)
571
 
      if gemspec.nil? then
572
 
        ui.say "Failed to download spec #{spec_name} from #{source_uri}:\n" \
573
 
                 "\t#{@fetch_error.message}"
574
 
      else
575
 
        add_spec gemspec
576
 
        progress.updated spec_name
577
 
      end
578
 
      @fetch_error = nil
579
 
    end
580
 
    progress.done
581
 
    progress.count
582
 
  end
583
 
 
584
353
end
585
354
 
586
355
# :stopdoc:
593
362
  Cache = SourceIndex
594
363
 
595
364
end
 
365
 
 
366
class Gem::SourceIndex
 
367
  extend Deprecate
 
368
 
 
369
  deprecate :all_gems,         :none,                        2011, 10
 
370
 
 
371
  deprecate :==,               :none,                        2011, 11 # noisy
 
372
  deprecate :add_specs,        :none,                        2011, 11 # noisy
 
373
  deprecate :each,             :none,                        2011, 11
 
374
  deprecate :gems,             :none,                        2011, 11
 
375
  deprecate :load_gems_in,     :none,                        2011, 11
 
376
  deprecate :refresh!,         :none,                        2011, 11
 
377
  deprecate :spec_dirs=,       "Specification.dirs=",        2011, 11 # noisy
 
378
  deprecate :add_spec,         "Specification.add_spec",     2011, 11
 
379
  deprecate :find_name,        "Specification.find_by_name", 2011, 11
 
380
  deprecate :gem_signature,    :none,                        2011, 11
 
381
  deprecate :index_signature,  :none,                        2011, 11
 
382
  deprecate :initialize,       :none,                        2011, 11
 
383
  deprecate :latest_specs,     "Specification.latest_specs", 2011, 11
 
384
  deprecate :length,           "Specification.all.length",   2011, 11
 
385
  deprecate :outdated,         :none,                        2011, 11
 
386
  deprecate :prerelease_gems,  :none,                        2011, 11
 
387
  deprecate :prerelease_specs, :none,                        2011, 11
 
388
  deprecate :released_gems,    :none,                        2011, 11
 
389
  deprecate :released_specs,   :none,                        2011, 11
 
390
  deprecate :remove_spec,      "Specification.remove_spec",  2011, 11
 
391
  deprecate :search,           :none,                        2011, 11
 
392
  deprecate :size,             "Specification.all.size",     2011, 11
 
393
  deprecate :spec_dirs,        "Specification.dirs",         2011, 11
 
394
  deprecate :specification,    "Specification.find",         2011, 11
 
395
 
 
396
  class << self
 
397
    extend Deprecate
 
398
 
 
399
    deprecate :from_gems_in,               :none,                2011, 10
 
400
    deprecate :from_installed_gems,        :none,                2011, 10
 
401
    deprecate :installed_spec_directories, "Specification.dirs", 2011, 11
 
402
    deprecate :load_specification,         :none,                2011, 10
 
403
  end
 
404
end
 
405
 
596
406
# :startdoc:
597