bundler 0.9.26 → 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bundler might be problematic. Click here for more details.
- data/CHANGELOG.md +7 -0
- data/README.md +3 -1
- data/ROADMAP.md +19 -34
- data/TODO.md +16 -0
- data/lib/bundler.rb +87 -41
- data/lib/bundler/cli.rb +115 -73
- data/lib/bundler/definition.rb +203 -34
- data/lib/bundler/dependency.rb +77 -0
- data/lib/bundler/dsl.rb +57 -16
- data/lib/bundler/environment.rb +13 -109
- data/lib/bundler/graph.rb +130 -0
- data/lib/bundler/index.rb +19 -41
- data/lib/bundler/installer.rb +22 -58
- data/lib/bundler/lazy_specification.rb +67 -0
- data/lib/bundler/lockfile_parser.rb +100 -0
- data/lib/bundler/remote_specification.rb +2 -1
- data/lib/bundler/resolver.rb +185 -45
- data/lib/bundler/rubygems_ext.rb +105 -6
- data/lib/bundler/runtime.rb +24 -82
- data/lib/bundler/settings.rb +9 -5
- data/lib/bundler/setup.rb +6 -14
- data/lib/bundler/shared_helpers.rb +2 -25
- data/lib/bundler/source.rb +306 -132
- data/lib/bundler/spec_set.rb +77 -17
- data/lib/bundler/templates/Executable +28 -0
- data/lib/bundler/vendor/thor.rb +31 -4
- data/lib/bundler/vendor/thor/invocation.rb +1 -1
- data/lib/bundler/vendor/thor/parser/arguments.rb +14 -3
- data/lib/bundler/vendor/thor/parser/options.rb +0 -5
- data/lib/bundler/vendor/thor/shell/basic.rb +28 -0
- data/lib/bundler/vendor/thor/task.rb +6 -6
- data/lib/bundler/vendor/thor/util.rb +13 -14
- data/lib/bundler/vendor/thor/version.rb +1 -1
- data/lib/bundler/version.rb +1 -1
- metadata +11 -5
- data/lib/bundler/templates/environment.erb +0 -91
data/lib/bundler/definition.rb
CHANGED
@@ -2,74 +2,243 @@ require "digest/sha1"
|
|
2
2
|
|
3
3
|
module Bundler
|
4
4
|
class Definition
|
5
|
-
|
5
|
+
attr_reader :dependencies, :platforms
|
6
|
+
|
7
|
+
def self.build(gemfile, lockfile, unlock)
|
8
|
+
unlock ||= {}
|
6
9
|
gemfile = Pathname.new(gemfile).expand_path
|
7
10
|
|
8
11
|
unless gemfile.file?
|
9
12
|
raise GemfileNotFound, "#{gemfile} not found"
|
10
13
|
end
|
11
14
|
|
12
|
-
|
15
|
+
# TODO: move this back into DSL
|
16
|
+
builder = Dsl.new
|
17
|
+
builder.instance_eval(File.read(gemfile.to_s), gemfile.to_s, 1)
|
18
|
+
builder.to_definition(lockfile, unlock)
|
13
19
|
end
|
14
20
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
=begin
|
22
|
+
How does the new system work?
|
23
|
+
===
|
24
|
+
* Load information from Gemfile and Lockfile
|
25
|
+
* Invalidate stale locked specs
|
26
|
+
* All specs from stale source are stale
|
27
|
+
* All specs that are reachable only through a stale
|
28
|
+
dependency are stale.
|
29
|
+
* If all fresh dependencies are satisfied by the locked
|
30
|
+
specs, then we can try to resolve locally.
|
31
|
+
=end
|
32
|
+
|
33
|
+
def initialize(lockfile, dependencies, sources, unlock)
|
34
|
+
@dependencies, @sources, @unlock = dependencies, sources, unlock
|
35
|
+
@specs = nil
|
36
|
+
@unlock[:gems] ||= []
|
37
|
+
@unlock[:sources] ||= []
|
38
|
+
|
39
|
+
if lockfile && File.exists?(lockfile)
|
40
|
+
locked = LockfileParser.new(File.read(lockfile))
|
41
|
+
@platforms = locked.platforms
|
42
|
+
@locked_deps = locked.dependencies
|
43
|
+
@last_resolve = SpecSet.new(locked.specs)
|
44
|
+
@locked_sources = locked.sources
|
45
|
+
else
|
46
|
+
@platforms = []
|
47
|
+
@locked_deps = []
|
48
|
+
@last_resolve = SpecSet.new([])
|
49
|
+
@locked_sources = []
|
21
50
|
end
|
22
51
|
|
23
|
-
|
24
|
-
|
25
|
-
|
52
|
+
current_platform = Gem.platforms.map { |p| p.to_generic }.compact.last
|
53
|
+
@platforms |= [current_platform]
|
54
|
+
|
55
|
+
converge
|
56
|
+
end
|
57
|
+
|
58
|
+
def resolve_remotely!
|
59
|
+
raise "Specs already loaded" if @specs
|
60
|
+
@sources.each { |s| s.remote! }
|
61
|
+
specs
|
62
|
+
end
|
63
|
+
|
64
|
+
def specs
|
65
|
+
@specs ||= resolve.materialize(requested_dependencies)
|
66
|
+
end
|
67
|
+
|
68
|
+
def missing_specs
|
69
|
+
missing = []
|
70
|
+
resolve.materialize(requested_dependencies, missing)
|
71
|
+
missing
|
72
|
+
end
|
73
|
+
|
74
|
+
def requested_specs
|
75
|
+
@requested_specs ||= begin
|
76
|
+
groups = self.groups - Bundler.settings.without
|
77
|
+
groups.map! { |g| g.to_sym }
|
78
|
+
specs_for(groups)
|
26
79
|
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def current_dependencies
|
83
|
+
dependencies.reject { |d| !d.should_include? }
|
84
|
+
end
|
27
85
|
|
28
|
-
|
86
|
+
def specs_for(groups)
|
87
|
+
deps = dependencies.select { |d| (d.groups & groups).any? }
|
88
|
+
deps.delete_if { |d| !d.should_include? }
|
89
|
+
specs.for(expand_dependencies(deps))
|
29
90
|
end
|
30
91
|
|
31
|
-
|
92
|
+
def resolve
|
93
|
+
@resolve ||= begin
|
94
|
+
if @last_resolve.valid_for?(expanded_dependencies)
|
95
|
+
@last_resolve
|
96
|
+
else
|
97
|
+
source_requirements = {}
|
98
|
+
dependencies.each do |dep|
|
99
|
+
next unless dep.source
|
100
|
+
source_requirements[dep.name] = dep.source.specs
|
101
|
+
end
|
32
102
|
|
33
|
-
|
103
|
+
# Run a resolve against the locally available gems
|
104
|
+
Resolver.resolve(expanded_dependencies, index, source_requirements, @last_resolve)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def index
|
110
|
+
@index ||= Index.build do |idx|
|
111
|
+
@sources.each do |s|
|
112
|
+
idx.use s.specs
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
34
116
|
|
35
|
-
def
|
36
|
-
@
|
37
|
-
@sources = sources
|
117
|
+
def no_sources?
|
118
|
+
@sources.length == 1 && @sources.first.remotes.empty?
|
38
119
|
end
|
39
120
|
|
40
121
|
def groups
|
41
122
|
dependencies.map { |d| d.groups }.flatten.uniq
|
42
123
|
end
|
43
124
|
|
44
|
-
|
45
|
-
|
46
|
-
|
125
|
+
def to_lock
|
126
|
+
out = ""
|
127
|
+
|
128
|
+
sorted_sources.each do |source|
|
129
|
+
# Add the source header
|
130
|
+
out << source.to_lock
|
131
|
+
# Find all specs for this source
|
132
|
+
resolve.
|
133
|
+
select { |s| s.source == source }.
|
134
|
+
sort_by { |s| [s.name, s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s] }.
|
135
|
+
each do |spec|
|
136
|
+
out << spec.to_lock
|
137
|
+
end
|
138
|
+
out << "\n"
|
139
|
+
end
|
140
|
+
|
141
|
+
out << "PLATFORMS\n"
|
142
|
+
|
143
|
+
platforms.map { |p| p.to_s }.sort.each do |p|
|
144
|
+
out << " #{p}\n"
|
145
|
+
end
|
146
|
+
|
147
|
+
out << "\n"
|
148
|
+
out << "DEPENDENCIES\n"
|
149
|
+
|
150
|
+
dependencies.
|
151
|
+
sort_by { |d| d.name }.
|
152
|
+
each do |dep|
|
153
|
+
out << dep.to_lock
|
47
154
|
end
|
48
155
|
|
49
|
-
|
50
|
-
|
156
|
+
out
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def converge
|
162
|
+
converge_sources
|
163
|
+
converge_dependencies
|
164
|
+
converge_locked_specs
|
165
|
+
end
|
166
|
+
|
167
|
+
def converge_sources
|
168
|
+
@sources = (@locked_sources & @sources) | @sources
|
169
|
+
@sources.each do |source|
|
170
|
+
source.unlock! if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name)
|
51
171
|
end
|
172
|
+
end
|
52
173
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
174
|
+
def converge_dependencies
|
175
|
+
(@dependencies + @locked_deps).each do |dep|
|
176
|
+
if dep.source
|
177
|
+
source = @sources.find { |s| dep.source == s }
|
178
|
+
raise "Something went wrong, there is no matching source" unless source
|
179
|
+
dep.source = source
|
57
180
|
end
|
58
181
|
end
|
182
|
+
end
|
59
183
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
184
|
+
def converge_locked_specs
|
185
|
+
deps = []
|
186
|
+
|
187
|
+
@dependencies.each do |dep|
|
188
|
+
if in_locked_deps?(dep) || satisfies_locked_spec?(dep)
|
189
|
+
deps << dep
|
65
190
|
end
|
66
191
|
end
|
67
192
|
|
68
|
-
|
69
|
-
|
70
|
-
|
193
|
+
converged = []
|
194
|
+
@last_resolve.each do |s|
|
195
|
+
s.source = @sources.find { |src| s.source == src }
|
196
|
+
|
197
|
+
next if s.source.nil? || @unlock[:sources].include?(s.name)
|
198
|
+
|
199
|
+
converged << s
|
200
|
+
end
|
201
|
+
|
202
|
+
resolve = SpecSet.new(converged)
|
203
|
+
resolve = resolve.for(expand_dependencies(deps), @unlock[:gems])
|
204
|
+
@last_resolve.select!(resolve.names)
|
205
|
+
end
|
206
|
+
|
207
|
+
def in_locked_deps?(dep)
|
208
|
+
@locked_deps.any? do |d|
|
209
|
+
dep == d && dep.source == d.source
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def satisfies_locked_spec?(dep)
|
214
|
+
@last_resolve.any? { |s| s.satisfies?(dep) }
|
215
|
+
end
|
216
|
+
|
217
|
+
def expanded_dependencies
|
218
|
+
@expanded_dependencies ||= expand_dependencies(dependencies)
|
219
|
+
end
|
220
|
+
|
221
|
+
def expand_dependencies(dependencies)
|
222
|
+
deps = []
|
223
|
+
dependencies.each do |dep|
|
224
|
+
dep.gem_platforms(@platforms).each do |p|
|
225
|
+
deps << DepProxy.new(dep, p)
|
71
226
|
end
|
72
227
|
end
|
228
|
+
deps
|
229
|
+
end
|
230
|
+
|
231
|
+
def sorted_sources
|
232
|
+
@sources.sort_by do |s|
|
233
|
+
# Place GEM at the top
|
234
|
+
[ s.is_a?(Source::Rubygems) ? 1 : 0, s.to_s ]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def requested_dependencies
|
239
|
+
groups = self.groups - Bundler.settings.without
|
240
|
+
groups.map! { |g| g.to_sym }
|
241
|
+
dependencies.reject { |d| !d.should_include? || (d.groups & groups).empty? }
|
73
242
|
end
|
74
243
|
end
|
75
244
|
end
|
data/lib/bundler/dependency.rb
CHANGED
@@ -5,6 +5,15 @@ module Bundler
|
|
5
5
|
class Dependency < Gem::Dependency
|
6
6
|
attr_reader :autorequire
|
7
7
|
attr_reader :groups
|
8
|
+
attr_reader :platforms
|
9
|
+
|
10
|
+
PLATFORM_MAP = {
|
11
|
+
:ruby => Gem::Platform::RUBY,
|
12
|
+
:ruby_18 => Gem::Platform::RUBY,
|
13
|
+
:ruby_19 => Gem::Platform::RUBY,
|
14
|
+
:jruby => Gem::Platform::JAVA,
|
15
|
+
:mswin => Gem::Platform::MSWIN
|
16
|
+
}
|
8
17
|
|
9
18
|
def initialize(name, version, options = {}, &blk)
|
10
19
|
super(name, version)
|
@@ -12,10 +21,78 @@ module Bundler
|
|
12
21
|
@autorequire = nil
|
13
22
|
@groups = Array(options["group"] || :default).map { |g| g.to_sym }
|
14
23
|
@source = options["source"]
|
24
|
+
@platforms = Array(options["platforms"])
|
25
|
+
@env = options["env"]
|
15
26
|
|
16
27
|
if options.key?('require')
|
17
28
|
@autorequire = Array(options['require'] || [])
|
18
29
|
end
|
19
30
|
end
|
31
|
+
|
32
|
+
def gem_platforms(valid_platforms)
|
33
|
+
return valid_platforms if @platforms.empty?
|
34
|
+
|
35
|
+
platforms = []
|
36
|
+
@platforms.each do |p|
|
37
|
+
platform = PLATFORM_MAP[p]
|
38
|
+
next unless valid_platforms.include?(platform)
|
39
|
+
platforms |= [platform]
|
40
|
+
end
|
41
|
+
platforms
|
42
|
+
end
|
43
|
+
|
44
|
+
def should_include?
|
45
|
+
current_env? && current_platform?
|
46
|
+
end
|
47
|
+
|
48
|
+
def current_env?
|
49
|
+
return true unless @env
|
50
|
+
if Hash === @env
|
51
|
+
@env.all? do |key, val|
|
52
|
+
ENV[key.to_s] && (String === val ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
ENV[@env.to_s]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def current_platform?
|
60
|
+
return true if @platforms.empty?
|
61
|
+
@platforms.any? { |p| send("#{p}?") }
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_lock
|
65
|
+
out = " #{name}"
|
66
|
+
|
67
|
+
unless requirement == Gem::Requirement.default
|
68
|
+
out << " (#{requirement.to_s})"
|
69
|
+
end
|
70
|
+
|
71
|
+
out << '!' if source
|
72
|
+
|
73
|
+
out << "\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def ruby?
|
79
|
+
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx")
|
80
|
+
end
|
81
|
+
|
82
|
+
def ruby_18?
|
83
|
+
ruby? && RUBY_VERSION < "1.9"
|
84
|
+
end
|
85
|
+
|
86
|
+
def ruby_19?
|
87
|
+
ruby? && RUBY_VERSION >= "1.9"
|
88
|
+
end
|
89
|
+
|
90
|
+
def jruby?
|
91
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
92
|
+
end
|
93
|
+
|
94
|
+
def mswin?
|
95
|
+
# w0t?
|
96
|
+
end
|
20
97
|
end
|
21
98
|
end
|
data/lib/bundler/dsl.rb
CHANGED
@@ -8,11 +8,16 @@ module Bundler
|
|
8
8
|
builder.to_definition
|
9
9
|
end
|
10
10
|
|
11
|
+
VALID_PLATFORMS = [:ruby_18, :ruby_19, :ruby, :jruby, :mswin]
|
12
|
+
|
11
13
|
def initialize
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@
|
14
|
+
@rubygems_source = Source::Rubygems.new
|
15
|
+
@source = nil
|
16
|
+
@sources = []
|
17
|
+
@dependencies = []
|
18
|
+
@groups = []
|
19
|
+
@platforms = []
|
20
|
+
@env = nil
|
16
21
|
end
|
17
22
|
|
18
23
|
def gem(name, *args)
|
@@ -29,12 +34,16 @@ module Bundler
|
|
29
34
|
end
|
30
35
|
|
31
36
|
def source(source, options = {})
|
32
|
-
|
33
|
-
when :gemcutter, :rubygems, :rubyforge then
|
34
|
-
|
35
|
-
|
37
|
+
case source
|
38
|
+
when :gemcutter, :rubygems, :rubyforge then
|
39
|
+
rubygems_source "http://rubygems.org"
|
40
|
+
return
|
41
|
+
when String
|
42
|
+
rubygems_source source
|
43
|
+
return
|
36
44
|
end
|
37
45
|
|
46
|
+
@source = source
|
38
47
|
options[:prepend] ? @sources.unshift(@source) : @sources << @source
|
39
48
|
|
40
49
|
yield if block_given?
|
@@ -51,15 +60,31 @@ module Bundler
|
|
51
60
|
source Source::Git.new(_normalize_hash(options).merge("uri" => uri)), source_options, &blk
|
52
61
|
end
|
53
62
|
|
54
|
-
def to_definition
|
55
|
-
|
63
|
+
def to_definition(lockfile, unlock)
|
64
|
+
@sources << @rubygems_source
|
65
|
+
@sources.uniq!
|
66
|
+
Definition.new(lockfile, @dependencies, @sources, unlock)
|
56
67
|
end
|
57
68
|
|
58
69
|
def group(*args, &blk)
|
59
|
-
|
70
|
+
@groups.concat args
|
71
|
+
yield
|
72
|
+
ensure
|
73
|
+
args.each { @groups.pop }
|
74
|
+
end
|
75
|
+
|
76
|
+
def platforms(*platforms)
|
77
|
+
@platforms.concat platforms
|
78
|
+
yield
|
79
|
+
ensure
|
80
|
+
platforms.each { @platforms.pop }
|
81
|
+
end
|
82
|
+
|
83
|
+
def env(name)
|
84
|
+
@env, old = name, @env
|
60
85
|
yield
|
61
86
|
ensure
|
62
|
-
@
|
87
|
+
@env = old
|
63
88
|
end
|
64
89
|
|
65
90
|
# Deprecated methods
|
@@ -87,6 +112,11 @@ module Bundler
|
|
87
112
|
|
88
113
|
private
|
89
114
|
|
115
|
+
def rubygems_source(source)
|
116
|
+
@rubygems_source.add_remote source
|
117
|
+
@sources << @rubygems_source
|
118
|
+
end
|
119
|
+
|
90
120
|
def _version?(version)
|
91
121
|
version && Gem::Version.new(version) rescue false
|
92
122
|
end
|
@@ -117,7 +147,17 @@ module Bundler
|
|
117
147
|
raise InvalidOption, message
|
118
148
|
end
|
119
149
|
|
120
|
-
|
150
|
+
groups = @groups.dup
|
151
|
+
groups.concat Array(opts.delete("group"))
|
152
|
+
groups = [:default] if groups.empty?
|
153
|
+
|
154
|
+
platforms = @platforms.dup
|
155
|
+
platforms.concat Array(opts.delete("platforms"))
|
156
|
+
platforms.map! { |p| p.to_sym }
|
157
|
+
platforms.each do |p|
|
158
|
+
next if VALID_PLATFORMS.include?(p)
|
159
|
+
raise DslError, "`#{p}` is not a valid platform. The available options are: #{VALID_PLATFORMS.inspect}"
|
160
|
+
end
|
121
161
|
|
122
162
|
# Normalize git and path options
|
123
163
|
["git", "path"].each do |type|
|
@@ -128,9 +168,10 @@ module Bundler
|
|
128
168
|
end
|
129
169
|
end
|
130
170
|
|
131
|
-
opts["source"]
|
132
|
-
|
133
|
-
opts["
|
171
|
+
opts["source"] ||= @source
|
172
|
+
opts["env"] ||= @env
|
173
|
+
opts["platforms"] = @platforms.dup
|
174
|
+
opts["group"] = groups
|
134
175
|
end
|
135
176
|
|
136
177
|
def _deprecated_options(options)
|