bundler 1.0.21 → 1.1.rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. data/.travis.yml +7 -0
  2. data/CHANGELOG.md +153 -1
  3. data/README.md +3 -3
  4. data/Rakefile +15 -27
  5. data/bin/bundle +7 -0
  6. data/bundler.gemspec +1 -1
  7. data/lib/bundler/cli.rb +126 -45
  8. data/lib/bundler/definition.rb +22 -5
  9. data/lib/bundler/dep_proxy.rb +35 -0
  10. data/lib/bundler/dsl.rb +17 -34
  11. data/lib/bundler/endpoint_specification.rb +69 -0
  12. data/lib/bundler/fetcher.rb +221 -0
  13. data/lib/bundler/gem_helper.rb +0 -1
  14. data/lib/bundler/gem_helpers.rb +23 -0
  15. data/lib/bundler/index.rb +77 -38
  16. data/lib/bundler/installer.rb +43 -1
  17. data/lib/bundler/man/bundle-benchmark +19 -0
  18. data/lib/bundler/man/bundle-benchmark.txt +27 -0
  19. data/lib/bundler/man/bundle-config +1 -1
  20. data/lib/bundler/man/bundle-config.txt +3 -3
  21. data/lib/bundler/man/bundle-install +288 -0
  22. data/lib/bundler/man/bundle-install.txt +74 -79
  23. data/lib/bundler/man/bundle-package +1 -1
  24. data/lib/bundler/man/bundle-package.txt +1 -1
  25. data/lib/bundler/man/bundle-update +1 -1
  26. data/lib/bundler/man/bundle-update.txt +41 -41
  27. data/lib/bundler/man/gemfile.5 +6 -7
  28. data/lib/bundler/man/gemfile.5.txt +9 -9
  29. data/lib/bundler/match_platform.rb +13 -0
  30. data/lib/bundler/remote_specification.rb +6 -8
  31. data/lib/bundler/resolver.rb +32 -19
  32. data/lib/bundler/rubygems_ext.rb +2 -86
  33. data/lib/bundler/rubygems_integration.rb +35 -0
  34. data/lib/bundler/runtime.rb +84 -1
  35. data/lib/bundler/source.rb +85 -88
  36. data/lib/bundler/spec_set.rb +2 -0
  37. data/lib/bundler/templates/Executable +1 -1
  38. data/lib/bundler/templates/newgem/Gemfile.tt +1 -1
  39. data/lib/bundler/templates/newgem/Rakefile.tt +1 -0
  40. data/lib/bundler/templates/newgem/bin/newgem.tt +1 -1
  41. data/lib/bundler/templates/newgem/gitignore.tt +14 -1
  42. data/lib/bundler/templates/newgem/newgem.gemspec.tt +13 -20
  43. data/lib/bundler/ui.rb +32 -17
  44. data/lib/bundler/vendor/net/http/faster.rb +27 -0
  45. data/lib/bundler/vendor/net/http/persistent.rb +468 -0
  46. data/lib/bundler/version.rb +1 -1
  47. data/lib/bundler.rb +56 -23
  48. data/man/bundle-install.ronn +7 -0
  49. data/man/bundle.ronn +3 -0
  50. data/man/gemfile.5.ronn +6 -6
  51. data/spec/bundler/dsl_spec.rb +22 -0
  52. data/spec/bundler/source_spec.rb +25 -0
  53. data/spec/install/deprecated_spec.rb +2 -3
  54. data/spec/install/gems/dependency_api_spec.rb +358 -0
  55. data/spec/install/gems/flex_spec.rb +1 -1
  56. data/spec/install/gems/groups_spec.rb +17 -8
  57. data/spec/install/gems/platform_spec.rb +16 -0
  58. data/spec/install/gems/post_install_spec.rb +47 -0
  59. data/spec/install/gems/simple_case_spec.rb +61 -64
  60. data/spec/install/gems/standalone_spec.rb +238 -0
  61. data/spec/install/git_spec.rb +62 -0
  62. data/spec/other/check_spec.rb +30 -0
  63. data/spec/other/clean_spec.rb +397 -0
  64. data/spec/other/exec_spec.rb +0 -29
  65. data/spec/other/newgem_spec.rb +39 -0
  66. data/spec/other/outdated_spec.rb +93 -0
  67. data/spec/other/show_spec.rb +6 -0
  68. data/spec/quality_spec.rb +1 -1
  69. data/spec/realworld/edgecases_spec.rb +12 -0
  70. data/spec/runtime/executable_spec.rb +10 -0
  71. data/spec/runtime/require_spec.rb +8 -9
  72. data/spec/runtime/with_clean_env_spec.rb +60 -7
  73. data/spec/spec_helper.rb +8 -1
  74. data/spec/support/artifice/endopint_marshal_fail_basic_authentication.rb +13 -0
  75. data/spec/support/artifice/endpoint.rb +54 -0
  76. data/spec/support/artifice/endpoint_500.rb +37 -0
  77. data/spec/support/artifice/endpoint_api_missing.rb +16 -0
  78. data/spec/support/artifice/endpoint_basic_authentication.rb +13 -0
  79. data/spec/support/artifice/endpoint_extra.rb +27 -0
  80. data/spec/support/artifice/endpoint_extra_missing.rb +15 -0
  81. data/spec/support/artifice/endpoint_fallback.rb +18 -0
  82. data/spec/support/artifice/endpoint_marshal_fail.rb +11 -0
  83. data/spec/support/artifice/endpoint_redirect.rb +15 -0
  84. data/spec/support/builders.rb +7 -0
  85. data/spec/support/fakeweb/rack-1.0.0.marshal +2 -0
  86. data/spec/support/fakeweb/windows.rb +23 -0
  87. data/spec/support/helpers.rb +36 -3
  88. data/spec/support/path.rb +2 -0
  89. data/spec/support/rubygems_ext.rb +3 -3
  90. metadata +48 -74
