diff options
Diffstat (limited to 'lib/bundler/rubygems_integration.rb')
-rw-r--r-- | lib/bundler/rubygems_integration.rb | 251 |
1 files changed, 68 insertions, 183 deletions
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index 789797d8d0..da555681f9 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -4,33 +4,24 @@ require "rubygems" unless defined?(Gem) module Bundler class RubygemsIntegration - if defined?(Gem::Ext::Builder::CHDIR_MONITOR) - EXT_LOCK = Gem::Ext::Builder::CHDIR_MONITOR - else - require "monitor" + require "monitor" - EXT_LOCK = Monitor.new - end - - def self.version - @version ||= Gem::Version.new(Gem::VERSION) - end - - def self.provides?(req_str) - Gem::Requirement.new(req_str).satisfied_by?(version) - end + EXT_LOCK = Monitor.new def initialize @replaced_methods = {} - backport_ext_builder_monitor end def version - self.class.version + @version ||= Gem.rubygems_version end def provides?(req_str) - self.class.provides?(req_str) + Gem::Requirement.new(req_str).satisfied_by?(version) + end + + def supports_bundler_trampolining? + provides?(">= 3.3.0.a") end def build_args @@ -47,18 +38,6 @@ module Bundler Gem.loaded_specs[name] end - def add_to_load_path(paths) - return Gem.add_to_load_path(*paths) if Gem.respond_to?(:add_to_load_path) - - if insert_index = Gem.load_path_insert_index - # Gem directories must come after -I and ENV['RUBYLIB'] - $LOAD_PATH.insert(insert_index, *paths) - else - # We are probably testing in core, -I and RUBYLIB don't apply - $LOAD_PATH.unshift(*paths) - end - end - def mark_loaded(spec) if spec.respond_to?(:activated=) current = Gem.loaded_specs[spec.name] @@ -86,22 +65,18 @@ module Bundler def spec_missing_extensions?(spec, default = true) return spec.missing_extensions? if spec.respond_to?(:missing_extensions?) - return false if spec_default_gem?(spec) + return false if spec.default_gem? return false if spec.extensions.empty? default end - def spec_default_gem?(spec) - spec.respond_to?(:default_gem?) && spec.default_gem? - end - def spec_matches_for_glob(spec, glob) return spec.matches_for_glob(glob) if spec.respond_to?(:matches_for_glob) - spec.load_paths.map do |lp| + spec.load_paths.flat_map do |lp| Dir["#{lp}/#{glob}#{suffix_pattern}"] - end.flatten(1) + end end def stub_set_spec(stub, spec) @@ -112,18 +87,6 @@ module Bundler obj.to_s end - def configuration - require_relative "psyched_yaml" - Gem.configuration - rescue Gem::SystemExitException, LoadError => e - Bundler.ui.error "#{e.class}: #{e.message}" - Bundler.ui.trace e - raise - rescue ::Psych::SyntaxError => e - raise YamlSyntaxError.new(e, "Your RubyGems configuration, which is " \ - "usually located in ~/.gemrc, contains invalid YAML syntax.") - end - def ruby_engine Gem.ruby_engine end @@ -136,29 +99,6 @@ module Bundler Gem::Util.inflate(obj) end - def correct_for_windows_path(path) - if Gem::Util.respond_to?(:correct_for_windows_path) - Gem::Util.correct_for_windows_path(path) - elsif path[0].chr == "/" && path[1].chr =~ /[a-z]/i && path[2].chr == ":" - path[1..-1] - else - path - end - end - - def sources=(val) - # Gem.configuration creates a new Gem::ConfigFile, which by default will read ~/.gemrc - # If that file exists, its settings (including sources) will overwrite the values we - # are about to set here. In order to avoid that, we force memoizing the config file now. - configuration - - Gem.sources = val - end - - def sources - Gem.sources - end - def gem_dir Gem.dir end @@ -194,7 +134,7 @@ module Bundler def spec_cache_dirs @spec_cache_dirs ||= begin dirs = gem_path.map {|dir| File.join(dir, "specifications") } - dirs << Gem.spec_cache_dir if Gem.respond_to?(:spec_cache_dir) # Not in RubyGems 2.0.3 or earlier + dirs << Gem.spec_cache_dir dirs.uniq.select {|dir| File.directory? dir } end end @@ -216,18 +156,6 @@ module Bundler loaded_gem_paths.flatten end - def load_plugins - Gem.load_plugins if Gem.respond_to?(:load_plugins) - end - - def load_plugin_files(files) - Gem.load_plugin_files(files) if Gem.respond_to?(:load_plugin_files) - end - - def load_env_plugins - Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins) - end - def ui=(obj) Gem::DefaultUserInteraction.ui = obj end @@ -236,32 +164,9 @@ module Bundler EXT_LOCK end - def with_build_args(args) - ext_lock.synchronize do - old_args = build_args - begin - self.build_args = args - yield - ensure - self.build_args = old_args - end - end - end - - def spec_from_gem(path, policy = nil) - require "rubygems/security" - require_relative "psyched_yaml" - gem_from_path(path, security_policies[policy]).spec - rescue Exception, Gem::Exception, Gem::Security::Exception => e # rubocop:disable Lint/RescueException - if e.is_a?(Gem::Security::Exception) || - e.message =~ /unknown trust policy|unsigned gem/i || - e.message =~ /couldn't verify (meta)?data signature/i - raise SecurityError, - "The gem #{File.basename(path, ".gem")} can't be installed because " \ - "the security policy didn't allow it, with the message: #{e.message}" - else - raise e - end + def spec_from_gem(path) + require "rubygems/package" + Gem::Package.new(path).spec end def build_gem(gem_dir, spec) @@ -283,23 +188,23 @@ module Bundler def reverse_rubygems_kernel_mixin # Disable rubygems' gem activation system - kernel = (class << ::Kernel; self; end) - [kernel, ::Kernel].each do |k| - if k.private_method_defined?(:gem_original_require) - redefine_method(k, :require, k.instance_method(:gem_original_require)) + if Gem.respond_to?(:discover_gems_on_require=) + Gem.discover_gems_on_require = false + else + [::Kernel.singleton_class, ::Kernel].each do |k| + if k.private_method_defined?(:gem_original_require) + redefine_method(k, :require, k.instance_method(:gem_original_require)) + end end end end def replace_gem(specs, specs_by_name) - reverse_rubygems_kernel_mixin - executables = nil - kernel = (class << ::Kernel; self; end) - [kernel, ::Kernel].each do |kernel_class| + [::Kernel.singleton_class, ::Kernel].each do |kernel_class| redefine_method(kernel_class, :gem) do |dep, *reqs| - if executables && executables.include?(File.basename(caller.first.split(":").first)) + if executables&.include?(File.basename(caller.first.split(":").first)) break end @@ -328,11 +233,7 @@ module Bundler e = Gem::LoadError.new(message) e.name = dep.name - if e.respond_to?(:requirement=) - e.requirement = dep.requirement - elsif e.respond_to?(:version_requirement=) - e.version_requirement = dep.requirement - end + e.requirement = dep.requirement raise e end @@ -414,6 +315,14 @@ module Bundler def replace_entrypoints(specs) specs_by_name = add_default_gems_to(specs) + reverse_rubygems_kernel_mixin + begin + # bundled_gems only provide with Ruby 3.3 or later + require "bundled_gems" + rescue LoadError + else + Gem::BUNDLED_GEMS.replace_require(specs) if Gem::BUNDLED_GEMS.respond_to?(:replace_require) + end replace_gem(specs, specs_by_name) stub_rubygems(specs) replace_bin_path(specs_by_name) @@ -503,61 +412,58 @@ module Bundler Gem::Specification.all = specs end - def fetch_specs(remote, name) + def fetch_specs(remote, name, fetcher) + require "rubygems/remote_fetcher" path = remote.uri.to_s + "#{name}.#{Gem.marshal_version}.gz" - fetcher = gem_remote_fetcher - fetcher.headers = { "X-Gemfile-Source" => remote.original_uri.to_s } if remote.original_uri string = fetcher.fetch_path(path) - Bundler.load_marshal(string) - rescue Gem::RemoteFetcher::FetchError => e + specs = Bundler.safe_load_marshal(string) + raise MarshalError, "Specs #{name} from #{remote} is expected to be an Array but was unexpected class #{specs.class}" unless specs.is_a?(Array) + specs + rescue Gem::RemoteFetcher::FetchError # it's okay for prerelease to fail - raise e unless name == "prerelease_specs" + raise unless name == "prerelease_specs" end - def fetch_all_remote_specs(remote) - specs = fetch_specs(remote, "specs") - pres = fetch_specs(remote, "prerelease_specs") || [] + def fetch_all_remote_specs(remote, gem_remote_fetcher) + specs = fetch_specs(remote, "specs", gem_remote_fetcher) + pres = fetch_specs(remote, "prerelease_specs", gem_remote_fetcher) || [] specs.concat(pres) end - def download_gem(spec, uri, path) + def download_gem(spec, uri, cache_dir, fetcher) + require "rubygems/remote_fetcher" uri = Bundler.settings.mirror_for(uri) - fetcher = gem_remote_fetcher - fetcher.headers = { "X-Gemfile-Source" => spec.remote.original_uri.to_s } if spec.remote.original_uri Bundler::Retry.new("download gem from #{uri}").attempts do - fetcher.download(spec, uri, path) + gem_file_name = spec.file_name + local_gem_path = File.join cache_dir, gem_file_name + return if File.exist? local_gem_path + + begin + remote_gem_path = uri + "gems/#{gem_file_name}" + + SharedHelpers.filesystem_access(local_gem_path) do + fetcher.cache_update_path remote_gem_path, local_gem_path + end + rescue Gem::RemoteFetcher::FetchError + raise if spec.original_platform == spec.platform + + original_gem_file_name = "#{spec.original_name}.gem" + raise if gem_file_name == original_gem_file_name + + gem_file_name = original_gem_file_name + retry + end end rescue Gem::RemoteFetcher::FetchError => e raise Bundler::HTTPError, "Could not download gem from #{uri} due to underlying error <#{e.message}>" end - def gem_remote_fetcher - require "rubygems/remote_fetcher" - proxy = configuration[:http_proxy] - Gem::RemoteFetcher.new(proxy) - end - - def gem_from_path(path, policy = nil) - require "rubygems/package" - p = Gem::Package.new(path) - p.security_policy = policy if policy - p - end - def build(spec, skip_validation = false) require "rubygems/package" Gem::Package.build(spec, skip_validation) end - def repository_subdirectories - Gem::REPOSITORY_SUBDIRECTORIES - end - - def install_with_build_args(args) - yield - end - def path_separator Gem.path_separator end @@ -568,37 +474,16 @@ module Bundler end end - def backport_ext_builder_monitor - # So we can avoid requiring "rubygems/ext" in its entirety - Gem.module_eval <<-RUBY, __FILE__, __LINE__ + 1 - module Ext - end - RUBY - - require "rubygems/ext/builder" - - Gem::Ext::Builder.class_eval do - unless const_defined?(:CHDIR_MONITOR) - const_set(:CHDIR_MONITOR, EXT_LOCK) - end - - remove_const(:CHDIR_MUTEX) if const_defined?(:CHDIR_MUTEX) - const_set(:CHDIR_MUTEX, const_get(:CHDIR_MONITOR)) - end + def find_bundler(version) + find_name("bundler").find {|s| s.version.to_s == version } end def find_name(name) Gem::Specification.stubs_for(name).map(&:to_spec) end - if Gem::Specification.respond_to?(:default_stubs) - def default_stubs - Gem::Specification.default_stubs("*.gemspec") - end - else - def default_stubs - Gem::Specification.send(:default_stubs, "*.gemspec") - end + def default_stubs + Gem::Specification.default_stubs("*.gemspec") end end |