diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 1cac0433cd81..7e40280a8cdd 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -1410,23 +1410,26 @@ def default_gem_load_paths MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/".freeze - autoload :ConfigFile, File.expand_path("rubygems/config_file", __dir__) - autoload :CIDetector, File.expand_path("rubygems/ci_detector", __dir__) - autoload :Dependency, File.expand_path("rubygems/dependency", __dir__) - autoload :DependencyList, File.expand_path("rubygems/dependency_list", __dir__) - autoload :Installer, File.expand_path("rubygems/installer", __dir__) - autoload :Licenses, File.expand_path("rubygems/util/licenses", __dir__) - autoload :NameTuple, File.expand_path("rubygems/name_tuple", __dir__) - autoload :PathSupport, File.expand_path("rubygems/path_support", __dir__) - autoload :RequestSet, File.expand_path("rubygems/request_set", __dir__) - autoload :Requirement, File.expand_path("rubygems/requirement", __dir__) - autoload :Resolver, File.expand_path("rubygems/resolver", __dir__) - autoload :Source, File.expand_path("rubygems/source", __dir__) - autoload :SourceList, File.expand_path("rubygems/source_list", __dir__) - autoload :SpecFetcher, File.expand_path("rubygems/spec_fetcher", __dir__) - autoload :SpecificationPolicy, File.expand_path("rubygems/specification_policy", __dir__) - autoload :Util, File.expand_path("rubygems/util", __dir__) - autoload :Version, File.expand_path("rubygems/version", __dir__) + # Autoload by feature name. An absolute path skips the $LOADED_FEATURES + # check, so a second RubyGems tree on the load path gets loaded on top of + # this one. + autoload :ConfigFile, "rubygems/config_file" + autoload :CIDetector, "rubygems/ci_detector" + autoload :Dependency, "rubygems/dependency" + autoload :DependencyList, "rubygems/dependency_list" + autoload :Installer, "rubygems/installer" + autoload :Licenses, "rubygems/util/licenses" + autoload :NameTuple, "rubygems/name_tuple" + autoload :PathSupport, "rubygems/path_support" + autoload :RequestSet, "rubygems/request_set" + autoload :Requirement, "rubygems/requirement" + autoload :Resolver, "rubygems/resolver" + autoload :Source, "rubygems/source" + autoload :SourceList, "rubygems/source_list" + autoload :SpecFetcher, "rubygems/spec_fetcher" + autoload :SpecificationPolicy, "rubygems/specification_policy" + autoload :Util, "rubygems/util" + autoload :Version, "rubygems/version" end require_relative "rubygems/exceptions" diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb index c3dc4c0bcabc..2f3848e52ddd 100644 --- a/lib/rubygems/resolver/api_set.rb +++ b/lib/rubygems/resolver/api_set.rb @@ -5,7 +5,7 @@ # Returns instances of APISpecification. class Gem::Resolver::APISet < Gem::Resolver::Set - autoload :GemParser, File.expand_path("api_set/gem_parser", __dir__) + autoload :GemParser, "rubygems/resolver/api_set/gem_parser" ## # The URI for the Compact Index API this APISet uses. diff --git a/test/rubygems/test_rubygems.rb b/test/rubygems/test_rubygems.rb index 6566b5981e69..6ecd1a0851af 100644 --- a/test/rubygems/test_rubygems.rb +++ b/test/rubygems/test_rubygems.rb @@ -52,8 +52,38 @@ def self.default_dir end end + def test_autoload_does_not_load_a_second_copy_of_the_same_file + # Two RubyGems trees on the load path, as when Bundler runs from a RubyGems + # checkout while the host RubyGems is already loaded. + path = util_install_name_tuple_rb + + output = Gem::Util.popen( + *ruby_with_shadowing_rubygems_in_load_path(path), + "-e", + "require \"rubygems/name_tuple\"; puts $LOADED_FEATURES.count {|f| f.end_with?(\"rubygems/name_tuple.rb\") }", + { err: [:child, :out] } + ).strip + + assert_equal "1", output + end + private + def util_install_name_tuple_rb + dir = Dir.mktmpdir("test_shadowing_rubygems_lib", @tempdir) + + name_tuple_rb = File.join dir, "rubygems", "name_tuple.rb" + + FileUtils.mkdir_p File.dirname(name_tuple_rb) + FileUtils.cp File.join(rubygems_path, "rubygems", "name_tuple.rb"), name_tuple_rb + + dir + end + + def ruby_with_shadowing_rubygems_in_load_path(shadowing_path) + [Gem.ruby, "-I", shadowing_path, *ruby_with_rubygems_in_load_path.drop(1)] + end + def util_install_operating_system_rb(content) dir_lib = Dir.mktmpdir("test_operating_system_lib", @tempdir) dir_lib_arg = File.join dir_lib, "lib"