@@ -12,11 +12,11 @@ module Bundler
12
12
  attr_reader :name, :version, :platform
13
13
  attr_accessor :source
14
14
 
15
- def initialize(name, version, platform, source_uri)
16
- @name = name
17
- @version = version
18
- @platform = platform
19
- @source_uri = source_uri
15
+ def initialize(name, version, platform, spec_fetcher)
16
+ @name = name
17
+ @version = version
18
+ @platform = platform
19
+ @spec_fetcher = spec_fetcher
20
20
  end
21
21
 
22
22
  # Needed before installs, since the arch matters then and quick
@@ -43,9 +43,7 @@ module Bundler
43
43
  private
44
44
 
45
45
  def _remote_specification
46
- @specification ||= begin
47
- Gem::SpecFetcher.new.fetch_spec([@name, @version, @platform], URI(@source_uri.to_s))
48
- end
46
+ @specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
49
47
  end
50
48
 
51
49
  def method_missing(method, *args, &blk)
@@ -137,7 +137,7 @@ module Bundler
137
137
  @stack = []
138
138
  @base = base
139
139
  @index = index
140
- @gems_size = {}
140
+ @deps_for = {}
141
141
  @missing_gems = Hash.new(0)
142
142
  @source_requirements = source_requirements
143
143
  end
@@ -248,11 +248,11 @@ module Bundler
248
248
  conflicts = Set.new
249
249
 
250
250
  # Fetch all gem versions matching the requirement
251
- #
252
- # TODO: Warn / error when no matching versions are found.
253
251
  matching_versions = search(current)
254
252
 
253
+ # If we found no versions that match the current requirement
255
254
  if matching_versions.empty?
255
+ # If this is a top-level Gemfile requirement
256
256
  if current.required_by.empty?
257
257
  if base = @base[current.name] and !base.empty?
258
258
  version = base.first.version
@@ -271,13 +271,14 @@ module Bundler
271
271
  end
272
272
  else
273
273
  message = "Could not find gem '#{current}' "
274
- if @index.sources.include?(Bundler::Source::Rubygems)
274
+ if @index.source_types.include?(Bundler::Source::Rubygems)
275
275
  message << "in any of the gem sources listed in your Gemfile."
