bundler 1.2.5 → 1.3.0.pre

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.

Files changed (124) hide show
  1. data/.gitignore +10 -7
  2. data/.travis.yml +12 -3
  3. data/CHANGELOG.md +26 -19
  4. data/CONTRIBUTE.md +97 -0
  5. data/README.md +4 -2
  6. data/Rakefile +17 -59
  7. data/bundler.gemspec +2 -1
  8. data/lib/bundler.rb +23 -20
  9. data/lib/bundler/cli.rb +68 -22
  10. data/lib/bundler/definition.rb +3 -2
  11. data/lib/bundler/deprecate.rb +15 -0
  12. data/lib/bundler/dsl.rb +14 -16
  13. data/lib/bundler/environment.rb +0 -5
  14. data/lib/bundler/fetcher.rb +23 -78
  15. data/lib/bundler/friendly_errors.rb +4 -5
  16. data/lib/bundler/gem_helper.rb +14 -16
  17. data/lib/bundler/injector.rb +64 -0
  18. data/lib/bundler/installer.rb +1 -7
  19. data/lib/bundler/lazy_specification.rb +6 -3
  20. data/lib/bundler/lockfile_parser.rb +25 -13
  21. data/lib/bundler/resolver.rb +0 -1
  22. data/lib/bundler/rubygems_integration.rb +83 -17
  23. data/lib/bundler/settings.rb +4 -2
  24. data/lib/bundler/similarity_detector.rb +63 -0
  25. data/lib/bundler/source.rb +3 -886
  26. data/lib/bundler/source/git.rb +267 -0
  27. data/lib/bundler/source/git/git_proxy.rb +142 -0
  28. data/lib/bundler/source/path.rb +209 -0
  29. data/lib/bundler/source/path/installer.rb +33 -0
  30. data/lib/bundler/source/rubygems.rb +261 -0
  31. data/lib/bundler/templates/newgem/newgem.gemspec.tt +3 -0
  32. data/lib/bundler/templates/newgem/rspec.tt +2 -0
  33. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +9 -0
  34. data/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +2 -0
  35. data/lib/bundler/templates/newgem/test/minitest_helper.rb.tt +4 -0
  36. data/lib/bundler/templates/newgem/test/test_newgem.rb.tt +11 -0
  37. data/lib/bundler/ui.rb +20 -5
  38. data/lib/bundler/vendor/.document +0 -0
  39. data/lib/bundler/vendor/thor.rb +74 -5
  40. data/lib/bundler/vendor/thor/actions.rb +5 -5
  41. data/lib/bundler/vendor/thor/actions/directory.rb +1 -0
  42. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +7 -1
  43. data/lib/bundler/vendor/thor/base.rb +44 -11
  44. data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +5 -0
  45. data/lib/bundler/vendor/thor/parser/argument.rb +14 -7
  46. data/lib/bundler/vendor/thor/parser/arguments.rb +7 -1
  47. data/lib/bundler/vendor/thor/parser/option.rb +8 -8
  48. data/lib/bundler/vendor/thor/parser/options.rb +62 -24
  49. data/lib/bundler/vendor/thor/runner.rb +1 -1
  50. data/lib/bundler/vendor/thor/shell/basic.rb +2 -2
  51. data/lib/bundler/vendor/thor/task.rb +2 -2
  52. data/lib/bundler/vendor/thor/version.rb +1 -1
  53. data/lib/bundler/vendored_persistent.rb +3 -15
  54. data/lib/bundler/version.rb +1 -1
  55. data/man/bundle-exec.ronn +1 -1
  56. data/man/bundle-update.ronn +1 -1
  57. data/man/bundle.ronn +4 -1
  58. data/spec/bundler/bundler_spec.rb +2 -28
  59. data/spec/bundler/cli_rspec.rb +9 -0
  60. data/spec/bundler/definition_spec.rb +1 -1
  61. data/spec/bundler/dsl_spec.rb +15 -8
  62. data/spec/bundler/gem_helper_spec.rb +38 -21
  63. data/spec/bundler/psyched_yaml_spec.rb +1 -0
  64. data/spec/bundler/source_spec.rb +3 -3
  65. data/spec/cache/gems_spec.rb +24 -24
  66. data/spec/cache/git_spec.rb +21 -23
  67. data/spec/cache/path_spec.rb +11 -11
  68. data/spec/cache/platform_spec.rb +6 -6
  69. data/spec/install/deploy_spec.rb +38 -38
  70. data/spec/install/gems/c_ext_spec.rb +2 -2
  71. data/spec/install/gems/dependency_api_spec.rb +23 -116
  72. data/spec/install/gems/env_spec.rb +1 -1
  73. data/spec/install/gems/flex_spec.rb +7 -8
  74. data/spec/install/gems/groups_spec.rb +10 -10
  75. data/spec/install/gems/packed_spec.rb +4 -4
  76. data/spec/install/gems/platform_spec.rb +3 -3
  77. data/spec/install/gems/post_install_spec.rb +9 -9
  78. data/spec/install/gems/resolving_spec.rb +2 -2
  79. data/spec/install/gems/simple_case_spec.rb +50 -53
  80. data/spec/install/gems/standalone_spec.rb +19 -19
  81. data/spec/install/gems/sudo_spec.rb +31 -16
  82. data/spec/install/gems/win32_spec.rb +1 -1
  83. data/spec/install/gemspec_spec.rb +6 -6
  84. data/spec/install/git_spec.rb +34 -34
  85. data/spec/install/invalid_spec.rb +3 -3
  86. data/spec/install/path_spec.rb +71 -8
  87. data/spec/install/upgrade_spec.rb +2 -2
  88. data/spec/integration/inject.rb +78 -0
  89. data/spec/lock/git_spec.rb +2 -2
  90. data/spec/lock/lockfile_spec.rb +14 -14
  91. data/spec/other/check_spec.rb +29 -29
  92. data/spec/other/clean_spec.rb +47 -48
  93. data/spec/other/config_spec.rb +20 -20
  94. data/spec/other/console_spec.rb +5 -5
  95. data/spec/other/exec_spec.rb +48 -28
  96. data/spec/other/ext_spec.rb +3 -3
  97. data/spec/other/help_spec.rb +6 -6
  98. data/spec/other/init_spec.rb +8 -8
  99. data/spec/other/newgem_spec.rb +95 -15
  100. data/spec/other/open_spec.rb +10 -5
  101. data/spec/other/outdated_spec.rb +8 -8
  102. data/spec/other/platform_spec.rb +45 -45
  103. data/spec/other/show_spec.rb +10 -10
  104. data/spec/quality_spec.rb +2 -2
  105. data/spec/realworld/dependency_api_spec.rb +61 -0
  106. data/spec/realworld/edgecases_spec.rb +8 -8
  107. data/spec/runtime/executable_spec.rb +13 -13
  108. data/spec/runtime/load_spec.rb +12 -12
  109. data/spec/runtime/platform_spec.rb +1 -1
  110. data/spec/runtime/require_spec.rb +24 -24
  111. data/spec/runtime/setup_spec.rb +113 -56
  112. data/spec/runtime/with_clean_env_spec.rb +11 -13
  113. data/spec/spec_helper.rb +6 -0
  114. data/spec/support/artifice/endpoint.rb +28 -13
  115. data/spec/support/artifice/endpoint_extra.rb +4 -0
  116. data/spec/support/builders.rb +1 -1
  117. data/spec/support/helpers.rb +2 -7
  118. data/spec/support/indexes.rb +3 -3
  119. data/spec/support/matchers.rb +6 -6
  120. data/spec/update/gems_spec.rb +19 -8
  121. data/spec/update/git_spec.rb +10 -10
  122. data/spec/update/source_spec.rb +1 -1
  123. metadata +86 -55
  124. data/.rspec +0 -2
