summaryrefslogtreecommitdiff
path: root/lib/rubygems/request_set.rb
blob: 6c52b90c4005d48e9396993e6b45d1b30b99e445 (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
require 'rubygems'
require 'rubygems/dependency'
require 'rubygems/dependency_resolver'
require 'rubygems/dependency_list'
require 'rubygems/installer'
require 'tsort'

module Gem
  class RequestSet

    include TSort

    def initialize(*deps)
      @dependencies = deps

      yield self if block_given?
    end

    attr_reader :dependencies

    # 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::Depedency objects to the set.
    #
    def import(deps)
      @dependencies += deps
    end

    # Resolve the requested dependencies and return an Array of
    # Specification objects to be activated.
    #
    def resolve(set=nil)
      r = Gem::DependencyResolver.new(@dependencies, set)
      @requests = r.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 DependencyResolver::CurrentSet.new
    end

    # Load a dependency management file.
    #
    def load_gemdeps(path)
      gf = GemDepedencyAPI.new(self, path)
      gf.load
    end

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

    def tsort_each_node(&block)
      @requests.each(&block)
    end

    def tsort_each_child(node)
      node.spec.dependencies.each do |dep|
        next if dep.type == :development

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

    def sorted_requests
      @sorted ||= strongly_connected_components.flatten
    end

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

    def install_into(dir, force=true, &b)
      existing = force ? [] : specs_in(dir)

      dir = File.expand_path dir

      installed = []

      sorted_requests.each do |req|
        if existing.find { |s| s.full_name == req.spec.full_name }
          b.call req, nil if b
          next
        end

        path = req.download(dir)

        inst = Gem::Installer.new path, :install_dir => dir,
                                        :only_install_dir => true

        b.call req, inst if b

        inst.install

        installed << req
      end

      installed
    end

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

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

      specs = []

      sorted_requests.each do |req|
        if req.installed?
          b.call req, nil if b
          next
        end

        path = req.download cache_dir

        inst = Gem::Installer.new path, options

        b.call req, inst if b

        specs << inst.install
      end

      specs
    end

    # A semi-compatible DSL for Bundler's Gemfile format
    #
    class GemDepedencyAPI
      def initialize(set, path)
        @set = set
        @path = path
      end

      def load
        instance_eval File.read(@path).untaint, @path, 1
      end

      # DSL

      def source(url)
      end

      def gem(name, *reqs)
        # Ignore the opts for now.
        reqs.pop if reqs.last.kind_of?(Hash)

        @set.gem name, *reqs
      end

      def platform(what)
        if what == :ruby
          yield
        end
      end

      alias_method :platforms, :platform

      def group(*what)
      end
    end
  end
end