276
276
  else
277
277
  message << "in the gems available on this machine."
278
278
  end
279
279
  end
280
280
  raise GemNotFound, message
281
+ # This is not a top-level Gemfile requirement
281
282
  else
282
283
  @errors[current.name] = [nil, current]
283
284
  end
@@ -333,8 +334,13 @@ module Bundler
333
334
  length = @stack.length
334
335
  @stack << requirement.name
335
336
  retval = catch(requirement.name) do
337
+ # clear the search cache since the catch means we couldn't meet the
338
+ # requirement we need with the current constraints on search
339
+ clear_search_cache
340
+ # try to resolve the next option
336
341
  resolve(reqs, activated)
337
342
  end
343
+
338
344
  # Since we're doing a lot of throw / catches. A push does not necessarily match
339
345
  # up to a pop. So, we simply slice the stack back to what it was before the catch
340
346
  # block.
@@ -343,7 +349,11 @@ module Bundler
343
349
  end
344
350
 
345
351
  def gems_size(dep)
346
- @gems_size[dep] ||= search(dep).size
352
+ search(dep).size
353
+ end
354
+
355
+ def clear_search_cache
356
+ @deps_for = {}
347
357
  end
348
358
 
349
359
  def search(dep)
@@ -353,22 +363,25 @@ module Bundler
353
363
  else
354
364
  d = dep.dep
355
365
  end
356
- index = @source_requirements[d.name] || @index
357
- results = index.search_for_all_platforms(d, @base[d.name])
358
-
359
- if results.any?
360
- version = results.first.version
361
- nested = [[]]
362
- results.each do |spec|
363
- if spec.version != version
364
- nested << []
365
- version = spec.version
366
+
367
+ @deps_for[d.hash] ||= begin
368
+ index = @source_requirements[d.name] || @index
369
+ results = index.search(d, @base[d.name])
370
+
371
+ if results.any?
372
+ version = results.first.version
373
+ nested = [[]]
374
+ results.each do |spec|
375
+ if spec.version != version
376
+ nested << []
377
+ version = spec.version
378
+ end
379
+ nested.last << spec
366
380
  end
367
- nested.last << spec
381
+ deps = nested.map{|a| SpecGroup.new(a) }.select{|sg| sg.for?(dep.__platform) }
382
+ else
383
+ deps = []
368
384
  end
369
- nested.map { |a| SpecGroup.new(a) }.select { |sg| sg.for?(dep.__platform) }
370
- else
371
- []
372
385
  end
373
386
  end
374
387
 
@@ -7,6 +7,7 @@ end
7
7
 
8
8
  require 'rubygems'
9
9
  require 'rubygems/specification'
10
+ require 'bundler/match_platform'
10
11
 
11
12
  module Gem
12
13
  @loaded_stacks = Hash.new { |h,k| h[k] = [] }
@@ -70,22 +71,6 @@ module Gem
70
71
  dependencies - development_dependencies
71
72
  end
72
73
 
73
- def add_bundler_dependencies(*groups)
74
- Bundler.ui.warn "#add_bundler_dependencies is deprecated and will " \
75
- "be removed in Bundler 1.0. Instead, please use the #gemspec method " \
76
- "in your Gemfile, which will pull in any dependencies specified in " \
77
- "your gemspec"
78
-
79
- groups = [:default] if groups.empty?
80
- Bundler.definition.dependencies.each do |dep|
81
- if dep.groups.include?(:development)
82
- self.add_development_dependency(dep.name, dep.requirement.to_s)
83
- elsif (dep.groups & groups).any?
84
- self.add_dependency(dep.name, dep.requirement.to_s)
85
- end
86
- end
87
- end
88
-
89
74
  private
90
75
 
91
76
  def dependencies_to_gemfile(dependencies, group = nil)
@@ -159,79 +144,10 @@ module Gem
159
144
 
160
145
  alias eql? ==
161
146
  end
