1require 'rubygems'
2require 'rubygems/dependency'
3require 'rubygems/dependency_resolver'
4require 'rubygems/dependency_list'
5require 'rubygems/installer'
6require 'tsort'
7
8module Gem
9  class RequestSet
10
11    include TSort
12
13    def initialize(*deps)
14      @dependencies = deps
15
16      yield self if block_given?
17    end
18
19    attr_reader :dependencies
20
21    # Declare that a gem of name +name+ with +reqs+ requirements
22    # is needed.
23    #
24    def gem(name, *reqs)
25      @dependencies << Gem::Dependency.new(name, reqs)
26    end
27
28    # Add +deps+ Gem::Depedency objects to the set.
29    #
30    def import(deps)
31      @dependencies += deps
32    end
33
34    # Resolve the requested dependencies and return an Array of
35    # Specification objects to be activated.
36    #
37    def resolve(set=nil)
38      r = Gem::DependencyResolver.new(@dependencies, set)
39      @requests = r.resolve
40    end
41
42    # Resolve the requested dependencies against the gems
43    # available via Gem.path and return an Array of Specification
44    # objects to be activated.
45    #
46    def resolve_current
47      resolve DependencyResolver::CurrentSet.new
48    end
49
50    # Load a dependency management file.
51    #
52    def load_gemdeps(path)
53      gf = GemDepedencyAPI.new(self, path)
54      gf.load
55    end
56
57    def specs
58      @specs ||= @requests.map { |r| r.full_spec }
59    end
60
61    def tsort_each_node(&block)
62      @requests.each(&block)
63    end
64
65    def tsort_each_child(node)
66      node.spec.dependencies.each do |dep|
67        next if dep.type == :development
68
69        match = @requests.find { |r| dep.match? r.spec.name, r.spec.version }
70        if match
71          begin
72            yield match
73          rescue TSort::Cyclic
74          end
75        else
76          raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
77        end
78      end
79    end
80
81    def sorted_requests
82      @sorted ||= strongly_connected_components.flatten
83    end
84
85    def specs_in(dir)
86      Dir["#{dir}/specifications/*.gemspec"].map do |g|
87        Gem::Specification.load g
88      end
89    end
90
91    def install_into(dir, force=true, &b)
92      existing = force ? [] : specs_in(dir)
93
94      dir = File.expand_path dir
95
96      installed = []
97
98      sorted_requests.each do |req|
99        if existing.find { |s| s.full_name == req.spec.full_name }
100          b.call req, nil if b
101          next
102        end
103
104        path = req.download(dir)
105
106        inst = Gem::Installer.new path, :install_dir => dir,
107                                        :only_install_dir => true
108
109        b.call req, inst if b
110
111        inst.install
112
113        installed << req
114      end
115
116      installed
117    end
118
119    def install(options, &b)
120      if dir = options[:install_dir]
121        return install_into(dir, false, &b)
122      end
123
124      cache_dir = options[:cache_dir] || Gem.dir
125
126      specs = []
127
128      sorted_requests.each do |req|
129        if req.installed?
130          b.call req, nil if b
131          next
132        end
133
134        path = req.download cache_dir
135
136        inst = Gem::Installer.new path, options
137
138        b.call req, inst if b
139
140        specs << inst.install
141      end
142
143      specs
144    end
145
146    # A semi-compatible DSL for Bundler's Gemfile format
147    #
148    class GemDepedencyAPI
149      def initialize(set, path)
150        @set = set
151        @path = path
152      end
153
154      def load
155        instance_eval File.read(@path).untaint, @path, 1
156      end
157
158      # DSL
159
160      def source(url)
161      end
162
163      def gem(name, *reqs)
164        # Ignore the opts for now.
165        reqs.pop if reqs.last.kind_of?(Hash)
166
167        @set.gem name, *reqs
168      end
169
170      def platform(what)
171        if what == :ruby
172          yield
173        end
174      end
175
176      alias_method :platforms, :platform
177
178      def group(*what)
179      end
180    end
181  end
182end
183