diff options
Diffstat (limited to 'lib/rubygems.rb')
| -rw-r--r-- | lib/rubygems.rb | 190 |
1 files changed, 125 insertions, 65 deletions
diff --git a/lib/rubygems.rb b/lib/rubygems.rb index ef8abcb855..d289cab0fd 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -9,16 +9,15 @@ require "rbconfig" module Gem - VERSION = "3.7.0.dev" + VERSION = "4.1.0.dev" end -# Must be first since it unloads the prelude from 1.9.2 -require_relative "rubygems/compatibility" - require_relative "rubygems/defaults" require_relative "rubygems/deprecate" require_relative "rubygems/errors" require_relative "rubygems/target_rbconfig" +require_relative "rubygems/win_platform" +require_relative "rubygems/util/atomic_file_writer" ## # RubyGems is the Ruby standard for publishing and managing third party @@ -39,7 +38,7 @@ require_relative "rubygems/target_rbconfig" # 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 +# * {RubyGems API}[https://guides.rubygems.org/rubygems-org-api/] (also available from # <tt>gem server</tt>) # # == RubyGems Plugins @@ -71,7 +70,7 @@ require_relative "rubygems/target_rbconfig" # == Bugs # # You can submit bugs to the -# {RubyGems bug tracker}[https://github.com/rubygems/rubygems/issues] +# {RubyGems bug tracker}[https://github.com/ruby/rubygems/issues] # on GitHub # # == Credits @@ -107,7 +106,7 @@ require_relative "rubygems/target_rbconfig" # # == License # -# See {LICENSE.txt}[https://github.com/rubygems/rubygems/blob/master/LICENSE.txt] for permissions. +# See {LICENSE.txt}[https://github.com/ruby/rubygems/blob/master/LICENSE.txt] for permissions. # # Thanks! # @@ -116,18 +115,6 @@ require_relative "rubygems/target_rbconfig" module Gem RUBYGEMS_DIR = __dir__ - ## - # An Array of Regexps that match windows Ruby platforms. - - WIN_PATTERNS = [ - /bccwin/i, - /cygwin/i, - /djgpp/i, - /mingw/i, - /mswin/i, - /wince/i, - ].freeze - GEM_DEP_FILES = %w[ gem.deps.rb gems.rb @@ -156,7 +143,12 @@ module Gem specifications/default ].freeze - @@win_platform = nil + ## + # The default value for SOURCE_DATE_EPOCH if not specified. + # We want a date after 1980-01-01, to prevent issues with Zip files. + # This particular timestamp is for 1980-01-02 00:00:00 GMT. + + DEFAULT_SOURCE_DATE_EPOCH = 315_619_200 @configuration = nil @gemdeps = nil @@ -201,11 +193,13 @@ module Gem begin spec.activate rescue Gem::LoadError => e # this could fail due to gem dep collisions, go lax - spec_by_name = Gem::Specification.find_by_name(spec.name) - if spec_by_name.nil? + name = spec.name + spec = Gem::Specification.find_unloaded_by_path(path) + spec ||= Gem::Specification.find_by_name(name) + if spec.nil? raise e else - spec_by_name.activate + spec.activate end end @@ -220,7 +214,7 @@ module Gem finish_resolve rs end - def self.finish_resolve(request_set=Gem::RequestSet.new) + 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) } @@ -242,6 +236,16 @@ module Gem find_spec_for_exe(name, exec_name, requirements).bin_file exec_name end + def self.find_and_activate_spec_for_exe(name, exec_name, requirements) + spec = find_spec_for_exe name, exec_name, requirements + Gem::LOADED_SPECS_MUTEX.synchronize do + spec.activate + finish_resolve + end + spec + end + private_class_method :find_and_activate_spec_for_exe + def self.find_spec_for_exe(name, exec_name, requirements) raise ArgumentError, "you must supply exec_name" unless exec_name @@ -267,6 +271,42 @@ module Gem private_class_method :find_spec_for_exe ## + # Find and load 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_and_load_bin_path(name, exec_name = nil, *requirements) + spec = find_and_activate_spec_for_exe name, exec_name, requirements + + if spec.name == "bundler" + # Old versions of Bundler need a workaround to support nested `bundle + # exec` invocations by overriding `Gem.activate_bin_path`. However, + # RubyGems now uses this new `Gem.activate_and_load_bin_path` helper in + # binstubs, which is of course not overridden in Bundler since it didn't + # exist at the time. So, include the override here to workaround that. + load ENV["BUNDLE_BIN_PATH"] if ENV["BUNDLE_BIN_PATH"] && spec.version <= Gem::Version.create("2.5.22") + + # Make sure there's no version of Bundler in `$LOAD_PATH` that's different + # from the version we just activated. If that was the case (it happens + # when testing Bundler from ruby/ruby), we would load Bundler extensions + # to RubyGems from the copy in `$LOAD_PATH` but then load the binstub from + # an installed copy, causing those copies to be mixed and yet more + # redefinition warnings. + # + require_path = $LOAD_PATH.resolve_feature_path("bundler").last.delete_suffix("/bundler.rb") + Gem.load_bundler_extensions(spec.version) if spec.full_require_paths.include?(require_path) + end + + load spec.bin_file(exec_name) + end + + ## # 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 @@ -278,12 +318,7 @@ module Gem # 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 - spec.activate - finish_resolve - end - spec.bin_file exec_name + find_and_activate_spec_for_exe(name, exec_name, requirements).bin_file exec_name end ## @@ -296,7 +331,7 @@ module Gem ## # The path where gem executables are to be installed. - def self.bindir(install_dir=Gem.dir) + def self.bindir(install_dir = Gem.dir) return File.join install_dir, "bin" unless install_dir.to_s == Gem.default_dir.to_s Gem.default_bindir @@ -305,7 +340,7 @@ module Gem ## # The path were rubygems plugins are to be installed. - def self.plugindir(install_dir=Gem.dir) + def self.plugindir(install_dir = Gem.dir) File.join install_dir, "plugins" end @@ -317,6 +352,8 @@ module Gem def self.clear_paths @paths = nil @user_home = nil + @cache_home = nil + @data_home = nil Gem::Specification.reset Gem::Security.reset if defined?(Gem::Security) end @@ -489,7 +526,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} # 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) + def self.find_files(glob, check_load_path = true) files = [] files = find_files_from_load_path glob if check_load_path @@ -526,7 +563,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} # Unlike find_files, find_latest_files will return only files from the # latest version of a gem. - def self.find_latest_files(glob, check_load_path=true) + def self.find_latest_files(glob, check_load_path = true) files = [] files = find_files_from_load_path glob if check_load_path @@ -604,6 +641,14 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} end @yaml_loaded = false + @use_psych = nil + + ## + # Returns true if the Psych YAML parser is enabled via configuration. + + def self.use_psych? + @use_psych || false + end ## # Loads YAML, preferring Psych @@ -611,9 +656,15 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} def self.load_yaml return if @yaml_loaded - require "psych" - require_relative "rubygems/psych_tree" + @use_psych = ENV["RUBYGEMS_USE_PSYCH"] == "true" || + (defined?(@configuration) && @configuration && !@configuration[:use_psych].nil?) + + if @use_psych + require "psych" + require_relative "rubygems/psych_tree" + end + require_relative "rubygems/yaml_serializer" require_relative "rubygems/safe_yaml" @yaml_loaded = true @@ -630,6 +681,30 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} end ## + # Load Bundler extensions to RubyGems, making sure to avoid redefinition + # warnings in platform constants + + def self.load_bundler_extensions(version) + return unless version <= Gem::Version.create("2.6.9") + + previous_platforms = {} + + platform_const_list = ["JAVA", "MSWIN", "MSWIN64", "MINGW", "X64_MINGW_LEGACY", "X64_MINGW", "UNIVERSAL_MINGW", "WINDOWS", "X64_LINUX", "X64_LINUX_MUSL"] + + platform_const_list.each do |platform| + previous_platforms[platform] = Gem::Platform.const_get(platform) + Gem::Platform.send(:remove_const, platform) + end + + require "bundler/rubygems_ext" + + platform_const_list.each do |platform| + Gem::Platform.send(:remove_const, platform) if Gem::Platform.const_defined?(platform) + Gem::Platform.const_set(platform, previous_platforms[platform]) + end + end + + ## # The file name and line number of the caller of the caller of this method. # # +depth+ is how many layers up the call stack it should go. @@ -777,14 +852,12 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} end ## - # Safely write a file in binary mode on all platforms. + # Atomically write a file in binary mode on all platforms. def self.write_binary(path, data) - File.binwrite(path, data) - rescue Errno::ENOSPC - # If we ran out of space but the file exists, it's *guaranteed* to be corrupted. - File.delete(path) if File.exist?(path) - raise + Gem::AtomicFileWriter.open(path) do |file| + file.write(data) + end end ## @@ -1023,18 +1096,6 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} end ## - # Is this a windows platform? - - def self.win_platform? - if @@win_platform.nil? - ruby_platform = RbConfig::CONFIG["host_os"] - @@win_platform = !WIN_PATTERNS.find {|r| ruby_platform =~ r }.nil? - end - - @@win_platform - end - - ## # Is this a java platform? def self.java_platform? @@ -1155,8 +1216,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} ## # If the SOURCE_DATE_EPOCH environment variable is set, returns it's value. - # Otherwise, returns the time that +Gem.source_date_epoch_string+ was - # first called in the same format as SOURCE_DATE_EPOCH. + # Otherwise, returns DEFAULT_SOURCE_DATE_EPOCH as a string. # # NOTE(@duckinator): The implementation is a tad weird because we want to: # 1. Make builds reproducible by default, by having this function always @@ -1171,15 +1231,12 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} # https://reproducible-builds.org/specs/source-date-epoch/ def self.source_date_epoch_string - # The value used if $SOURCE_DATE_EPOCH is not set. - @default_source_date_epoch ||= Time.now.to_i.to_s - specified_epoch = ENV["SOURCE_DATE_EPOCH"] # If it's empty or just whitespace, treat it like it wasn't set at all. specified_epoch = nil if !specified_epoch.nil? && specified_epoch.strip.empty? - epoch = specified_epoch || @default_source_date_epoch + epoch = specified_epoch || DEFAULT_SOURCE_DATE_EPOCH.to_s epoch.strip end @@ -1243,10 +1300,17 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} prefix_pattern = /^(#{prefix_group})/ end + native_extension_suffixes = Gem.dynamic_library_suffixes.reject(&:empty?) + spec.files.each do |file| if new_format file = file.sub(prefix_pattern, "") - next unless $~ + unless $~ + # Also register native extension files (e.g. date_core.bundle) + # that are listed without require path prefix in the gemspec + next if file.include?("/") + next unless file.end_with?(*native_extension_suffixes) + end end spec.activate if already_loaded?(file) @@ -1370,9 +1434,7 @@ require_relative "rubygems/specification" # REFACTOR: This should be pulled out into some kind of hacks file. begin - ## # Defaults the operating system (or packager) wants to provide for RubyGems. - require "rubygems/defaults/operating_system" rescue LoadError # Ignored @@ -1387,9 +1449,7 @@ rescue StandardError => e end begin - ## # Defaults the Ruby implementation wants to provide for RubyGems - require "rubygems/defaults/#{RUBY_ENGINE}" rescue LoadError end |
