Skip to content

Commit d6b2547

Browse files
committedFeb 27, 2018
Bundler is installed with RubyGems and part of stdlib now.
1 parent 1012517 commit d6b2547

File tree

1 file changed

+545
-0
lines changed

1 file changed

+545
-0
lines changed
 

‎lib/ruby/stdlib/bundler.rb

+545
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,545 @@
1+
# frozen_string_literal: true
2+
3+
require "bundler/compatibility_guard"
4+
5+
require "bundler/vendored_fileutils"
6+
require "pathname"
7+
require "rbconfig"
8+
require "thread"
9+
10+
require "bundler/errors"
11+
require "bundler/environment_preserver"
12+
require "bundler/plugin"
13+
require "bundler/rubygems_ext"
14+
require "bundler/rubygems_integration"
15+
require "bundler/version"
16+
require "bundler/constants"
17+
require "bundler/current_ruby"
18+
require "bundler/build_metadata"
19+
20+
module Bundler
21+
environment_preserver = EnvironmentPreserver.new(ENV, EnvironmentPreserver::BUNDLER_KEYS)
22+
ORIGINAL_ENV = environment_preserver.restore
23+
ENV.replace(environment_preserver.backup)
24+
SUDO_MUTEX = Mutex.new
25+
26+
autoload :Definition, "bundler/definition"
27+
autoload :Dependency, "bundler/dependency"
28+
autoload :DepProxy, "bundler/dep_proxy"
29+
autoload :Deprecate, "bundler/deprecate"
30+
autoload :Dsl, "bundler/dsl"
31+
autoload :EndpointSpecification, "bundler/endpoint_specification"
32+
autoload :Env, "bundler/env"
33+
autoload :Fetcher, "bundler/fetcher"
34+
autoload :FeatureFlag, "bundler/feature_flag"
35+
autoload :GemHelper, "bundler/gem_helper"
36+
autoload :GemHelpers, "bundler/gem_helpers"
37+
autoload :GemRemoteFetcher, "bundler/gem_remote_fetcher"
38+
autoload :GemVersionPromoter, "bundler/gem_version_promoter"
39+
autoload :Graph, "bundler/graph"
40+
autoload :Index, "bundler/index"
41+
autoload :Injector, "bundler/injector"
42+
autoload :Installer, "bundler/installer"
43+
autoload :LazySpecification, "bundler/lazy_specification"
44+
autoload :LockfileParser, "bundler/lockfile_parser"
45+
autoload :MatchPlatform, "bundler/match_platform"
46+
autoload :ProcessLock, "bundler/process_lock"
47+
autoload :RemoteSpecification, "bundler/remote_specification"
48+
autoload :Resolver, "bundler/resolver"
49+
autoload :Retry, "bundler/retry"
50+
autoload :RubyDsl, "bundler/ruby_dsl"
51+
autoload :RubyGemsGemInstaller, "bundler/rubygems_gem_installer"
52+
autoload :RubyVersion, "bundler/ruby_version"
53+
autoload :Runtime, "bundler/runtime"
54+
autoload :Settings, "bundler/settings"
55+
autoload :SharedHelpers, "bundler/shared_helpers"
56+
autoload :Source, "bundler/source"
57+
autoload :SourceList, "bundler/source_list"
58+
autoload :SpecSet, "bundler/spec_set"
59+
autoload :StubSpecification, "bundler/stub_specification"
60+
autoload :UI, "bundler/ui"
61+
autoload :URICredentialsFilter, "bundler/uri_credentials_filter"
62+
autoload :VersionRanges, "bundler/version_ranges"
63+
64+
class << self
65+
def configure
66+
@configured ||= configure_gem_home_and_path
67+
end
68+
69+
def ui
70+
(defined?(@ui) && @ui) || (self.ui = UI::Silent.new)
71+
end
72+
73+
def ui=(ui)
74+
Bundler.rubygems.ui = ui ? UI::RGProxy.new(ui) : nil
75+
@ui = ui
76+
end
77+
78+
# Returns absolute path of where gems are installed on the filesystem.
79+
def bundle_path
80+
@bundle_path ||= Pathname.new(configured_bundle_path.path).expand_path(root)
81+
end
82+
83+
def configured_bundle_path
84+
@configured_bundle_path ||= settings.path.tap(&:validate!)
85+
end
86+
87+
# Returns absolute location of where binstubs are installed to.
88+
def bin_path
89+
@bin_path ||= begin
90+
path = settings[:bin] || "bin"
91+
path = Pathname.new(path).expand_path(root).expand_path
92+
SharedHelpers.filesystem_access(path) {|p| FileUtils.mkdir_p(p) }
93+
path
94+
end
95+
end
96+
97+
def setup(*groups)
98+
# Return if all groups are already loaded
99+
return @setup if defined?(@setup) && @setup
100+
101+
definition.validate_runtime!
102+
103+
SharedHelpers.print_major_deprecations!
104+
105+
if groups.empty?
106+
# Load all groups, but only once
107+
@setup = load.setup
108+
else
109+
load.setup(*groups)
110+
end
111+
end
112+
113+
def require(*groups)
114+
setup(*groups).require(*groups)
115+
end
116+
117+
def load
118+
@load ||= Runtime.new(root, definition)
119+
end
120+
121+
def environment
122+
SharedHelpers.major_deprecation 2, "Bundler.environment has been removed in favor of Bundler.load"
123+
load
124+
end
125+
126+
# Returns an instance of Bundler::Definition for given Gemfile and lockfile
127+
#
128+
# @param unlock [Hash, Boolean, nil] Gems that have been requested
129+
# to be updated or true if all gems should be updated
130+
# @return [Bundler::Definition]
131+
def definition(unlock = nil)
132+
@definition = nil if unlock
133+
@definition ||= begin
134+
configure
135+
Definition.build(default_gemfile, default_lockfile, unlock)
136+
end
137+
end
138+
139+
def frozen?
140+
frozen = settings[:deployment]
141+
frozen ||= settings[:frozen] unless feature_flag.deployment_means_frozen?
142+
frozen
143+
end
144+
145+
def locked_gems
146+
@locked_gems ||=
147+
if defined?(@definition) && @definition
148+
definition.locked_gems
149+
elsif Bundler.default_lockfile.file?
150+
lock = Bundler.read_file(Bundler.default_lockfile)
151+
LockfileParser.new(lock)
152+
end
153+
end
154+
155+
def ruby_scope
156+
"#{Bundler.rubygems.ruby_engine}/#{Bundler.rubygems.config_map[:ruby_version]}"
157+
end
158+
159+
def user_home
160+
@user_home ||= begin
161+
home = Bundler.rubygems.user_home
162+
163+
warning = if home.nil?
164+
"Your home directory is not set."
165+
elsif !File.directory?(home)
166+
"`#{home}` is not a directory."
167+
elsif !File.writable?(home)
168+
"`#{home}` is not writable."
169+
end
170+
171+
if warning
172+
user_home = tmp_home_path(Etc.getlogin, warning)
173+
Bundler.ui.warn "#{warning}\nBundler will use `#{user_home}' as your home directory temporarily.\n"
174+
user_home
175+
else
176+
Pathname.new(home)
177+
end
178+
end
179+
end
180+
181+
def tmp_home_path(login, warning)
182+
login ||= "unknown"
183+
Kernel.send(:require, "tmpdir")
184+
path = Pathname.new(Dir.tmpdir).join("bundler", "home")
185+
SharedHelpers.filesystem_access(path) do |tmp_home_path|
186+
unless tmp_home_path.exist?
187+
tmp_home_path.mkpath
188+
tmp_home_path.chmod(0o777)
189+
end
190+
tmp_home_path.join(login).tap(&:mkpath)
191+
end
192+
rescue => e
193+
raise e.exception("#{warning}\nBundler also failed to create a temporary home directory at `#{path}':\n#{e}")
194+
end
195+
196+
def user_bundle_path
197+
Pathname.new(user_home).join(".bundle")
198+
end
199+
200+
def home
201+
bundle_path.join("bundler")
202+
end
203+
204+
def install_path
205+
home.join("gems")
206+
end
207+
208+
def specs_path
209+
bundle_path.join("specifications")
210+
end
211+
212+
def user_cache
213+
user_bundle_path.join("cache")
214+
end
215+
216+
def root
217+
@root ||= begin
218+
SharedHelpers.root
219+
rescue GemfileNotFound
220+
bundle_dir = default_bundle_dir
221+
raise GemfileNotFound, "Could not locate Gemfile or .bundle/ directory" unless bundle_dir
222+
Pathname.new(File.expand_path("..", bundle_dir))
223+
end
224+
end
225+
226+
def app_config_path
227+
if app_config = ENV["BUNDLE_APP_CONFIG"]
228+
Pathname.new(app_config).expand_path(root)
229+
else
230+
root.join(".bundle")
231+
end
232+
end
233+
234+
def app_cache(custom_path = nil)
235+
path = custom_path || root
236+
Pathname.new(path).join(settings.app_cache_path)
237+
end
238+
239+
def tmp(name = Process.pid.to_s)
240+
Kernel.send(:require, "tmpdir")
241+
Pathname.new(Dir.mktmpdir(["bundler", name]))
242+
end
243+
244+
def rm_rf(path)
245+
FileUtils.remove_entry_secure(path) if path && File.exist?(path)
246+
rescue ArgumentError
247+
message = <<EOF
248+
It is a security vulnerability to allow your home directory to be world-writable, and bundler can not continue.
249+
You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
250+
Please refer to http://ruby-doc.org/stdlib-2.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
251+
EOF
252+
File.world_writable?(path) ? Bundler.ui.warn(message) : raise
253+
raise PathError, "Please fix the world-writable issue with your #{path} directory"
254+
end
255+
256+
def settings
257+
@settings ||= Settings.new(app_config_path)
258+
rescue GemfileNotFound
259+
@settings = Settings.new(Pathname.new(".bundle").expand_path)
260+
end
261+
262+
# @return [Hash] Environment present before Bundler was activated
263+
def original_env
264+
ORIGINAL_ENV.clone
265+
end
266+
267+
# @deprecated Use `original_env` instead
268+
# @return [Hash] Environment with all bundler-related variables removed
269+
def clean_env
270+
Bundler::SharedHelpers.major_deprecation(2, "`Bundler.clean_env` has weird edge cases, use `.original_env` instead")
271+
env = original_env
272+
273+
if env.key?("BUNDLER_ORIG_MANPATH")
274+
env["MANPATH"] = env["BUNDLER_ORIG_MANPATH"]
275+
end
276+
277+
env.delete_if {|k, _| k[0, 7] == "BUNDLE_" }
278+
279+
if env.key?("RUBYOPT")
280+
env["RUBYOPT"] = env["RUBYOPT"].sub "-rbundler/setup", ""
281+
end
282+
283+
if env.key?("RUBYLIB")
284+
rubylib = env["RUBYLIB"].split(File::PATH_SEPARATOR)
285+
rubylib.delete(File.expand_path("..", __FILE__))
286+
env["RUBYLIB"] = rubylib.join(File::PATH_SEPARATOR)
287+
end
288+
289+
env
290+
end
291+
292+
def with_original_env
293+
with_env(original_env) { yield }
294+
end
295+
296+
def with_clean_env
297+
with_env(clean_env) { yield }
298+
end
299+
300+
def clean_system(*args)
301+
with_clean_env { Kernel.system(*args) }
302+
end
303+
304+
def clean_exec(*args)
305+
with_clean_env { Kernel.exec(*args) }
306+
end
307+
308+
def local_platform
309+
return Gem::Platform::RUBY if settings[:force_ruby_platform]
310+
Gem::Platform.local
311+
end
312+
313+
def default_gemfile
314+
SharedHelpers.default_gemfile
315+
end
316+
317+
def default_lockfile
318+
SharedHelpers.default_lockfile
319+
end
320+
321+
def default_bundle_dir
322+
SharedHelpers.default_bundle_dir
323+
end
324+
325+
def system_bindir
326+
# Gem.bindir doesn't always return the location that RubyGems will install
327+
# system binaries. If you put '-n foo' in your .gemrc, RubyGems will
328+
# install binstubs there instead. Unfortunately, RubyGems doesn't expose
329+
# that directory at all, so rather than parse .gemrc ourselves, we allow
330+
# the directory to be set as well, via `bundle config bindir foo`.
331+
Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir
332+
end
333+
334+
def use_system_gems?
335+
configured_bundle_path.use_system_gems?
336+
end
337+
338+
def requires_sudo?
339+
return @requires_sudo if defined?(@requires_sudo_ran)
340+
341+
sudo_present = which "sudo" if settings.allow_sudo?
342+
343+
if sudo_present
344+
# the bundle path and subdirectories need to be writable for RubyGems
345+
# to be able to unpack and install gems without exploding
346+
path = bundle_path
347+
path = path.parent until path.exist?
348+
349+
# bins are written to a different location on OS X
350+
bin_dir = Pathname.new(Bundler.system_bindir)
351+
bin_dir = bin_dir.parent until bin_dir.exist?
352+
353+
# if any directory is not writable, we need sudo
354+
files = [path, bin_dir] | Dir[path.join("build_info/*").to_s] | Dir[path.join("*").to_s]
355+
sudo_needed = files.any? {|f| !File.writable?(f) }
356+
end
357+
358+
@requires_sudo_ran = true
359+
@requires_sudo = settings.allow_sudo? && sudo_present && sudo_needed
360+
end
361+
362+
def mkdir_p(path)
363+
if requires_sudo?
364+
sudo "mkdir -p '#{path}'" unless File.exist?(path)
365+
else
366+
SharedHelpers.filesystem_access(path, :write) do |p|
367+
FileUtils.mkdir_p(p)
368+
end
369+
end
370+
end
371+
372+
def which(executable)
373+
if File.file?(executable) && File.executable?(executable)
374+
executable
375+
elsif paths = ENV["PATH"]
376+
quote = '"'.freeze
377+
paths.split(File::PATH_SEPARATOR).find do |path|
378+
path = path[1..-2] if path.start_with?(quote) && path.end_with?(quote)
379+
executable_path = File.expand_path(executable, path)
380+
return executable_path if File.file?(executable_path) && File.executable?(executable_path)
381+
end
382+
end
383+
end
384+
385+
def sudo(str)
386+
SUDO_MUTEX.synchronize do
387+
prompt = "\n\n" + <<-PROMPT.gsub(/^ {6}/, "").strip + " "
388+
Your user account isn't allowed to install to the system RubyGems.
389+
You can cancel this installation and run:
390+
391+
bundle install --path vendor/bundle
392+
393+
to install the gems into ./vendor/bundle/, or you can enter your password
394+
and install the bundled gems to RubyGems using sudo.
395+
396+
Password:
397+
PROMPT
398+
399+
unless @prompted_for_sudo ||= system(%(sudo -k -p "#{prompt}" true))
400+
raise SudoNotPermittedError,
401+
"Bundler requires sudo access to install at the moment. " \
402+
"Try installing again, granting Bundler sudo access when prompted, or installing into a different path."
403+
end
404+
405+
`sudo -p "#{prompt}" #{str}`
406+
end
407+
end
408+
409+
def read_file(file)
410+
File.open(file, "rb", &:read)
411+
end
412+
413+
def load_marshal(data)
414+
Marshal.load(data)
415+
rescue => e
416+
raise MarshalError, "#{e.class}: #{e.message}"
417+
end
418+
419+
def load_gemspec(file, validate = false)
420+
@gemspec_cache ||= {}
421+
key = File.expand_path(file)
422+
@gemspec_cache[key] ||= load_gemspec_uncached(file, validate)
423+
# Protect against caching side-effected gemspecs by returning a
424+
# new instance each time.
425+
@gemspec_cache[key].dup if @gemspec_cache[key]
426+
end
427+
428+
def load_gemspec_uncached(file, validate = false)
429+
path = Pathname.new(file)
430+
contents = path.read
431+
spec = if contents.start_with?("---") # YAML header
432+
eval_yaml_gemspec(path, contents)
433+
else
434+
# Eval the gemspec from its parent directory, because some gemspecs
435+
# depend on "./" relative paths.
436+
SharedHelpers.chdir(path.dirname.to_s) do
437+
eval_gemspec(path, contents)
438+
end
439+
end
440+
return unless spec
441+
spec.loaded_from = path.expand_path.to_s
442+
Bundler.rubygems.validate(spec) if validate
443+
spec
444+
end
445+
446+
def clear_gemspec_cache
447+
@gemspec_cache = {}
448+
end
449+
450+
def git_present?
451+
return @git_present if defined?(@git_present)
452+
@git_present = Bundler.which("git") || Bundler.which("git.exe")
453+
end
454+
455+
def feature_flag
456+
@feature_flag ||= FeatureFlag.new(VERSION)
457+
end
458+
459+
def reset!
460+
reset_paths!
461+
Plugin.reset!
462+
reset_rubygems!
463+
end
464+
465+
def reset_paths!
466+
@bin_path = nil
467+
@bundler_major_version = nil
468+
@bundle_path = nil
469+
@configured = nil
470+
@configured_bundle_path = nil
471+
@definition = nil
472+
@load = nil
473+
@locked_gems = nil
474+
@root = nil
475+
@settings = nil
476+
@setup = nil
477+
@user_home = nil
478+
end
479+
480+
def reset_rubygems!
481+
return unless defined?(@rubygems) && @rubygems
482+
rubygems.undo_replacements
483+
rubygems.reset
484+
@rubygems = nil
485+
end
486+
487+
private
488+
489+
def eval_yaml_gemspec(path, contents)
490+
Kernel.send(:require, "bundler/psyched_yaml")
491+
492+
# If the YAML is invalid, Syck raises an ArgumentError, and Psych
493+
# raises a Psych::SyntaxError. See psyched_yaml.rb for more info.
494+
Gem::Specification.from_yaml(contents)
495+
rescue YamlLibrarySyntaxError, ArgumentError, Gem::EndOfYAMLException, Gem::Exception
496+
eval_gemspec(path, contents)
497+
end
498+
499+
def eval_gemspec(path, contents)
500+
eval(contents, TOPLEVEL_BINDING.dup, path.expand_path.to_s)
501+
rescue ScriptError, StandardError => e
502+
msg = "There was an error while loading `#{path.basename}`: #{e.message}"
503+
504+
if e.is_a?(LoadError) && RUBY_VERSION >= "1.9"
505+
msg += "\nDoes it try to require a relative path? That's been removed in Ruby 1.9"
506+
end
507+
508+
raise GemspecError, Dsl::DSLError.new(msg, path, e.backtrace, contents)
509+
end
510+
511+
def configure_gem_home_and_path
512+
configure_gem_path
513+
configure_gem_home
514+
bundle_path
515+
end
516+
517+
def configure_gem_path(env = ENV)
518+
blank_home = env["GEM_HOME"].nil? || env["GEM_HOME"].empty?
519+
if !use_system_gems?
520+
# this needs to be empty string to cause
521+
# PathSupport.split_gem_path to only load up the
522+
# Bundler --path setting as the GEM_PATH.
523+
env["GEM_PATH"] = ""
524+
elsif blank_home
525+
possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path]
526+
paths = possibles.flatten.compact.uniq.reject(&:empty?)
527+
env["GEM_PATH"] = paths.join(File::PATH_SEPARATOR)
528+
end
529+
end
530+
531+
def configure_gem_home
532+
Bundler::SharedHelpers.set_env "GEM_HOME", File.expand_path(bundle_path, root)
533+
Bundler.rubygems.clear_paths
534+
end
535+
536+
# @param env [Hash]
537+
def with_env(env)
538+
backup = ENV.to_hash
539+
ENV.replace(env)
540+
yield
541+
ensure
542+
ENV.replace(backup)
543+
end
544+
end
545+
end

0 commit comments

Comments
 (0)
Please sign in to comment.