162
-
163
- end
164
-
165
- module Bundler
166
- class DepProxy
167
-
168
- attr_reader :required_by, :__platform, :dep
169
-
170
- def initialize(dep, platform)
171
- @dep, @__platform, @required_by = dep, platform, []
172
- end
173
-
174
- def hash
175
- @hash ||= dep.hash
176
- end
177
-
178
- def ==(o)
179
- dep == o.dep && __platform == o.__platform
180
- end
181
-
182
- alias eql? ==
183
-
184
- def type
185
- @dep.type
186
- end
187
-
188
- def to_s
189
- "#{name} (#{requirement}) #{__platform}"
190
- end
191
-
192
- private
193
-
194
- def method_missing(*args)
195
- @dep.send(*args)
196
- end
197
-
198
- end
199
-
200
- module GemHelpers
201
-
202
- GENERIC_CACHE = {}
203
- GENERICS = [
204
- Gem::Platform::JAVA,
205
- Gem::Platform::MSWIN,
206
- Gem::Platform::MINGW,
207
- Gem::Platform::RUBY
208
- ]
209
-
210
- def generic(p)
211
- return p if p == Gem::Platform::RUBY
212
-
213
- GENERIC_CACHE[p] ||= begin
214
- found = GENERICS.find do |p2|
215
- p2.is_a?(Gem::Platform) && p.os == p2.os
216
- end
217
- found || Gem::Platform::RUBY
218
- end
219
- end
220
- end
221
-
222
- module MatchPlatform
223
- include GemHelpers
224
-
225
- def match_platform(p)
226
- Gem::Platform::RUBY == platform or
227
- platform.nil? or p == platform or
228
- generic(Gem::Platform.new(platform)) == p
229
- end
230
- end
231
147
  end
232
148
 
233
149
  module Gem
234
150
  class Specification
235
- include Bundler::MatchPlatform
151
+ include ::Bundler::MatchPlatform
236
152
  end
237
153
  end
@@ -266,8 +266,43 @@ module Bundler
266
266
  end
267
267
  end
268
268
 
269
+ # This backports base_dir which replaces installation path
270
+ # Rubygems 1.8+
271
+ def backport_base_dir
272
+ Gem::Specification.send(:define_method, :base_dir) do
273
+ installation_path
274
+ end
275
+ end
276
+
277
+ def backport_cache_file
278
+ Gem::Specification.send(:define_method, :cache_dir) do
279
+ @cache_dir ||= File.join base_dir, "cache"
280
+ end
281
+
282
+ Gem::Specification.send(:define_method, :cache_file) do
283
+ @cache_file ||= File.join cache_dir, "#{full_name}.gem"
284
+ end
285
+ end
286
+
287
+ def backport_spec_file
288
+ Gem::Specification.send(:define_method, :spec_dir) do
289
+ @spec_dir ||= File.join base_dir, "specifications"
290
+ end
291
+
292
+ Gem::Specification.send(:define_method, :spec_file) do
293
+ @spec_file ||= File.join spec_dir, "#{full_name}.gemspec"
294
+ end
295
+ end
296
+
269
297
  # Rubygems 1.4 through 1.6
270
298
  class Legacy < RubygemsIntegration
299
+ def initialize
300
+ super
301
+ backport_base_dir
302
+ backport_cache_file
303
+ backport_spec_file
304
+ end
305
+
271
306
  def stub_rubygems(specs)
272
307
  stub_source_index137(specs)
273
308
  end
@@ -69,7 +69,18 @@ module Bundler
69
69
  end
70
70
  rescue LoadError => e
71
71
  REGEXPS.find { |r| r =~ e.message }
72
- raise if dep.autorequire || $1 != required_file
72
+
73
+ if dep.name.include?('-')
74
+ begin
75
+ namespaced_file = dep.name.gsub('-', '/')
76
+ Kernel.require namespaced_file
77
+ rescue LoadError
78
+ REGEXPS.find { |r| r =~ e.message }
79
+ raise if dep.autorequire || $1.gsub('-', '/') != namespaced_file
80
+ end
81
+ else
82
+ raise if dep.autorequire || $1 != required_file
83
+ end
73
84
  end