@@ -0,0 +1,267 @@
1
+ require 'fileutils'
2
+ require 'uri'
3
+ require 'digest/sha1'
4
+
5
+ module Bundler
6
+ module Source
7
+
8
+ class Git < Path
9
+ autoload :GitProxy, 'bundler/source/git/git_proxy'
10
+
11
+ attr_reader :uri, :ref, :branch, :options, :submodules
12
+
13
+ def initialize(options)
14
+ @options = options
15
+ @glob = options["glob"] || DEFAULT_GLOB
16
+
17
+ @allow_cached = false
18
+ @allow_remote = false
19
+
20
+ # Stringify options that could be set as symbols
21
+ %w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] }
22
+
23
+ @uri = options["uri"]
24
+ @branch = options["branch"]
25
+ @ref = options["ref"] || options["branch"] || options["tag"] || 'master'
26
+ @submodules = options["submodules"]
27
+ @name = options["name"]
28
+ @version = options["version"]
29
+
30
+ @update = false
31
+ @installed = nil
32
+ @local = false
33
+ end
34
+
35
+ def self.from_lock(options)
36
+ new(options.merge("uri" => options.delete("remote")))
37
+ end
38
+
39
+ def to_lock
40
+ out = "GIT\n"
41
+ out << " remote: #{@uri}\n"
42
+ out << " revision: #{revision}\n"
43
+ %w(ref branch tag submodules).each do |opt|
44
+ out << " #{opt}: #{options[opt]}\n" if options[opt]
45
+ end
46
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
47
+ out << " specs:\n"
48
+ end
49
+
50
+ def eql?(o)
51
+ Git === o &&
52
+ uri == o.uri &&
53
+ ref == o.ref &&
54
+ branch == o.branch &&
55
+ name == o.name &&
56
+ version == o.version &&
57
+ submodules == o.submodules
58
+ end
59
+
60
+ alias == eql?
61
+
62
+ def to_s
63
+ at = if local?
64
+ path
65
+ elsif options["ref"]
66
+ shortref_for_display(options["ref"])
67
+ else
68
+ ref
69
+ end
70
+ "#{uri} (at #{at})"
71
+ end
72
+
73
+ def name
74
+ File.basename(@uri, '.git')
75
+ end
76
+
77
+ # This is the path which is going to contain a specific
78
+ # checkout of the git repository. When using local git
79
+ # repos, this is set to the local repo.
80
+ def install_path
81
+ @install_path ||= begin
82
+ git_scope = "#{base_name}-#{shortref_for_path(revision)}"
83
+
84
+ if Bundler.requires_sudo?
85
+ Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope)
86
+ else
87
+ Bundler.install_path.join(git_scope)
88
+ end
89
+ end
90
+ end
91
+
92
+ alias :path :install_path
93
+
94
+ def unlock!
95
+ git_proxy.revision = nil
96
+ end
97
+
98
+ def local_override!(path)
99
+ return false if local?
100
+
101
+ path = Pathname.new(path)
102
+ path = path.expand_path(Bundler.root) unless path.relative?
103
+
104
+ unless options["branch"] || Bundler.settings[:disable_local_branch_check]
105
+ raise GitError, "Cannot use local override for #{name} at #{path} because " \
106
+ ":branch is not specified in Gemfile. Specify a branch or use " \
107
+ "`bundle config --delete` to remove the local override"
108
+ end
109
+
110
+ unless path.exist?
111
+ raise GitError, "Cannot use local override for #{name} because #{path} " \
112
+ "does not exist. Check `bundle config --delete` to remove the local override"
113
+ end
114
+
115
+ set_local!(path)
116
+
117
+ # Create a new git proxy without the cached revision
118
+ # so the Gemfile.lock always picks up the new revision.
119
+ @git_proxy = GitProxy.new(path, uri, ref)
120
+
121
+ if git_proxy.branch != options["branch"] && !Bundler.settings[:disable_local_branch_check]
122
+ raise GitError, "Local override for #{name} at #{path} is using branch " \
123
+ "#{git_proxy.branch} but Gemfile specifies #{options["branch"]}"
124
+ end
125
+
126
+ changed = cached_revision && cached_revision != git_proxy.revision
127
+
128
+ if changed && !git_proxy.contains?(cached_revision)
129
+ raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
130
+ "but the current branch in your local override for #{name} does not contain such commit. " \
131
+ "Please make sure your branch is up to date."
132
+ end
133
+
134
+ changed
135
+ end
136
+
137
+ # TODO: actually cache git specs
138
+ def specs(*)
139
+ if has_app_cache? && !local?
140
+ set_local!(app_cache_path)
141
+ end
142
+
143
+ if requires_checkout? && !@update
144
+ git_proxy.checkout
145
+ git_proxy.copy_to(install_path, submodules)
146
+ @update = true
147
+ end
148
+
149
+ local_specs
150
+ end
151
+
152
+ def install(spec)
153
+ Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
154
+ if requires_checkout? && !@installed
155
+ Bundler.ui.debug " * Checking out revision: #{ref}"
156
+ git_proxy.copy_to(install_path, submodules)
157
+ @installed = true
158
+ end
159
+ generate_bin(spec)
160
+ end
161
+
162
+ def cache(spec)
163
+ return unless Bundler.settings[:cache_all]
164
+ return if path == app_cache_path
165
+ cached!
166
+ FileUtils.rm_rf(app_cache_path)
167
+ git_proxy.checkout if requires_checkout?
168
+ git_proxy.copy_to(app_cache_path, @submodules)
169
+ FileUtils.rm_rf(app_cache_path.join(".git"))
170
+ FileUtils.touch(app_cache_path.join(".bundlecache"))
171
+ end
172
+
173
+ def load_spec_files
174
+ super
175
+ rescue PathError, GitError
176
+ raise GitError, "#{to_s} is not checked out. Please run `bundle install`"
177
+ end
178
+
179
+ # This is the path which is going to contain a cache
180
+ # of the git repository. When using the same git repository
181
+ # across different projects, this cache will be shared.
182
+ # When using local git repos, this is set to the local repo.
183
+ def cache_path
184
+ @cache_path ||= begin
185
+ git_scope = "#{base_name}-#{uri_hash}"
186
+
187
+ if Bundler.requires_sudo?
188
+ Bundler.user_bundle_path.join("cache/git", git_scope)
189
+ else
190
+ Bundler.cache.join("git", git_scope)
191
+ end
192
+ end
193
+ end
194
+
195
+ def app_cache_dirname
196
+ "#{base_name}-#{shortref_for_path(cached_revision || revision)}"
197
+ end
198
+
199
+ private
200
+
201
+ def set_local!(path)
202
+ @local = true
203
+ @local_specs = @git_proxy = nil
204
+ @cache_path = @install_path = path
205
+ end
206
+
207
+ def has_app_cache?
208
+ cached_revision && super
209
+ end
210
+
211
+ def local?
212
+ @local
213
+ end
214
+
215
+ def requires_checkout?
216
+ allow_git_ops? && !local?
217
+ end
218
+
219
+ def base_name
220
+ File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*},''),".git")
221
+ end
222
+
223
+ def shortref_for_display(ref)
224
+ ref[0..6]
225
+ end
226
+
227
+ def shortref_for_path(ref)
228
+ ref[0..11]
229
+ end
230
+
231
+ def uri_hash
232
+ if uri =~ %r{^\w+://(\w+@)?}
233
+ # Downcase the domain component of the URI
234
+ # and strip off a trailing slash, if one is present
235
+ input = URI.parse(uri).normalize.to_s.sub(%r{/$},'')
236
+ else
237
+ # If there is no URI scheme, assume it is an ssh/git URI
238
+ input = uri
239
+ end
240
+ Digest::SHA1.hexdigest(input)
241
+ end
242
+
243
+ def allow_git_ops?
244
+ @allow_remote || @allow_cached
245
+ end
246
+
247
+ def cached_revision
248
+ options["revision"]
249
+ end
250
+
251
+ def revision
252
+ git_proxy.revision
253
+ end
254
+
255
+ def cached?
256
+ cache_path.exist?
257
+ end
258
+
259
+ def git_proxy
260
+ @git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision){ allow_git_ops? }
261
+
262
+ end
263
+
264
+ end
265
+
266
+ end
267
+ end
@@ -0,0 +1,142 @@
1
+ module Bundler
2
+ module Source
3
+
4
+ class Git < Path
5
+ # The GitProxy is responsible to iteract with git repositories.
6
+ # All actions required by the Git source is encapsualted in this
7
+ # object.
8
+ class GitProxy
9
+ attr_accessor :path, :uri, :ref
10
+ attr_writer :revision
11
+
12
+ def initialize(path, uri, ref, revision=nil, &allow)
13
+ @path = path
14
+ @uri = uri
15
+ @ref = ref
16
+ @revision = revision
17
+ @allow = allow || Proc.new { true }
18
+ end
19
+
20
+ def revision
21
+ @revision ||= allowed_in_path { git("rev-parse #{ref}").strip }
22
+ end
23
+
24
+ def branch
25
+ @branch ||= allowed_in_path do
26
+ git("branch") =~ /^\* (.*)$/ && $1.strip
27
+ end
28
+ end
29
+
30
+ def contains?(commit)
31
+ allowed_in_path do
32
+ result = git_null("branch --contains #{commit}")
33
+ $? == 0 && result =~ /^\* (.*)$/
34
+ end
35
+ end
36
+
37
+ def checkout
38
+ if path.exist?
39
+ return if has_revision_cached?
40
+ Bundler.ui.info "Updating #{uri}"
41
+ in_path do
42
+ git %|fetch --force --quiet --tags #{uri_escaped} "refs/heads/*:refs/heads/*"|
43
+ end
44
+ else
45
+ Bundler.ui.info "Fetching #{uri}"
46
+ FileUtils.mkdir_p(path.dirname)
47
+ git %|clone #{uri_escaped} "#{path}" --bare --no-hardlinks|
48
+ end
49
+ end
50
+
51
+ def copy_to(destination, submodules=false)
52
+ unless File.exist?(destination.join(".git"))
53
+ FileUtils.mkdir_p(destination.dirname)
54
+ FileUtils.rm_rf(destination)
55
+ git %|clone --no-checkout "#{path}" "#{destination}"|
56
+ File.chmod((0777 & ~File.umask), destination)
57
+ end
58
+
59
+ Dir.chdir(destination) do
60
+ git %|fetch --force --quiet --tags "#{path}"|
61
+ git "reset --hard #{@revision}"
62
+
63
+ if submodules
64
+ git "submodule update --init --recursive"
65
+ end
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # TODO: Do not rely on /dev/null.
72
+ # Given that open3 is not cross platform until Ruby 1.9.3,
73
+ # the best solution is to pipe to /dev/null if it exists.
74
+ # If it doesn't, everything will work fine, but the user
75
+ # will get the $stderr messages as well.
76
+ def git_null(command)
77
+ if !Bundler::WINDOWS && File.exist?("/dev/null")
78
+ git("#{command} 2>/dev/null", false)
79
+ else
80
+ git(command, false)
81
+ end
82
+ end
83
+
84
+ def git(command, check_errors=true)
85
+ if allow?
86
+ out = %x{git #{command}}
87
+
88
+ if check_errors && $?.exitstatus != 0
89
+ msg = "Git error: command `git #{command}` in directory #{Dir.pwd} has failed."
90
+ msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path.exist?
91
+ raise GitError, msg
92
+ end
93
+ out
94
+ else
95
+ raise GitError, "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " \
96
+ "this error message could probably be more useful. Please submit a ticket at http://github.com/carlhuda/bundler/issues " \
97
+ "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
98
+ end
99
+ end
100
+
101
+ def has_revision_cached?
102
+ return unless @revision
103
+ in_path { git("cat-file -e #{@revision}") }
104
+ true
105
+ rescue GitError
106
+ false
107
+ end
108
+
109
+ # Escape the URI for git commands
110
+ def uri_escaped
111
+ if Bundler::WINDOWS
112
+ # Windows quoting requires double quotes only, with double quotes
113
+ # inside the string escaped by being doubled.
114
+ '"' + uri.gsub('"') {|s| '""'} + '"'
115
+ else
116
+ # Bash requires single quoted strings, with the single quotes escaped
117
+ # by ending the string, escaping the quote, and restarting the string.
118
+ "'" + uri.gsub("'") {|s| "'\\''"} + "'"
119
+ end
120
+ end
121
+
122
+ def allow?
123
+ @allow.call
124
+ end
125
+
126
+ def in_path(&blk)
127
+ checkout unless path.exist?
128
+ Dir.chdir(path, &blk)
129
+ end
130
+
131
+ def allowed_in_path
132
+ if allow?
133
+ in_path { yield }
134
+ else
135
+ raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
136
+ end
137
+ end
138
+ end
139
+
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,209 @@
1
+ module Bundler
2
+ module Source
3
+
4
+ class Path
5
+ autoload :Installer, 'bundler/source/path/installer'
6
+
7
+ attr_reader :path, :options
8
+ attr_writer :name
9
+ attr_accessor :version
10
+
11
+ DEFAULT_GLOB = "{,*,*/*}.gemspec"
12
+
13
+ def initialize(options)
14
+ @options = options
15
+ @glob = options["glob"] || DEFAULT_GLOB
16
+
17
+ @allow_cached = false
18
+ @allow_remote = false
19
+
20
+ if options["path"]
21
+ @path = Pathname.new(options["path"])
22
+ @path = @path.expand_path(Bundler.root) unless @path.relative?
23
+ end
24
+
25
+ @name = options["name"]
26
+ @version = options["version"]
27
+
28
+ # Stores the original path. If at any point we move to the
29
+ # cached directory, we still have the original path to copy from.
30
+ @original_path = @path
31
+ end
32
+
33
+ def remote!
34
+ @allow_remote = true
35
+ end
36
+
37
+ def cached!
38
+ @allow_cached = true
39
+ end
40
+
41
+ def self.from_lock(options)
42
+ new(options.merge("path" => options.delete("remote")))
43
+ end
44
+
45
+ def to_lock
46
+ out = "PATH\n"
47
+ out << " remote: #{relative_path}\n"
48
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
49
+ out << " specs:\n"
50
+ end
51
+
52
+ def to_s
53
+ "source at #{@path}"
54
+ end
55
+
56
+ def hash
57
+ self.class.hash
58
+ end
59
+
60
+ def eql?(o)
61
+ o.instance_of?(Path) &&
62
+ path.expand_path(Bundler.root) == o.path.expand_path(Bundler.root) &&
63
+ version == o.version
64
+ end
65
+
66
+ alias == eql?
67
+
68
+ def name
69
+ File.basename(path.expand_path(Bundler.root).to_s)
70
+ end
71
+
72
+ def install(spec)
73
+ Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
74
+ generate_bin(spec, :disable_extensions)
75
+ end
76
+
77
+ def cache(spec)
78
+ return unless Bundler.settings[:cache_all]
79
+ return if @original_path.expand_path(Bundler.root).to_s.index(Bundler.root.to_s) == 0
80
+ FileUtils.rm_rf(app_cache_path)
81
+ FileUtils.cp_r("#{@original_path}/.", app_cache_path)
82
+ FileUtils.touch(app_cache_path.join(".bundlecache"))
83
+ end
84
+
85
+ def local_specs(*)
86
+ @local_specs ||= load_spec_files
87
+ end
88
+
89
+ def specs
90
+ if has_app_cache?
91
+ @path = app_cache_path
92
+ end
93
+ local_specs
94
+ end
95
+
96
+ def app_cache_dirname
97
+ name
98
+ end
99
+
100
+ private
101
+
102
+ def app_cache_path
103
+ @app_cache_path ||= Bundler.app_cache.join(app_cache_dirname)
104
+ end
105
+
106
+ def has_app_cache?
107
+ SharedHelpers.in_bundle? && app_cache_path.exist?
108
+ end
109
+
110
+ def load_spec_files
111
+ index = Index.new
112
+ expanded_path = path.expand_path(Bundler.root)
113
+
114
+ if File.directory?(expanded_path)
115
+ Dir["#{expanded_path}/#{@glob}"].each do |file|
116
+ spec = Bundler.load_gemspec(file)
117
+ if spec
118
+ spec.loaded_from = file.to_s
119
+ spec.source = self
120
+ index << spec
121
+ end
122
+ end
123
+
124
+ if index.empty? && @name && @version
125
+ index << Gem::Specification.new do |s|
126
+ s.name = @name
127
+ s.source = self
128
+ s.version = Gem::Version.new(@version)
129
+ s.platform = Gem::Platform::RUBY
130
+ s.summary = "Fake gemspec for #{@name}"
131
+ s.relative_loaded_from = "#{@name}.gemspec"
132
+ s.authors = ["no one"]
133
+ if expanded_path.join("bin").exist?
134
+ executables = expanded_path.join("bin").children
135
+ executables.reject!{|p| File.directory?(p) }
136
+ s.executables = executables.map{|c| c.basename.to_s }
137
+ end
138
+ end
139
+ end
140
+ else
141
+ raise PathError, "The path `#{expanded_path}` does not exist."
142
+ end
143
+
144
+ index
145
+ end
146
+
147
+ def relative_path
148
+ if path.to_s.match(%r{^#{Regexp.escape Bundler.root.to_s}})
149
+ return path.relative_path_from(Bundler.root)
150
+ end
151
+ path
152
+ end
153
+
154
+ def generate_bin(spec, disable_extensions = false)
155
+ gem_dir = Pathname.new(spec.full_gem_path)
156
+
157
+ # Some gem authors put absolute paths in their gemspec
158
+ # and we have to save them from themselves
159
+ spec.files = spec.files.map do |p|
160
+ next if File.directory?(p)
161
+ begin
162
+ Pathname.new(p).relative_path_from(gem_dir).to_s
163
+ rescue ArgumentError
164
+ p
165
+ end
166
+ end.compact
167
+
168
+ gem_file = Bundler.rubygems.build_gem gem_dir, spec
169
+
170
+ installer = Path::Installer.new(spec, :env_shebang => false)
171
+ run_hooks(:pre_install, installer)
172
+ installer.build_extensions unless disable_extensions
173
+ run_hooks(:post_build, installer)
174
+ installer.generate_bin
175
+ run_hooks(:post_install, installer)
176
+ rescue Gem::InvalidSpecificationException => e
177
+ Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
178
+ "This prevents bundler from installing bins or native extensions, but " \
179
+ "that may not affect its functionality."
180
+
181
+ if !spec.extensions.empty? && !spec.email.empty?
182
+ Bundler.ui.warn "If you need to use this package without installing it from a gem " \
183
+ "repository, please contact #{spec.email} and ask them " \
184
+ "to modify their .gemspec so it can work with `gem build`."
185
+ end
186
+
187
+ Bundler.ui.warn "The validation message from Rubygems was:\n #{e.message}"
188
+ ensure
189
+ if gem_dir && gem_file
190
+ Dir.chdir(gem_dir){ FileUtils.rm_rf(gem_file) if File.exist?(gem_file) }
191
+ end
192
+ end
193
+
194
+ def run_hooks(type, installer)
195
+ hooks_meth = "#{type}_hooks"
196
+ return unless Gem.respond_to?(hooks_meth)
197
+ Gem.send(hooks_meth).each do |hook|
198
+ result = hook.call(installer)
199
+ if result == false
200
+ location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
201
+ message = "#{type} hook#{location} failed for #{installer.spec.full_name}"
202
+ raise InstallHookError, message
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ end
209
+ end