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