74
85
  end
75
86
  end
@@ -119,6 +130,78 @@ module Bundler
119
130
  end
120
131
  end
121
132
 
133
+ def clean
134
+ gem_bins = Dir["#{Gem.dir}/bin/*"]
135
+ git_dirs = Dir["#{Gem.dir}/bundler/gems/*"]
136
+ git_cache_dirs = Dir["#{Gem.dir}/cache/bundler/git/*"]
137
+ gem_dirs = Dir["#{Gem.dir}/gems/*"]
138
+ gem_files = Dir["#{Gem.dir}/cache/*.gem"]
139
+ gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"]
140
+ spec_gem_paths = []
141
+ spec_git_paths = []
142
+ spec_git_cache_dirs = []
143
+ spec_gem_executables = []
144
+ spec_cache_paths = []
145
+ spec_gemspec_paths = []
146
+ specs.each do |spec|
147
+ spec_gem_paths << spec.full_gem_path
148
+ # need to check here in case gems are nested like for the rails git repo
149
+ md = %r{(.+bundler/gems/.+-[a-f0-9]{12})}.match(spec.full_gem_path)
150
+ spec_git_paths << md[1] if md
151
+ spec_gem_executables << spec.executables.collect do |executable|
152
+ "#{Gem.dir}/#{spec.bindir}/#{executable}"
153
+ end
154
+ spec_cache_paths << spec.cache_file
155
+ spec_gemspec_paths << spec.spec_file
156
+ spec_git_cache_dirs << spec.source.cache_path.to_s if spec.source.is_a?(Bundler::Source::Git)
157
+ end
158
+ spec_gem_paths.uniq!
159
+ spec_gem_executables.flatten!
160
+
161
+ stale_gem_bins = gem_bins - spec_gem_executables
162
+ stale_git_dirs = git_dirs - spec_git_paths
163
+ stale_git_cache_dirs = git_cache_dirs - spec_git_cache_dirs
164
+ stale_gem_dirs = gem_dirs - spec_gem_paths
165
+ stale_gem_files = gem_files - spec_cache_paths
166
+ stale_gemspec_files = gemspec_files - spec_gemspec_paths
167
+
168
+ stale_gem_bins.each {|bin| FileUtils.rm(bin) }
169
+ output = stale_gem_dirs.collect do |gem_dir|
170
+ full_name = Pathname.new(gem_dir).basename.to_s
171
+
172
+ FileUtils.rm_rf(gem_dir)
173
+
174
+ parts = full_name.split('-')
175
+ name = parts[0..-2].join('-')
176
+ version = parts.last
177
+ output = "#{name} (#{version})"
178
+
179
+ Bundler.ui.info "Removing #{output}"
180
+
181
+ output
182
+ end + stale_git_dirs.collect do |gem_dir|
183
+ full_name = Pathname.new(gem_dir).basename.to_s
184
+
185
+ FileUtils.rm_rf(gem_dir)
186
+
187
+ parts = full_name.split('-')
188
+ name = parts[0..-3].join('-')
189
+ revision = parts[-1]
190
+ version = parts[-2]
191
+ output = "#{name} (#{version} #{revision})"
192
+
193
+ Bundler.ui.info "Removing #{output}"
194
+
195
+ output
196
+ end
197
+
198
+ stale_gem_files.each {|file| FileUtils.rm(file) if File.exists?(file) }
199
+ stale_gemspec_files.each {|file| FileUtils.rm(file) if File.exists?(file) }
200
+ stale_git_cache_dirs.each {|dir| FileUtils.rm_rf(dir) if File.exists?(dir) }
201
+
202
+ output
203
+ end
204
+
122
205
  private
123
206
 
124
207
  def cache_path
@@ -10,20 +10,20 @@ module Bundler
10
10
  module Source
11
11
  # TODO: Refactor this class
12
12
  class Rubygems
13
- attr_reader :remotes
13
+ FORCE_MODERN_INDEX_LIMIT = 100 # threshold for switching back to the modern index instead of fetching every spec
14
+
15
+ attr_reader :remotes, :caches
16
+ attr_accessor :dependency_names
14
17
 
