summaryrefslogtreecommitdiff
path: root/lib/rubygems/request_set.rb
blob: a45c64e0b4c6c713bb131a68ef1c86569ff008e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
require 'rubygems'
require 'rubygems/dependency'
require 'rubygems/dependency_resolver'
require 'rubygems/dependency_list'
require 'rubygems/installer'
require 'tsort'

class Gem::RequestSet

  include TSort

  ##
  # Array of gems to install even if already installed

  attr_reader :always_install

  attr_reader :dependencies

  attr_accessor :development

  ##
  # Treat missing dependencies as silent errors

  attr_accessor :soft_missing

  def initialize *deps
    @dependencies = deps

    @always_install = []
    @development    = false
    @requests       = []
    @soft_missing   = false
    @sorted         = nil
    @specs          = nil

    yield self if block_given?
  end

  ##
  # Declare that a gem of name +name+ with +reqs+ requirements is needed.

  def gem name, *reqs
    @dependencies << Gem::Dependency.new(name, reqs)
  end

  ##
  # Add +deps+ Gem::Dependency objects to the set.

  def import deps
    @dependencies += deps
  end

  def install options, &block
    if dir = options[:install_dir]
      return install_into dir, false, options, &block
    end

    cache_dir = options[:cache_dir] || Gem.dir

    specs = []

    sorted_requests.each do |req|
      if req.installed? and
         @always_install.none? { |spec| spec == req.spec.spec } then
        yield req, nil if block_given?
        next
      end

      path = req.download cache_dir

      inst = Gem::Installer.new path, options

      yield req, inst if block_given?

      specs << inst.install
    end

    specs
  end

  def install_into dir, force = true, options = {}
    existing = force ? [] : specs_in(dir)
    existing.delete_if { |s| @always_install.include? s }

    dir = File.expand_path dir

    installed = []

    sorted_requests.each do |req|
      if existing.find { |s| s.full_name == req.spec.full_name }
        yield req, nil if block_given?
        next
      end

      path = req.download(dir)

      unless path then # already installed
        yield req, nil if block_given?
        next
      end

      options[:install_dir] = dir
      options[:only_install_dir] = true

      inst = Gem::Installer.new path, options

      yield req, inst if block_given?

      inst.install

      installed << req
    end

    installed
  end

  ##
  # Load a dependency management file.

  def load_gemdeps path
    gf = Gem::RequestSet::GemDepedencyAPI.new self, path
    gf.load
  end

  ##
  # Resolve the requested dependencies and return an Array of Specification
  # objects to be activated.

  def resolve set = nil
    resolver = Gem::DependencyResolver.new @dependencies, set
    resolver.development  = @development
    resolver.soft_missing = @soft_missing

    @requests = resolver.resolve
  end

  ##
  # Resolve the requested dependencies against the gems available via Gem.path
  # and return an Array of Specification objects to be activated.

  def resolve_current
    resolve Gem::DependencyResolver::CurrentSet.new
  end

  def sorted_requests
    @sorted ||= strongly_connected_components.flatten
  end

  def specs
    @specs ||= @requests.map { |r| r.full_spec }
  end

  def specs_in dir
    Dir["#{dir}/specifications/*.gemspec"].map do |g|
      Gem::Specification.load g
    end
  end

  def tsort_each_node &block # :nodoc:
    @requests.each(&block)
  end

  def tsort_each_child node # :nodoc:
    node.spec.dependencies.each do |dep|
      next if dep.type == :development and not @development

      match = @requests.find { |r| dep.match? r.spec.name, r.spec.version }
      if match
        begin
          yield match
        rescue TSort::Cyclic
        end
      else
        unless @soft_missing
          raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
        end
      end
    end
  end

end

require 'rubygems/request_set/gem_dependency_api'