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
|
require "digest/sha1"
# TODO: In the 0.10 release, there shouldn't be a locked subclass of Definition
module Bundler
class Definition
attr_reader :dependencies, :sources, :locked_specs
def self.build(gemfile, lockfile)
gemfile = Pathname.new(gemfile).expand_path
unless gemfile.file?
raise GemfileNotFound, "#{gemfile} not found"
end
# TODO: move this back into DSL
builder = Dsl.new
builder.instance_eval(File.read(gemfile.to_s), gemfile.to_s, 1)
builder.to_definition(lockfile)
end
def initialize(lockfile, dependencies, sources)
@dependencies, @sources = dependencies, sources
if lockfile && File.exists?(lockfile)
locked = LockfileParser.new(File.read(lockfile))
@locked_deps = locked.dependencies
@locked_specs = SpecSet.new(locked.specs)
@sources = locked.sources
else
@locked_deps = []
@locked_specs = SpecSet.new([])
end
end
def resolve_remotely!
@specs = resolve_remote_specs
end
def specs
@specs ||= resolve(:local_specs, index)
end
def index
@index ||= Index.build do |idx|
sources.each do |s|
idx.use s.local_specs
end
end
end
def remote_index
@remote_index ||= Index.build do |idx|
sources.each { |source| idx.use source.specs }
end
end
def no_sources?
sources.length == 1 && sources.first.remotes.empty?
end
# TODO: OMG LOL
def resolver_dependencies
@resolver_dependencies ||= begin
deps = locked_specs_as_deps
dependencies.each do |dep|
deps << dep unless deps.any? { |d| d.name == dep.name }
end
deps
end
end
def groups
dependencies.map { |d| d.groups }.flatten.uniq
end
private
# We have the dependencies from Gemfile.lock and the dependencies from the
# Gemfile. Here, we are finding a list of all dependencies that were
# originally present in the Gemfile that still satisfy the requirements
# of the dependencies in the Gemfile.lock
#
# This allows us to add on the *new* requirements in the Gemfile and make
# sure that the changes result in a conservative update to the Gemfile.lock.
def locked_specs_as_deps
deps = @dependencies & @locked_deps
@dependencies.each do |dep|
next if deps.include?(dep)
deps << dep if @locked_specs.any? { |s| s.satisfies?(dep) }
end
meta_deps = @locked_specs.for(deps).map do |s|
dep = Gem::Dependency.new(s.name, s.version)
@locked_deps.each do |d|
dep.source = d.source if d.name == dep.name
end
dep
end
end
def resolve(type, idx)
source_requirements = {}
resolver_dependencies.each do |dep|
next unless dep.source
source_requirements[dep.name] = dep.source.send(type)
end
# Run a resolve against the locally available gems
Resolver.resolve(resolver_dependencies, idx, source_requirements)
end
def resolve_remote_specs
# An ambiguous dependency is any dependency that does not have
# a requirement on an explicit version. If there are any, then
# we must do a remote resolve.
if resolver_dependencies.any? { |d| ambiguous?(d) }
return resolve(:specs, remote_index)
end
# Simple logic for now. Can improve later.
if specs.length == resolver_dependencies.length
return specs
else
return resolve(:specs, remote_index)
end
rescue GemNotFound, PathError => e
resolve(:specs, remote_index)
end
def ambiguous?(dep)
dep.requirement.requirements.any? { |op,_| op != '=' }
end
end
end
|