15
18
  def initialize(options = {})
16
19
  @options = options
17
20
  @remotes = (options["remotes"] || []).map { |r| normalize_uri(r) }
21
+ @fetchers = {}
18
22
  @allow_remote = false
19
23
  @allow_cached = false
20
24
 
21
- # Hardcode the paths for now
22
- @caches = [ Bundler.app_cache ] + Bundler.rubygems.gem_path.map do |x|
23
- File.expand_path("#{x}/cache")
24
- end
25
-
26
- @spec_fetch_map = {}
25
+ @caches = [ Bundler.app_cache ] +
26
+ Bundler.rubygems.gem_path.map{|p| File.expand_path("#{p}/cache") }
27
27
  end
28
28
 
29
29
  def remote!
@@ -70,15 +70,6 @@ module Bundler
70
70
  @specs ||= fetch_specs
71
71
  end
72
72
 
73
- def fetch(spec)
74
- spec, uri = @spec_fetch_map[spec.full_name]
75
- if spec
76
- path = download_gem_from_uri(spec, uri)
77
- s = Bundler.rubygems.spec_from_gem(path)
78
- spec.__swap__(s)
79
- end
80
- end
81
-
82
73
  def install(spec)
83
74
  if installed_specs[spec].any?
84
75
  Bundler.ui.info "Using #{spec.name} (#{spec.version}) "
@@ -87,38 +78,43 @@ module Bundler
87
78
 
88
79
  Bundler.ui.info "Installing #{spec.name} (#{spec.version}) "
89
80
  path = cached_gem(spec)
81
+ if Bundler.requires_sudo?
82
+ install_path = Bundler.tmp
83
+ bin_path = install_path.join("bin")
84
+ else
85
+ install_path = Bundler.rubygems.gem_dir
86
+ bin_path = Bundler.system_bindir
87
+ end
90
88
 
91
89
  Bundler.rubygems.preserve_paths do
90
+ Bundler::GemInstaller.new(path,
91
+ :install_dir => install_path.to_s,
92
+ :bin_dir => bin_path.to_s,
93
+ :ignore_dependencies => true,
94
+ :wrappers => true,
95
+ :env_shebang => true
96
+ ).install
97
+ end
92
98
 
93
- install_path = Bundler.requires_sudo? ? Bundler.tmp : Bundler.rubygems.gem_dir
94
- options = { :install_dir => install_path,
95
- :ignore_dependencies => true,
96
- :wrappers => true,
97
- :env_shebang => true }
98
- options.merge!(:bin_dir => "#{install_path}/bin") unless spec.executables.nil? || spec.executables.empty?
99
-
100
- installer = Bundler::GemInstaller.new path, options
101
- installer.install
99
+ if spec.post_install_message
100
+ Installer.post_install_messages[spec.name] = spec.post_install_message
102
101
  end
103
102
 
104
103
  # SUDO HAX
105
104
  if Bundler.requires_sudo?
106
- sudo "mkdir -p #{Bundler.rubygems.gem_dir}/gems #{Bundler.rubygems.gem_dir}/specifications"
107
- sudo "cp -R #{Bundler.tmp}/gems/#{spec.full_name} #{Bundler.rubygems.gem_dir}/gems/"
108
- sudo "cp -R #{Bundler.tmp}/specifications/#{spec.full_name}.gemspec #{Bundler.rubygems.gem_dir}/specifications/"
105
+ Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/gems"
106
+ Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/specifications"
107
+ Bundler.sudo "cp -R #{Bundler.tmp}/gems/#{spec.full_name} #{Bundler.rubygems.gem_dir}/gems/"
108
+ Bundler.sudo "cp -R #{Bundler.tmp}/specifications/#{spec.full_name}.gemspec #{Bundler.rubygems.gem_dir}/specifications/"
109
109
  spec.executables.each do |exe|
110
- sudo "mkdir -p #{Bundler.rubygems.gem_bindir}"
111
- sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Bundler.rubygems.gem_bindir}"
110
+ Bundler.mkdir_p Bundler.system_bindir
111
+ Bundler.sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Bundler.system_bindir}"
112
112
  end
