bundler 1.3.6 → 1.4.0.pre.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -3
- data/CHANGELOG.md +27 -14
- data/CONTRIBUTING.md +2 -2
- data/{CONTRIBUTE.md → DEVELOPMENT.md} +31 -12
- data/ISSUES.md +1 -1
- data/README.md +6 -4
- data/Rakefile +1 -15
- data/bin/bundle +5 -8
- data/bundler.gemspec +1 -1
- data/lib/bundler.rb +37 -21
- data/lib/bundler/cli.rb +33 -21
- data/lib/bundler/constants.rb +5 -0
- data/lib/bundler/current_ruby.rb +88 -0
- data/lib/bundler/definition.rb +35 -11
- data/lib/bundler/dependency.rb +7 -78
- data/lib/bundler/dsl.rb +1 -1
- data/lib/bundler/fetcher.rb +37 -24
- data/lib/bundler/gem_helper.rb +2 -2
- data/lib/bundler/gem_installer.rb +9 -0
- data/lib/bundler/installer.rb +76 -7
- data/lib/bundler/parallel_workers.rb +18 -0
- data/lib/bundler/parallel_workers/thread_worker.rb +27 -0
- data/lib/bundler/parallel_workers/unix_worker.rb +88 -0
- data/lib/bundler/parallel_workers/worker.rb +68 -0
- data/lib/bundler/resolver.rb +17 -11
- data/lib/bundler/rubygems_ext.rb +2 -2
- data/lib/bundler/rubygems_integration.rb +37 -25
- data/lib/bundler/runtime.rb +8 -1
- data/lib/bundler/safe_catch.rb +101 -0
- data/lib/bundler/shared_helpers.rb +27 -1
- data/lib/bundler/source/git.rb +2 -1
- data/lib/bundler/source/git/git_proxy.rb +3 -3
- data/lib/bundler/source/path.rb +3 -2
- data/lib/bundler/source/rubygems.rb +5 -17
- data/lib/bundler/spec_set.rb +16 -1
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
- data/lib/bundler/vendor/net/http/persistent.rb +136 -38
- data/lib/bundler/vendor/thor.rb +211 -188
- data/lib/bundler/vendor/thor/actions.rb +19 -19
- data/lib/bundler/vendor/thor/actions/create_link.rb +3 -0
- data/lib/bundler/vendor/thor/actions/directory.rb +30 -10
- data/lib/bundler/vendor/thor/actions/empty_directory.rb +3 -19
- data/lib/bundler/vendor/thor/actions/file_manipulation.rb +6 -3
- data/lib/bundler/vendor/thor/base.rb +101 -97
- data/lib/bundler/vendor/thor/{task.rb → command.rb} +17 -13
- data/lib/bundler/vendor/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/bundler/vendor/thor/error.rb +8 -11
- data/lib/bundler/vendor/thor/group.rb +35 -38
- data/lib/bundler/vendor/thor/invocation.rb +28 -26
- data/lib/bundler/vendor/thor/parser/options.rb +21 -19
- data/lib/bundler/vendor/thor/rake_compat.rb +3 -2
- data/lib/bundler/vendor/thor/runner.rb +22 -21
- data/lib/bundler/vendor/thor/shell/basic.rb +44 -22
- data/lib/bundler/vendor/thor/shell/color.rb +13 -9
- data/lib/bundler/vendor/thor/shell/html.rb +13 -9
- data/lib/bundler/vendor/thor/util.rb +214 -210
- data/lib/bundler/vendor/thor/version.rb +1 -1
- data/lib/bundler/version.rb +1 -1
- data/man/bundle-install.ronn +5 -1
- data/man/gemfile.5.ronn +10 -2
- data/spec/bundler/dsl_spec.rb +3 -3
- data/spec/bundler/gem_helper_spec.rb +14 -17
- data/spec/bundler/safe_catch_spec.rb +37 -0
- data/spec/install/gems/dependency_api_spec.rb +1 -36
- data/spec/install/gems/packed_spec.rb +4 -2
- data/spec/install/gems/resolving_spec.rb +37 -0
- data/spec/install/gems/simple_case_spec.rb +18 -16
- data/spec/install/git_spec.rb +1 -1
- data/spec/other/binstubs_spec.rb +24 -13
- data/spec/other/exec_spec.rb +24 -2
- data/spec/other/help_spec.rb +6 -6
- data/spec/other/outdated_spec.rb +3 -3
- data/spec/quality_spec.rb +3 -2
- data/spec/realworld/dependency_api_spec.rb +1 -1
- data/spec/realworld/edgecases_spec.rb +3 -3
- data/spec/realworld/parallel_install_spec.rb +19 -0
- data/spec/resolver/basic_spec.rb +11 -0
- data/spec/runtime/require_spec.rb +9 -0
- data/spec/runtime/setup_spec.rb +2 -3
- data/spec/spec_helper.rb +0 -1
- data/spec/support/builders.rb +2 -4
- data/spec/support/helpers.rb +4 -8
- data/spec/support/indexes.rb +18 -0
- data/spec/support/streams.rb +13 -0
- metadata +19 -11
- data/lib/bundler/vendor/thor/core_ext/dir_escape.rb +0 -0
- data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +0 -9
- data/spec/support/artifice/endpoint_host_redirect.rb +0 -15
- data/spec/support/permissions.rb +0 -11
@@ -0,0 +1,88 @@
|
|
1
|
+
module Bundler
|
2
|
+
# Returns current version of Ruby
|
3
|
+
#
|
4
|
+
# @return [CurrentRuby] Current version of Ruby
|
5
|
+
def self.current_ruby
|
6
|
+
@current_ruby ||= CurrentRuby.new
|
7
|
+
end
|
8
|
+
|
9
|
+
class CurrentRuby
|
10
|
+
def on_18?
|
11
|
+
RUBY_VERSION =~ /^1\.8/
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_19?
|
15
|
+
RUBY_VERSION =~ /^1\.9/
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_20?
|
19
|
+
RUBY_VERSION =~ /^2\.0/
|
20
|
+
end
|
21
|
+
|
22
|
+
def ruby?
|
23
|
+
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev")
|
24
|
+
end
|
25
|
+
|
26
|
+
def ruby_18?
|
27
|
+
ruby? && on_18?
|
28
|
+
end
|
29
|
+
|
30
|
+
def ruby_19?
|
31
|
+
ruby? && on_19?
|
32
|
+
end
|
33
|
+
|
34
|
+
def ruby_20?
|
35
|
+
ruby? && on_20?
|
36
|
+
end
|
37
|
+
|
38
|
+
def mri?
|
39
|
+
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby")
|
40
|
+
end
|
41
|
+
|
42
|
+
def mri_18?
|
43
|
+
mri? && on_18?
|
44
|
+
end
|
45
|
+
|
46
|
+
def mri_19?
|
47
|
+
mri? && on_19?
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def mri_20?
|
52
|
+
mri? && on_20?
|
53
|
+
end
|
54
|
+
|
55
|
+
def rbx?
|
56
|
+
ruby? && defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
|
57
|
+
end
|
58
|
+
|
59
|
+
def jruby?
|
60
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
61
|
+
end
|
62
|
+
|
63
|
+
def maglev?
|
64
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == "maglev"
|
65
|
+
end
|
66
|
+
|
67
|
+
def mswin?
|
68
|
+
Bundler::WINDOWS
|
69
|
+
end
|
70
|
+
|
71
|
+
def mingw?
|
72
|
+
Bundler::WINDOWS && Gem::Platform.local.os == "mingw32"
|
73
|
+
end
|
74
|
+
|
75
|
+
def mingw_18?
|
76
|
+
mingw? && on_18?
|
77
|
+
end
|
78
|
+
|
79
|
+
def mingw_19?
|
80
|
+
mingw? && on_19?
|
81
|
+
end
|
82
|
+
|
83
|
+
def mingw_20?
|
84
|
+
mingw? && on_20?
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
data/lib/bundler/definition.rb
CHANGED
@@ -8,6 +8,13 @@ module Bundler
|
|
8
8
|
attr_reader :dependencies, :platforms, :sources, :ruby_version,
|
9
9
|
:locked_deps
|
10
10
|
|
11
|
+
# Given a gemfile and lockfile creates a Bundler definition
|
12
|
+
#
|
13
|
+
# @param gemfile [Pathname] Path to Gemfile
|
14
|
+
# @param lockfile [Pathname,nil] Path to Gemfile.lock
|
15
|
+
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
16
|
+
# to be updated or true if all gems should be updated
|
17
|
+
# @return [Bundler::Definition]
|
11
18
|
def self.build(gemfile, lockfile, unlock)
|
12
19
|
unlock ||= {}
|
13
20
|
gemfile = Pathname.new(gemfile).expand_path
|
@@ -19,18 +26,24 @@ module Bundler
|
|
19
26
|
Dsl.evaluate(gemfile, lockfile, unlock)
|
20
27
|
end
|
21
28
|
|
22
|
-
=begin
|
23
|
-
How does the new system work?
|
24
|
-
===
|
25
|
-
* Load information from Gemfile and Lockfile
|
26
|
-
* Invalidate stale locked specs
|
27
|
-
* All specs from stale source are stale
|
28
|
-
* All specs that are reachable only through a stale
|
29
|
-
dependency are stale.
|
30
|
-
* If all fresh dependencies are satisfied by the locked
|
31
|
-
specs, then we can try to resolve locally.
|
32
|
-
=end
|
33
29
|
|
30
|
+
#
|
31
|
+
# How does the new system work?
|
32
|
+
#
|
33
|
+
# * Load information from Gemfile and Lockfile
|
34
|
+
# * Invalidate stale locked specs
|
35
|
+
# * All specs from stale source are stale
|
36
|
+
# * All specs that are reachable only through a stale
|
37
|
+
# dependency are stale.
|
38
|
+
# * If all fresh dependencies are satisfied by the locked
|
39
|
+
# specs, then we can try to resolve locally.
|
40
|
+
#
|
41
|
+
# @param lockfile [Pathname] Path to Gemfile.lock
|
42
|
+
# @param dependencies [Array(Bundler::Dependency)] array of dependencies from Gemfile
|
43
|
+
# @param sources [Array(Bundler::Source::Rubygems)]
|
44
|
+
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
45
|
+
# to be updated or true if all gems should be updated
|
46
|
+
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
|
34
47
|
def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil)
|
35
48
|
@unlocking = unlock == true || !unlock.empty?
|
36
49
|
|
@@ -109,6 +122,12 @@ module Bundler
|
|
109
122
|
specs
|
110
123
|
end
|
111
124
|
|
125
|
+
# For given dependency list returns a SpecSet with Gemspec of all the required
|
126
|
+
# dependencies.
|
127
|
+
# 1. The method first resolves the dependencies specified in Gemfile
|
128
|
+
# 2. After that it tries and fetches gemspec of resolved dependencies
|
129
|
+
#
|
130
|
+
# @return [Bundler::SpecSet]
|
112
131
|
def specs
|
113
132
|
@specs ||= begin
|
114
133
|
specs = resolve.materialize(requested_dependencies)
|
@@ -159,6 +178,11 @@ module Bundler
|
|
159
178
|
specs.for(expand_dependencies(deps))
|
160
179
|
end
|
161
180
|
|
181
|
+
# Resolve all the dependencies specified in Gemfile. It ensures that
|
182
|
+
# dependencies that have been already resolved via locked file and are fresh
|
183
|
+
# are reused when resolving dependencies
|
184
|
+
#
|
185
|
+
# @return [SpecSet] resolved dependencies
|
162
186
|
def resolve
|
163
187
|
@resolve ||= begin
|
164
188
|
if Bundler.settings[:frozen] || (!@unlocking && nothing_changed?)
|
data/lib/bundler/dependency.rb
CHANGED
@@ -70,7 +70,9 @@ module Bundler
|
|
70
70
|
|
71
71
|
def current_platform?
|
72
72
|
return true if @platforms.empty?
|
73
|
-
@platforms.any? { |p|
|
73
|
+
@platforms.any? { |p|
|
74
|
+
Bundler.current_ruby.send("#{p}?")
|
75
|
+
}
|
74
76
|
end
|
75
77
|
|
76
78
|
def to_lock
|
@@ -79,84 +81,11 @@ module Bundler
|
|
79
81
|
out << "\n"
|
80
82
|
end
|
81
83
|
|
82
|
-
private
|
83
84
|
|
84
|
-
def
|
85
|
-
|
85
|
+
def specific?
|
86
|
+
super
|
87
|
+
rescue NoMethodError
|
88
|
+
requirement != ">= 0"
|
86
89
|
end
|
87
|
-
|
88
|
-
def on_19?
|
89
|
-
RUBY_VERSION =~ /^1\.9/
|
90
|
-
end
|
91
|
-
|
92
|
-
def on_20?
|
93
|
-
RUBY_VERSION =~ /^2\.0/
|
94
|
-
end
|
95
|
-
|
96
|
-
def ruby?
|
97
|
-
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev")
|
98
|
-
end
|
99
|
-
|
100
|
-
def ruby_18?
|
101
|
-
ruby? && on_18?
|
102
|
-
end
|
103
|
-
|
104
|
-
def ruby_19?
|
105
|
-
ruby? && on_19?
|
106
|
-
end
|
107
|
-
|
108
|
-
def ruby_20?
|
109
|
-
ruby? && on_20?
|
110
|
-
end
|
111
|
-
|
112
|
-
def mri?
|
113
|
-
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby")
|
114
|
-
end
|
115
|
-
|
116
|
-
def mri_18?
|
117
|
-
mri? && on_18?
|
118
|
-
end
|
119
|
-
|
120
|
-
def mri_19?
|
121
|
-
mri? && on_19?
|
122
|
-
end
|
123
|
-
|
124
|
-
|
125
|
-
def mri_20?
|
126
|
-
mri? && on_20?
|
127
|
-
end
|
128
|
-
|
129
|
-
def rbx?
|
130
|
-
ruby? && defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
|
131
|
-
end
|
132
|
-
|
133
|
-
def jruby?
|
134
|
-
defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
135
|
-
end
|
136
|
-
|
137
|
-
def maglev?
|
138
|
-
defined?(RUBY_ENGINE) && RUBY_ENGINE == "maglev"
|
139
|
-
end
|
140
|
-
|
141
|
-
def mswin?
|
142
|
-
Bundler::WINDOWS
|
143
|
-
end
|
144
|
-
|
145
|
-
def mingw?
|
146
|
-
Bundler::WINDOWS && Gem::Platform.local.os == "mingw32"
|
147
|
-
end
|
148
|
-
|
149
|
-
def mingw_18?
|
150
|
-
mingw? && on_18?
|
151
|
-
end
|
152
|
-
|
153
|
-
def mingw_19?
|
154
|
-
mingw? && on_19?
|
155
|
-
end
|
156
|
-
|
157
|
-
def mingw_20?
|
158
|
-
mingw? && on_20?
|
159
|
-
end
|
160
|
-
|
161
90
|
end
|
162
91
|
end
|
data/lib/bundler/dsl.rb
CHANGED
@@ -230,7 +230,7 @@ module Bundler
|
|
230
230
|
|
231
231
|
if github = opts.delete("github")
|
232
232
|
github = "#{github}/#{github}" unless github.include?("/")
|
233
|
-
opts["git"] = "
|
233
|
+
opts["git"] = "https://github.com/#{github}.git"
|
234
234
|
end
|
235
235
|
|
236
236
|
if gist = opts.delete("gist")
|
data/lib/bundler/fetcher.rb
CHANGED
@@ -4,11 +4,6 @@ module Bundler
|
|
4
4
|
|
5
5
|
# Handles all the fetching with the rubygems server
|
6
6
|
class Fetcher
|
7
|
-
# How many redirects to allew in one request
|
8
|
-
REDIRECT_LIMIT = 5
|
9
|
-
# how long to wait for each gemcutter API call
|
10
|
-
API_TIMEOUT = 10
|
11
|
-
|
12
7
|
# This error is raised if the API returns a 413 (only printed in verbose)
|
13
8
|
class FallbackError < HTTPError; end
|
14
9
|
# This is the error raised if OpenSSL fails the cert verification
|
@@ -33,7 +28,7 @@ module Bundler
|
|
33
28
|
end
|
34
29
|
|
35
30
|
class << self
|
36
|
-
attr_accessor :disable_endpoint
|
31
|
+
attr_accessor :disable_endpoint, :api_timeout, :redirect_limit, :max_retries
|
37
32
|
|
38
33
|
@@spec_fetch_map ||= {}
|
39
34
|
|
@@ -65,6 +60,13 @@ module Bundler
|
|
65
60
|
end
|
66
61
|
|
67
62
|
def initialize(remote_uri)
|
63
|
+
# How many redirects to allew in one request
|
64
|
+
@redirect_limit = 5
|
65
|
+
# How long to wait for each gemcutter API call
|
66
|
+
@api_timeout = 10
|
67
|
+
# How many retries for the gemcutter API call
|
68
|
+
@max_retries = 3
|
69
|
+
|
68
70
|
@remote_uri = remote_uri
|
69
71
|
@public_uri = remote_uri.dup
|
70
72
|
@public_uri.user, @public_uri.password = nil, nil # don't print these
|
@@ -77,7 +79,7 @@ module Bundler
|
|
77
79
|
raise SSLError if @remote_uri.scheme == "https"
|
78
80
|
@connection = Net::HTTP.new(@remote_uri.host, @remote_uri.port)
|
79
81
|
end
|
80
|
-
@connection.read_timeout =
|
82
|
+
@connection.read_timeout = @api_timeout
|
81
83
|
|
82
84
|
Socket.do_not_reverse_lookup = true
|
83
85
|
end
|
@@ -85,17 +87,28 @@ module Bundler
|
|
85
87
|
# fetch a gem specification
|
86
88
|
def fetch_spec(spec)
|
87
89
|
spec = spec - [nil, 'ruby', '']
|
88
|
-
spec_file_name = "#{spec.join '-'}.gemspec
|
89
|
-
|
90
|
-
uri = URI.parse("#{@remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}")
|
90
|
+
spec_file_name = "#{spec.join '-'}.gemspec"
|
91
91
|
|
92
|
-
|
93
|
-
|
92
|
+
uri = URI.parse("#{@remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
|
93
|
+
if uri.scheme == 'file'
|
94
|
+
Bundler.load_marshal Gem.inflate(Gem.read_binary(uri.path))
|
95
|
+
elsif cached_spec_path = gemspec_cached_path(spec_file_name)
|
96
|
+
Bundler.load_gemspec(cached_spec_path)
|
97
|
+
else
|
98
|
+
Bundler.load_marshal Gem.inflate(fetch(uri))
|
99
|
+
end
|
94
100
|
rescue MarshalError => e
|
95
101
|
raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
|
96
102
|
"Your network or your gem server is probably having issues right now."
|
97
103
|
end
|
98
104
|
|
105
|
+
# cached gem specification path, if one exists
|
106
|
+
def gemspec_cached_path spec_file_name
|
107
|
+
paths = Bundler.rubygems.spec_cache_dirs.map { |dir| File.join(dir, spec_file_name) }
|
108
|
+
paths = paths.select {|path| File.file? path }
|
109
|
+
paths.first
|
110
|
+
end
|
111
|
+
|
99
112
|
# return the specs in the bundler format as an index
|
100
113
|
def specs(gem_names, source)
|
101
114
|
index = Index.new
|
@@ -157,14 +170,19 @@ module Bundler
|
|
157
170
|
# 2. Marshal blob doesn't load properly
|
158
171
|
# 3. One of the YAML gemspecs has the Syck::DefaultKey problem
|
159
172
|
rescue HTTPError, MarshalError, GemspecError => e
|
160
|
-
@use_api = false
|
161
|
-
|
162
173
|
# new line now that the dots are over
|
163
174
|
Bundler.ui.info "" unless Bundler.ui.debug?
|
164
175
|
|
165
176
|
Bundler.ui.debug "Error during API request. #{e.class}: #{e.message}"
|
166
177
|
Bundler.ui.debug e.backtrace.join(" ")
|
167
178
|
|
179
|
+
@current_retries ||= 0
|
180
|
+
if @current_retries < @max_retries
|
181
|
+
@current_retries += 1
|
182
|
+
retry
|
183
|
+
end
|
184
|
+
|
185
|
+
@use_api = false
|
168
186
|
return nil
|
169
187
|
end
|
170
188
|
|
@@ -194,21 +212,16 @@ module Bundler
|
|
194
212
|
HTTP_ERRORS << Net::HTTP::Persistent::Error if defined?(Net::HTTP::Persistent)
|
195
213
|
|
196
214
|
def fetch(uri, counter = 0)
|
197
|
-
raise HTTPError, "Too many redirects" if counter >=
|
215
|
+
raise HTTPError, "Too many redirects" if counter >= @redirect_limit
|
198
216
|
|
199
217
|
begin
|
200
218
|
Bundler.ui.debug "Fetching from: #{uri}"
|
219
|
+
req = Net::HTTP::Get.new uri.request_uri
|
220
|
+
req.basic_auth(uri.user, uri.password) if uri.user && uri.password
|
201
221
|
if defined?(Net::HTTP::Persistent)
|
202
|
-
response = @connection.request(uri)
|
222
|
+
response = @connection.request(uri, req)
|
203
223
|
else
|
204
|
-
|
205
|
-
req.basic_auth(uri.user, uri.password) if uri.user && uri.password
|
206
|
-
if uri.host == @connection.address && uri.port == @connection.port
|
207
|
-
connection = @connection
|
208
|
-
else
|
209
|
-
connection = Net::HTTP.new(uri.host, uri.port)
|
210
|
-
end
|
211
|
-
response = connection.request(req)
|
224
|
+
response = @connection.request(req)
|
212
225
|
end
|
213
226
|
rescue OpenSSL::SSL::SSLError
|
214
227
|
raise CertificateFailureError.new(@public_uri)
|
data/lib/bundler/gem_helper.rb
CHANGED
@@ -24,7 +24,7 @@ module Bundler
|
|
24
24
|
|
25
25
|
def initialize(base = nil, name = nil)
|
26
26
|
Bundler.ui = UI::Shell.new
|
27
|
-
@base = (base ||=
|
27
|
+
@base = (base ||= SharedHelpers.pwd)
|
28
28
|
gemspecs = name ? [File.join(base, "#{name}.gemspec")] : Dir[File.join(base, "{,*}.gemspec")]
|
29
29
|
raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
|
30
30
|
@spec_path = gemspecs.first
|
@@ -153,7 +153,7 @@ module Bundler
|
|
153
153
|
cmd << " 2>&1"
|
154
154
|
outbuf = ''
|
155
155
|
Bundler.ui.debug(cmd)
|
156
|
-
|
156
|
+
SharedHelpers.chdir(base) {
|
157
157
|
outbuf = `#{cmd}`
|
158
158
|
if $? == 0
|
159
159
|
block.call(outbuf) if block
|
@@ -5,5 +5,14 @@ module Bundler
|
|
5
5
|
def check_executable_overwrite(filename)
|
6
6
|
# Bundler needs to install gems regardless of binstub overwriting
|
7
7
|
end
|
8
|
+
|
9
|
+
if Bundler.current_ruby.mswin? || Bundler.current_ruby.jruby?
|
10
|
+
def build_extensions
|
11
|
+
# Gain the lock because rubygems use Dir.chdir
|
12
|
+
SharedHelpers.chdir('.') do
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
8
17
|
end
|
9
18
|
end
|
data/lib/bundler/installer.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'rubygems/dependency_installer'
|
3
|
+
require 'bundler/parallel_workers'
|
3
4
|
|
4
5
|
module Bundler
|
5
6
|
class Installer < Environment
|
@@ -87,8 +88,14 @@ module Bundler
|
|
87
88
|
# as dependencies might actually affect the installation of
|
88
89
|
# the gem.
|
89
90
|
Installer.post_install_messages = {}
|
90
|
-
|
91
|
-
|
91
|
+
|
92
|
+
size = options[:jobs] || 1
|
93
|
+
size = [size, 1].max
|
94
|
+
|
95
|
+
if size > 1 && can_install_parallely?
|
96
|
+
install_in_parallel size, options[:standalone]
|
97
|
+
else
|
98
|
+
install_sequentially options[:standalone]
|
92
99
|
end
|
93
100
|
|
94
101
|
lock
|
@@ -102,13 +109,12 @@ module Bundler
|
|
102
109
|
|
103
110
|
# Fetch the build settings, if there are any
|
104
111
|
settings = Bundler.settings["build.#{spec.name}"]
|
112
|
+
message = nil
|
105
113
|
Bundler.rubygems.with_build_args [settings] do
|
106
|
-
spec.source.install(spec)
|
107
|
-
Bundler.ui.debug "from #{spec.loaded_from}
|
114
|
+
message = spec.source.install(spec)
|
115
|
+
Bundler.ui.debug " #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
|
108
116
|
end
|
109
117
|
|
110
|
-
# newline comes after installing, some gems say "with native extensions"
|
111
|
-
Bundler.ui.info ""
|
112
118
|
if Bundler.settings[:bin] && standalone
|
113
119
|
generate_standalone_bundler_executable_stubs(spec)
|
114
120
|
elsif Bundler.settings[:bin]
|
@@ -116,6 +122,7 @@ module Bundler
|
|
116
122
|
end
|
117
123
|
|
118
124
|
FileUtils.rm_rf(Bundler.tmp)
|
125
|
+
message
|
119
126
|
rescue Exception => e
|
120
127
|
# install hook failed
|
121
128
|
raise e if e.is_a?(Bundler::InstallHookError) || e.is_a?(Bundler::SecurityError)
|
@@ -163,7 +170,7 @@ module Bundler
|
|
163
170
|
next
|
164
171
|
end
|
165
172
|
|
166
|
-
File.open(binstub_path, 'w',
|
173
|
+
File.open(binstub_path, 'w', 0755) do |f|
|
167
174
|
f.puts ERB.new(template, nil, '-').result(binding)
|
168
175
|
end
|
169
176
|
end
|
@@ -184,6 +191,16 @@ module Bundler
|
|
184
191
|
end
|
185
192
|
|
186
193
|
private
|
194
|
+
def can_install_parallely?
|
195
|
+
if Bundler.current_ruby.mri? || Bundler.rubygems.provides?(">= 2.1.0.rc")
|
196
|
+
true
|
197
|
+
else
|
198
|
+
Bundler.ui.warn "Rubygems #{Gem::VERSION} is not threadsafe, so your "\
|
199
|
+
"gems must be installed one at a time. Upgrade to Rubygems 2.1 or "\
|
200
|
+
"higher to enable parallel gem installation."
|
201
|
+
false
|
202
|
+
end
|
203
|
+
end
|
187
204
|
|
188
205
|
def generate_standalone_bundler_executable_stubs(spec)
|
189
206
|
# double-assignment to avoid warnings about variables that will be used by ERB
|
@@ -236,5 +253,57 @@ module Bundler
|
|
236
253
|
end
|
237
254
|
end
|
238
255
|
end
|
256
|
+
|
257
|
+
def install_sequentially(standalone)
|
258
|
+
specs.each do |spec|
|
259
|
+
message = install_gem_from_spec spec, standalone
|
260
|
+
if message
|
261
|
+
Installer.post_install_messages[spec.name] = message
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def install_in_parallel(size, standalone)
|
267
|
+
name2spec = {}
|
268
|
+
remains = {}
|
269
|
+
enqueued = {}
|
270
|
+
specs.each do |spec|
|
271
|
+
name2spec[spec.name] = spec
|
272
|
+
remains[spec.name] = true
|
273
|
+
end
|
274
|
+
|
275
|
+
worker_pool = ParallelWorkers.worker_pool size, lambda { |name|
|
276
|
+
spec = name2spec[name]
|
277
|
+
message = install_gem_from_spec spec, standalone
|
278
|
+
{ :name => spec.name, :post_install => message }
|
279
|
+
}
|
280
|
+
specs.each do |spec|
|
281
|
+
deps = spec.dependencies.select { |dep| dep.type != :development }
|
282
|
+
if deps.empty?
|
283
|
+
worker_pool.enq spec.name
|
284
|
+
enqueued[spec.name] = true
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
until remains.empty?
|
289
|
+
message = worker_pool.deq
|
290
|
+
remains.delete message[:name]
|
291
|
+
if message[:post_install]
|
292
|
+
Installer.post_install_messages[message[:name]] = message[:post_install]
|
293
|
+
end
|
294
|
+
remains.keys.each do |name|
|
295
|
+
next if enqueued[name]
|
296
|
+
spec = name2spec[name]
|
297
|
+
deps = spec.dependencies.select { |dep| remains[dep.name] and dep.type != :development }
|
298
|
+
if deps.empty?
|
299
|
+
worker_pool.enq name
|
300
|
+
enqueued[name] = true
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
message
|
305
|
+
ensure
|
306
|
+
worker_pool && worker_pool.stop
|
307
|
+
end
|
239
308
|
end
|
240
309
|
end
|