# frozen_string_literal: true
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# See LICENSE.txt for permissions.
# Must be first since it unloads the prelude from 1.9.2
require 'rubygems/compatibility'
require 'rubygems/defaults'
require 'rubygems/deprecate'
require 'rubygems/errors'
# RubyGems is the Ruby standard for publishing and managing third party
# For user documentation, see:
# * <tt>gem help</tt> and <tt>gem help [command]</tt>
# * {RubyGems User Guide}[https://guides.rubygems.org/]
# * {Frequently Asked Questions}[https://guides.rubygems.org/faqs]
# For gem developer documentation see:
# * {Creating Gems}[https://guides.rubygems.org/make-your-own-gem]
# * Gem::Version for version dependency notes
# Further RubyGems documentation can be found at:
# * {RubyGems Guides}[https://guides.rubygems.org]
# * {RubyGems API}[https://www.rubydoc.info/github/rubygems/rubygems] (also available from
# As of RubyGems 1.3.2, RubyGems will load plugins installed in gems or
# $LOAD_PATH. Plugins must be named 'rubygems_plugin' (.rb, .so, etc) and
# placed at the root of your gem's #require_path. Plugins are discovered via
# Gem::find_files and then loaded.
# For an example plugin, see the {Graph gem}[https://github.com/seattlerb/graph]
# which adds a `gem graph` command.
# == RubyGems Defaults, Packaging
# RubyGems defaults are stored in lib/rubygems/defaults.rb. If you're packaging
# RubyGems or implementing Ruby you can change RubyGems' defaults.
# For RubyGems packagers, provide lib/rubygems/defaults/operating_system.rb
# and override any defaults from lib/rubygems/defaults.rb.
# For Ruby implementers, provide lib/rubygems/defaults/#{RUBY_ENGINE}.rb and
# override any defaults from lib/rubygems/defaults.rb.
# If you need RubyGems to perform extra work on install or uninstall, your
# defaults override file can set pre/post install and uninstall hooks.
# See Gem::pre_install, Gem::pre_uninstall, Gem::post_install,
# You can submit bugs to the
# {RubyGems bug tracker}[https://github.com/rubygems/rubygems/issues]
# RubyGems is currently maintained by Eric Hodel.
# RubyGems was originally developed at RubyConf 2003 by:
# * Rich Kilmer -- rich(at)infoether.com
# * Chad Fowler -- chad(at)chadfowler.com
# * David Black -- dblack(at)wobblini.net
# * Paul Brannan -- paul(at)atdesk.com
# * Jim Weirich -- jim(at)weirichhouse.org
# * Gavin Sinclair -- gsinclair(at)soyabean.com.au
# * George Marrows -- george.marrows(at)ntlworld.com
# * Dick Davies -- rasputnik(at)hellooperator.net
# * Mauricio Fernandez -- batsman.geo(at)yahoo.com
# * Simon Strandgaard -- neoneye(at)adslhome.dk
# * Dave Glasser -- glasser(at)mit.edu
# * Paul Duncan -- pabs(at)pablotron.org
# * Ville Aine -- vaine(at)cs.helsinki.fi
# * Eric Hodel -- drbrain(at)segment7.net
# * Daniel Berger -- djberg96(at)gmail.com
# * Phil Hagelberg -- technomancy(at)gmail.com
# * Ryan Davis -- ryand-ruby(at)zenspider.com
# * Evan Phoenix -- evan(at)fallingsnow.net
# * Steve Klabnik -- steve(at)steveklabnik.com
# (If your name is missing, PLEASE let us know!)
# See {LICENSE.txt}[rdoc-ref:lib/rubygems/LICENSE.txt] for permissions.
RUBYGEMS_DIR = File.dirname File.expand_path(__FILE__)
# Taint support is deprecated in Ruby 2.7.
# This allows switching ".untaint" to ".tap(&Gem::UNTAINT)",
# to avoid deprecation warnings in Ruby 2.7.
UNTAINT = RUBY_VERSION < '2.7' ? :untaint.to_sym : proc{}
# An Array of Regexps that match windows Ruby platforms.
# Subdirectories in a gem repository
REPOSITORY_SUBDIRECTORIES = %w[
# Subdirectories in a gem repository for default gems
REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES = %w[
# Exception classes used in a Gem.read_binary +rescue+ statement
READ_BINARY_ERRORS = [Errno::EACCES, Errno::EROFS, Errno::ENOSYS, Errno::ENOTSUP].freeze
# Exception classes used in Gem.write_binary +rescue+ statement
WRITE_BINARY_ERRORS = [Errno::ENOSYS, Errno::ENOTSUP].freeze
LOADED_SPECS_MUTEX = Mutex.new
@path_to_default_spec_map = {}
@post_install_hooks ||= []
@post_uninstall_hooks ||= []
@pre_uninstall_hooks ||= []
@pre_install_hooks ||= []
@default_source_date_epoch = nil
# Try to activate a gem containing +path+. Returns true if
# activation succeeded or wasn't needed because it was already
# activated. Returns false if it can't find the path in a gem.
def self.try_activate(path)
# finds the _latest_ version... regardless of loaded specs and their deps
# if another gem had a requirement that would mean we shouldn't
# activate the latest version, then either it would already be activated
# or if it was ambiguous (and thus unresolved) the code in our custom
# require will try to activate the more specific version.
spec = Gem::Specification.find_by_path path
return true if spec.activated?
rescue Gem::LoadError => e # this could fail due to gem dep collisions, go lax
spec_by_name = Gem::Specification.find_by_name(spec.name)
def self.finish_resolve(request_set=Gem::RequestSet.new)
request_set.import Gem::Specification.unresolved_deps.values
request_set.import Gem.loaded_specs.values.map {|s| Gem::Dependency.new(s.name, s.version) }
request_set.resolve_current.each do |s|
# Find the full path to the executable for gem +name+. If the +exec_name+
# is not given, an exception will be raised, otherwise the
# specified executable's path is returned. +requirements+ allows
# you to specify specific gem versions.
def self.bin_path(name, exec_name = nil, *requirements)
# TODO: fails test_self_bin_path_bin_file_gone_in_latest
# Gem::Specification.find_by_name(name, *requirements).bin_file exec_name
requirements = Gem::Requirement.default if
find_spec_for_exe(name, exec_name, requirements).bin_file exec_name
def self.find_spec_for_exe(name, exec_name, requirements)
raise ArgumentError, "you must supply exec_name" unless exec_name
dep = Gem::Dependency.new name, requirements
loaded = Gem.loaded_specs[name]
return loaded if loaded && dep.matches_spec?(loaded)
specs = dep.matching_specs(true)
specs = specs.find_all do |spec|
spec.executables.include? exec_name
unless spec = specs.first
msg = "can't find gem #{dep} with executable #{exec_name}"
if name == "bundler" && bundler_message = Gem::BundlerVersionFinder.missing_version_message
raise Gem::GemNotFoundException, msg
private_class_method :find_spec_for_exe
# Find the full path to the executable for gem +name+. If the +exec_name+
# is not given, an exception will be raised, otherwise the
# specified executable's path is returned. +requirements+ allows
# you to specify specific gem versions.
# A side effect of this method is that it will activate the gem that
# contains the executable.
# This method should *only* be used in bin stub files.
def self.activate_bin_path(name, exec_name = nil, *requirements) # :nodoc:
spec = find_spec_for_exe name, exec_name, requirements
Gem::LOADED_SPECS_MUTEX.synchronize do
# The mode needed to read a file as straight binary.
# The path where gem executables are to be installed.
def self.bindir(install_dir=Gem.dir)
return File.join install_dir, 'bin' unless
install_dir.to_s == Gem.default_dir.to_s
# Reset the +dir+ and +path+ values. The next time +dir+ or +path+
# is requested, the values will be calculated from scratch. This is
# mainly used by the unit tests to provide test isolation.
Gem::Security.reset if defined?(Gem::Security)
# The path to standard location of the user's .gemrc file.
@config_file ||= File.join Gem.user_home, '.gemrc'
# The standard configuration object for gems.
@configuration ||= Gem::ConfigFile.new []
# Use the given configuration object (which implements the ConfigFile
# protocol) as the standard configuration object.
def self.configuration=(config)
# The path to the data directory specified by the gem name. If the
# package is not available as a gem, return nil.
def self.datadir(gem_name)
spec = @loaded_specs[gem_name]
# A Zlib::Deflate.deflate wrapper
Zlib::Deflate.deflate data
# Retrieve the PathSupport object that RubyGems uses to
@paths ||= Gem::PathSupport.new(ENV)
# Initialize the filesystem paths to use from +env+.
# +env+ is a hash-like object (typically ENV) that
# is queried for 'GEM_HOME', 'GEM_PATH', and 'GEM_SPEC_CACHE'
# Keys for the +env+ hash should be Strings, and values of the hash should
when 'GEM_HOME', 'GEM_PATH', 'GEM_SPEC_CACHE'
unless Gem::Deprecate.skip
Array values in the parameter to `Gem.paths=` are deprecated.
Please use a String or nil.
An Array (#{env.inspect}) was passed in from #{caller[3]}
target[k] = v.join File::PATH_SEPARATOR
@paths = Gem::PathSupport.new ENV.to_hash.merge(target)
Gem::Specification.dirs = @paths.path
# The path where gems are to be installed.
# FIXME deprecate these once everything else has been done -ebh
# Quietly ensure the Gem directory +dir+ contains all the proper
# subdirectories. If we can't create a directory due to a permission
# problem, then we will silently continue.
# If +mode+ is given, missing directories are created with this mode.
# World-writable directories will never be created.
def self.ensure_gem_subdirectories(dir = Gem.dir, mode = nil)
ensure_subdirectories(dir, mode, REPOSITORY_SUBDIRECTORIES)
# Quietly ensure the Gem directory +dir+ contains all the proper
# subdirectories for handling default gems. If we can't create a
# directory due to a permission problem, then we will silently continue.
# If +mode+ is given, missing directories are created with this mode.
# World-writable directories will never be created.
def self.ensure_default_gem_subdirectories(dir = Gem.dir, mode = nil)
ensure_subdirectories(dir, mode, REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES)
def self.ensure_subdirectories(dir, mode, subdirs) # :nodoc:
File.umask old_umask | 002
options[:mode] = mode if mode
subdir = File.join dir, name
next if File.exist? subdir
FileUtils.mkdir_p subdir, **options rescue nil
# The extension API version of ruby. This includes the static vs non-static
# distinction as extensions cannot be shared between the two.
def self.extension_api_version # :nodoc:
if 'no' == RbConfig::CONFIG['ENABLE_SHARED']
"#{ruby_api_version}-static"
# Returns a list of paths matching +glob+ that can be used by a gem to pick
# up features from other gems. For example:
# Gem.find_files('rdoc/discover').each do |path| load path end
# if +check_load_path+ is true (the default), then find_files also searches
# $LOAD_PATH for files as well as gems.
# Note that find_files will return all files even if they are from different
# versions of the same gem. See also find_latest_files
def self.find_files(glob, check_load_path=true)