113
113
  end
114
114
 
115
115
  spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
116
116
  end
117
117
 
118
- def sudo(str)
119
- Bundler.sudo(str)
120
- end
121
-
122
118
  def cache(spec)
123
119
  cached_path = cached_gem(spec)
124
120
  raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
@@ -228,56 +224,43 @@ module Bundler
228
224
  idx = Index.new
229
225
  old = Bundler.rubygems.sources
230
226
 
227
+ sources = {}
231
228
  remotes.each do |uri|
232
- Bundler.ui.info "Fetching source index for #{uri}"
233
- Gem.sources = ["#{uri}"]
234
- fetch_all_remote_specs do |n,v|
235
- v.each do |name, version, platform|
236
- next if name == 'bundler'
237
- spec = RemoteSpecification.new(name, version, platform, uri)
238
- spec.source = self
239
- @spec_fetch_map[spec.full_name] = [spec, uri]
240
- idx << spec
229
+ fetcher = Bundler::Fetcher.new(uri)
230
+ specs = fetcher.specs(dependency_names, self)
231
+ sources[fetcher] = specs.size
232
+
233
+ idx.use specs
234
+ end
235
+
236
+ # don't need to fetch all specifications for every gem/version on
237
+ # the rubygems repo if there's no api endpoints to search over
238
+ # or it has too many specs to fetch
239
+ fetchers = sources.keys
240
+ api_fetchers = fetchers.select {|fetcher| fetcher.has_api }
241
+ modern_index_fetchers = fetchers - api_fetchers
242
+ if api_fetchers.any? && modern_index_fetchers.all? {|fetcher| sources[fetcher] < FORCE_MODERN_INDEX_LIMIT }
243
+ # this will fetch all the specifications on the rubygems repo
244
+ unmet_dependency_names = idx.unmet_dependency_names
245
+ unmet_dependency_names -= ['bundler'] # bundler will always be unmet
246
+
247
+ Bundler.ui.debug "Unmet Dependencies: #{unmet_dependency_names}"
248
+ if unmet_dependency_names.any?
249
+ api_fetchers.each do |fetcher|
250
+ idx.use fetcher.specs(unmet_dependency_names, self)
241
251
  end
242
252
  end
253
+ else
254
+ Bundler::Fetcher.disable_endpoint = true
255
+ api_fetchers.each {|fetcher| idx.use fetcher.specs([], self) }
243
256
  end
257
+
244
258
  idx
245
259
  ensure
246
260
  Bundler.rubygems.sources = old
247
261
  end
248
262
  end
249
263
 
250
- def fetch_all_remote_specs(&blk)
251
- begin
252
- # Fetch all specs, minus prerelease specs
253
- Gem::SpecFetcher.new.list(true, false).each(&blk)
254
- # Then fetch the prerelease specs
255
- begin
256
- Gem::SpecFetcher.new.list(false, true).each(&blk)
257
- rescue Gem::RemoteFetcher::FetchError
258
- Bundler.ui.warn "Could not fetch prerelease specs from #{self}"
259
- end
260
- rescue Gem::RemoteFetcher::FetchError
261
- Bundler.ui.warn "Could not reach #{self}"
262
- end
263
- end
264
-
265
- def download_gem_from_uri(spec, uri)
266
- spec.fetch_platform
267
-
268
- download_path = Bundler.requires_sudo? ? Bundler.tmp : Bundler.rubygems.gem_dir
269
- gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem"
270
-
271
- FileUtils.mkdir_p("#{download_path}/cache")
272
- Bundler.rubygems.download_gem(spec, uri, download_path)
273
-
274
- if Bundler.requires_sudo?
275
- sudo "mkdir -p #{Bundler.rubygems.gem_dir}/cache"
276
- sudo "mv #{Bundler.tmp}/cache/#{spec.full_name}.gem #{gem_path}"
277
- end
278
-
279
- gem_path
280
- end
281
264
  end
282
265
 
283
266
  class Path
@@ -381,7 +364,7 @@ module Bundler
381
364
  index
382
365
  end
383
366
 
384
- def local_specs
367
+ def local_specs(*)
385
368
  @local_specs ||= load_spec_files
386
369
  end
387
370
 
@@ -456,8 +439,11 @@ module Bundler
456
439
  gem_file = Dir.chdir(gem_dir){ Gem::Builder.new(spec).build }
457
440
 
458
441
  installer = Installer.new(spec, :env_shebang => false)
442
+ run_hooks(:pre_install, installer)
459
443
  installer.build_extensions
444
+ run_hooks(:post_build, installer)
460
445
  installer.generate_bin
446
+ run_hooks(:post_install, installer)
461
447
  rescue Gem::InvalidSpecificationException => e
462
448
  Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
463
449
  "This prevents bundler from installing bins or native extensions, but " \
@@ -474,6 +460,18 @@ module Bundler
474
460
  Dir.chdir(gem_dir){ FileUtils.rm_rf(gem_file) if gem_file && File.exist?(gem_file) }
475
461
  end
476
462
 
463
+ def run_hooks(type, installer)
464
+ hooks_meth = "#{type}_hooks"
465
+ return unless Gem.respond_to?(hooks_meth)
466
+ Gem.send(hooks_meth).each do |hook|
467
+ result = hook.call(installer)
468
+ if result == false
469
+ location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
470
+ message = "#{type} hook#{location} failed for #{installer.spec.full_name}"
471
+ raise Gem::InstallError, message
472
+ end
473
+ end
474
+ end
477
475
  end
478
476
 
479
477
  class Git < Path
@@ -545,7 +543,7 @@ module Bundler
545
543
  end
546
544
 
547
545
  # TODO: actually cache git specs
548
- def specs
546
+ def specs(*)
549
547
  if allow_git_ops? && !@update
550
548
  # Start by making sure the git cache is up to date
551
549
  cache
@@ -572,6 +570,17 @@ module Bundler
572
570
  raise GitError, "#{to_s} is not checked out. Please run `bundle install`"
573
571
  end
574
572
 
573
+ def cache_path
574
+ @cache_path ||= begin
575
+ git_scope = "#{base_name}-#{uri_hash}"
576
+
577
+ if Bundler.requires_sudo?
578
+ Bundler.user_bundle_path.join("cache/git", git_scope)
579
+ else
580
+ Bundler.cache.join("git", git_scope)
581
+ end
582
+ end
583
+ end
575
584
  private
576
585
 
577
586
  def git(command)
@@ -628,18 +637,6 @@ module Bundler
628
637
  end
629
638
  end
630
639
 
631
- def cache_path
632
- @cache_path ||= begin
633
- git_scope = "#{base_name}-#{uri_hash}"
634
-
635
- if Bundler.requires_sudo?
636
- Bundler.user_bundle_path.join("cache/git", git_scope)
637
- else
638
- Bundler.cache.join("git", git_scope)
639
- end
640
- end
641
- end
642
-
643
640
  def cache
644
641
  if cached?
645
642
  return if has_revision_cached?
@@ -79,8 +79,10 @@ module Bundler
79
79
 
80
80
  def materialize(deps, missing_specs = nil)
81
81
  materialized = self.for(deps, [], false, true).to_a
82
+ deps = materialized.map {|s| s.name }.uniq
82
83
  materialized.map! do |s|
83
84
  next s unless s.is_a?(LazySpecification)
85
+ s.source.dependency_names = deps if s.source.respond_to?(:dependency_names=)
84
86
  spec = s.__materialize__
85
87
  if missing_specs
86
88
  missing_specs << s unless spec
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env <%= RbConfig::CONFIG['ruby_install_name'] %>
1
+ #!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG['ruby_install_name'] %>
2
2
  #
3
3
  # This file was generated by Bundler.
4
4
  #
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in <%=config[:name]%>.gemspec
4
4
  gemspec
@@ -1 +1,2 @@
1
+ #!/usr/bin/env rake
1
2
  require "bundler/gem_tasks"