diff options
author | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2019-06-01 12:49:40 +0300 |
---|---|---|
committer | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2019-06-09 12:44:10 +0900 |
commit | 8f37629519ad330032a38ac0e871b2912ed38a1b (patch) | |
tree | bb0529b77583d47993d8b0d608d68896aa3a5298 /lib/bundler/rubygems_integration.rb | |
parent | 66508992483ae5d77b56a98427c50c772341c0ac (diff) |
Merge bundler master from upstream.
Pick from 8dd59e3ba97eb80a599f8149f31bf40773b69dc0
Diffstat (limited to 'lib/bundler/rubygems_integration.rb')
-rw-r--r-- | lib/bundler/rubygems_integration.rb | 472 |
1 files changed, 109 insertions, 363 deletions
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index a4519246a9..9df0897ca2 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -require "monitor" - module Bundler class RubygemsIntegration if defined?(Gem::Ext::Builder::CHDIR_MONITOR) EXT_LOCK = Gem::Ext::Builder::CHDIR_MONITOR else + require "monitor" + EXT_LOCK = Monitor.new end @@ -20,6 +20,7 @@ module Bundler def initialize @replaced_methods = {} + backport_ext_builder_monitor end def version @@ -38,14 +39,22 @@ module Bundler Gem::Command.build_args = args end - def load_path_insert_index - Gem.load_path_insert_index - end - def loaded_specs(name) 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] @@ -110,7 +119,7 @@ module Bundler end def configuration - require "bundler/psyched_yaml" + require_relative "psyched_yaml" Gem.configuration rescue Gem::SystemExitException, LoadError => e Bundler.ui.error "#{e.class}: #{e.message}" @@ -198,10 +207,6 @@ module Bundler Gem::ConfigMap end - def repository_subdirectories - %w[cache doc gems specifications] - end - def clear_paths Gem.clear_paths end @@ -210,26 +215,14 @@ module Bundler Gem.bin_path(gem, bin, ver) end - def path_separator - File::PATH_SEPARATOR - end - def preserve_paths # this is a no-op outside of RubyGems 1.8 yield end def loaded_gem_paths - # RubyGems 2.2+ can put binary extension into dedicated folders, - # therefore use RubyGems facilities to obtain their load paths. - if Gem::Specification.method_defined? :full_require_paths - loaded_gem_paths = Gem.loaded_specs.map {|_, s| s.full_require_paths } - loaded_gem_paths.flatten - else - $LOAD_PATH.select do |p| - Bundler.rubygems.gem_path.any? {|gp| p =~ /^#{Regexp.escape(gp)}/ } - end - end + loaded_gem_paths = Gem.loaded_specs.map {|_, s| s.full_require_paths } + loaded_gem_paths.flatten end def load_plugins @@ -248,36 +241,12 @@ module Bundler EXT_LOCK end - def fetch_specs(all, pre, &blk) - require "rubygems/spec_fetcher" - specs = Gem::SpecFetcher.new.list(all, pre) - specs.each { yield } if block_given? - specs - end - def fetch_prerelease_specs fetch_specs(false, true) rescue Gem::RemoteFetcher::FetchError {} # if we can't download them, there aren't any end - # TODO: This is for older versions of RubyGems... should we support the - # X-Gemfile-Source header on these old versions? - # Maybe the newer implementation will work on older RubyGems? - # It seems difficult to keep this implementation and still send the header. - def fetch_all_remote_specs(remote) - old_sources = Bundler.rubygems.sources - Bundler.rubygems.sources = [remote.uri.to_s] - # Fetch all specs, minus prerelease specs - spec_list = fetch_specs(true, false) - # Then fetch the prerelease specs - fetch_prerelease_specs.each {|k, v| spec_list[k].concat(v) } - - spec_list.values.first - ensure - Bundler.rubygems.sources = old_sources - end - def with_build_args(args) ext_lock.synchronize do old_args = build_args @@ -290,18 +259,9 @@ module Bundler end end - def install_with_build_args(args) - with_build_args(args) { yield } - end - - def gem_from_path(path, policy = nil) - require "rubygems/format" - Gem::Format.from_file_by_path(path, policy) - end - def spec_from_gem(path, policy = nil) require "rubygems/security" - require "bundler/psyched_yaml" + require_relative "psyched_yaml" gem_from_path(path, security_policies[policy]).spec rescue Gem::Package::FormatError raise GemspecError, "Could not read gem at #{path}. It may be corrupted." @@ -317,23 +277,10 @@ module Bundler end end - def build(spec, skip_validation = false) - require "rubygems/builder" - Gem::Builder.new(spec).build - end - def build_gem(gem_dir, spec) build(spec) end - def download_gem(spec, uri, path) - uri = Bundler.settings.mirror_for(uri) - fetcher = Gem::RemoteFetcher.new(configuration[:http_proxy]) - Bundler::Retry.new("download gem from #{uri}").attempts do - fetcher.download(spec, uri, path) - end - end - def security_policy_keys %w[High Medium Low AlmostNo No].map {|level| "#{level}Security" } end @@ -357,12 +304,17 @@ module Bundler end end + # RubyGems-generated binstubs call Kernel#gem def binstubs_call_gem? - true + !provides?(">= 2.5.2") end + # only 2.5.2+ has all of the stub methods we want to use, and since this + # is a performance optimization _only_, + # we'll restrict ourselves to the most + # recent RG versions instead of all versions that have stubs def stubs_provide_full_functionality? - false + provides?(">= 2.5.2") end def replace_gem(specs, specs_by_name) @@ -411,26 +363,6 @@ module Bundler end end - def stub_source_index(specs) - Gem::SourceIndex.send(:alias_method, :old_initialize, :initialize) - redefine_method(Gem::SourceIndex, :initialize) do |*args| - @gems = {} - # You're looking at this thinking: Oh! This is how I make those - # rubygems deprecations go away! - # - # You'd be correct BUT using of this method in production code - # must be approved by the rubygems team itself! - # - # This is your warning. If you use this and don't have approval - # we can't protect you. - # - Deprecate.skip_during do - self.spec_dirs = *args - add_specs(*specs) - end - end - end - # Used to make bin stubs that are not created by bundler work # under bundler. The new Gem.bin_path only considers gems in # +specs+ @@ -531,25 +463,6 @@ module Bundler Gem.clear_paths end - # This backports the correct segment generation code from RubyGems 1.4+ - # by monkeypatching it into the method in RubyGems 1.3.6 and 1.3.7. - def backport_segment_generation - redefine_method(Gem::Version, :segments) do - @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s| - /^\d+$/ =~ s ? s.to_i : s - end - end - end - - # This backport fixes the marshaling of @segments. - def backport_yaml_initialize - redefine_method(Gem::Version, :yaml_initialize) do |_, map| - @version = map["version"] - @segments = nil - @hash = nil - end - end - # This backports base_dir which replaces installation path # RubyGems 1.8+ def backport_base_dir @@ -622,296 +535,129 @@ module Bundler end end - # RubyGems 1.4 through 1.6 - class Legacy < RubygemsIntegration - def initialize - super - backport_base_dir - backport_cache_file - backport_spec_file - backport_yaml_initialize - end + def stub_rubygems(specs) + Gem::Specification.all = specs - def stub_rubygems(specs) - # RubyGems versions lower than 1.7 use SourceIndex#from_gems_in - source_index_class = (class << Gem::SourceIndex; self; end) - redefine_method(source_index_class, :from_gems_in) do |*args| - Gem::SourceIndex.new.tap do |source_index| - source_index.spec_dirs = *args - source_index.add_specs(*specs) - end - end + Gem.post_reset do + Gem::Specification.all = specs end - def all_specs - Gem.source_index.gems.values + redefine_method((class << Gem; self; end), :finish_resolve) do |*| + [] end + end - def find_name(name) - Gem.source_index.find_name(name) - end + def fetch_specs(source, remote, name) + path = source + "#{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 + # it's okay for prerelease to fail + raise e unless name == "prerelease_specs" + end - def validate(spec) - # These versions of RubyGems always validate in "packaging" mode, - # which is too strict for the kinds of checks we care about. As a - # result, validation is disabled on versions of RubyGems below 1.7. - end + def fetch_all_remote_specs(remote) + source = remote.uri.is_a?(URI) ? remote.uri : URI.parse(source.to_s) - def post_reset_hooks - [] - end + specs = fetch_specs(source, remote, "specs") + pres = fetch_specs(source, remote, "prerelease_specs") || [] - def reset - end + specs.concat(pres) end - # RubyGems versions 1.3.6 and 1.3.7 - class Ancient < Legacy - def initialize - super - backport_segment_generation + def download_gem(spec, uri, path) + 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) end end - # RubyGems 1.7 - class Transitional < Legacy - def stub_rubygems(specs) - stub_source_index(specs) - end - - def validate(spec) - # Missing summary is downgraded to a warning in later versions, - # so we set it to an empty string to prevent an exception here. - spec.summary ||= "" - RubygemsIntegration.instance_method(:validate).bind(self).call(spec) - end + def gem_remote_fetcher + require "resolv" + proxy = configuration[:http_proxy] + dns = Resolv::DNS.new + Bundler::GemRemoteFetcher.new(proxy, dns) end - # RubyGems 1.8.5-1.8.19 - class Modern < RubygemsIntegration - def stub_rubygems(specs) - Gem::Specification.all = specs - - Gem.post_reset do - Gem::Specification.all = specs - end + def gem_from_path(path, policy = nil) + require "rubygems/package" + p = Gem::Package.new(path) + p.security_policy = policy if policy + p + end - stub_source_index(specs) - end + def build(spec, skip_validation = false) + require "rubygems/package" + Gem::Package.build(spec, skip_validation) + end - def all_specs - Gem::Specification.to_a - end + def repository_subdirectories + Gem::REPOSITORY_SUBDIRECTORIES + end - def find_name(name) - Gem::Specification.find_all_by_name name - end + def install_with_build_args(args) + yield end - # RubyGems 1.8.0 to 1.8.4 - class AlmostModern < Modern - # RubyGems [>= 1.8.0, < 1.8.5] has a bug that changes Gem.dir whenever - # you call Gem::Installer#install with an :install_dir set. We have to - # change it back for our sudo mode to work. - def preserve_paths - old_dir = gem_dir - old_path = gem_path - yield - Gem.use_paths(old_dir, old_path) - end + def path_separator + Gem.path_separator end - # RubyGems 1.8.20+ - class MoreModern < Modern - # RubyGems 1.8.20 and adds the skip_validation parameter, so that's - # when we start passing it through. - def build(spec, skip_validation = false) - require "rubygems/builder" - Gem::Builder.new(spec).build(skip_validation) + def all_specs + require_relative "remote_specification" + Gem::Specification.stubs.map do |stub| + StubSpecification.from_stub(stub) end end - # RubyGems 2.0 - class Future < RubygemsIntegration - def stub_rubygems(specs) - Gem::Specification.all = specs - - Gem.post_reset do - Gem::Specification.all = specs + def backport_ext_builder_monitor + # So we can avoid requiring "rubygems/ext" in its entirety + Gem.module_eval <<-RB, __FILE__, __LINE__ + 1 + module Ext end + RB - redefine_method((class << Gem; self; end), :finish_resolve) do |*| - [] - end - end - - def all_specs - Gem::Specification.to_a - end - - def find_name(name) - Gem::Specification.find_all_by_name name - end - - def fetch_specs(source, remote, name) - path = source + "#{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 - # it's okay for prerelease to fail - raise e unless name == "prerelease_specs" - end - - def fetch_all_remote_specs(remote) - source = remote.uri.is_a?(URI) ? remote.uri : URI.parse(source.to_s) + require "rubygems/ext/builder" - specs = fetch_specs(source, remote, "specs") - pres = fetch_specs(source, remote, "prerelease_specs") || [] - - specs.concat(pres) - end - - def download_gem(spec, uri, path) - 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::Ext::Builder.class_eval do + unless const_defined?(:CHDIR_MONITOR) + const_set(:CHDIR_MONITOR, EXT_LOCK) end - end - - def gem_remote_fetcher - require "resolv" - proxy = configuration[:http_proxy] - dns = Resolv::DNS.new - Bundler::GemRemoteFetcher.new(proxy, dns) - 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 + remove_const(:CHDIR_MUTEX) if const_defined?(:CHDIR_MUTEX) + const_set(:CHDIR_MUTEX, const_get(:CHDIR_MONITOR)) end end - # RubyGems 2.1.0 - class MoreFuture < Future - def initialize - super - backport_ext_builder_monitor - end - - def all_specs - require "bundler/remote_specification" - Gem::Specification.stubs.map do |stub| - StubSpecification.from_stub(stub) - end - end - - def backport_ext_builder_monitor - # So we can avoid requiring "rubygems/ext" in its entirety - Gem.module_eval <<-RB, __FILE__, __LINE__ + 1 - module Ext - end - RB - - 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 - end - - if Gem::Specification.respond_to?(:stubs_for) - def find_name(name) - Gem::Specification.stubs_for(name).map(&:to_spec) - end - else - def find_name(name) - Gem::Specification.stubs.find_all do |spec| - spec.name == name - end.map(&:to_spec) - end - 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 + if Gem::Specification.respond_to?(:default_stubs) + def default_stubs + Gem::Specification.default_stubs("*.gemspec") end - - def use_gemdeps(gemfile) - ENV["BUNDLE_GEMFILE"] ||= File.expand_path(gemfile) - require "bundler/gemdeps" - runtime = Bundler.setup - Bundler.ui = nil - activated_spec_names = runtime.requested_specs.map(&:to_spec).sort_by(&:name) - [Gemdeps.new(runtime), activated_spec_names] + else + def default_stubs + Gem::Specification.send(:default_stubs, "*.gemspec") end + end - if provides?(">= 2.5.2") - # RubyGems-generated binstubs call Kernel#gem - def binstubs_call_gem? - false - end - - # only 2.5.2+ has all of the stub methods we want to use, and since this - # is a performance optimization _only_, - # we'll restrict ourselves to the most - # recent RG versions instead of all versions that have stubs - def stubs_provide_full_functionality? - true - end - end + def use_gemdeps(gemfile) + ENV["BUNDLE_GEMFILE"] ||= File.expand_path(gemfile) + require_relative "gemdeps" + runtime = Bundler.setup + Bundler.ui = nil + activated_spec_names = runtime.requested_specs.map(&:to_spec).sort_by(&:name) + [Gemdeps.new(runtime), activated_spec_names] end end def self.rubygems - @rubygems ||= if RubygemsIntegration.provides?(">= 2.1.0") - RubygemsIntegration::MoreFuture.new - elsif RubygemsIntegration.provides?(">= 1.99.99") - RubygemsIntegration::Future.new - elsif RubygemsIntegration.provides?(">= 1.8.20") - RubygemsIntegration::MoreModern.new - elsif RubygemsIntegration.provides?(">= 1.8.5") - RubygemsIntegration::Modern.new - elsif RubygemsIntegration.provides?(">= 1.8.0") - RubygemsIntegration::AlmostModern.new - elsif RubygemsIntegration.provides?(">= 1.7.0") - RubygemsIntegration::Transitional.new - elsif RubygemsIntegration.provides?(">= 1.4.0") - RubygemsIntegration::Legacy.new - else # RubyGems 1.3.6 and 1.3.7 - RubygemsIntegration::Ancient.new - end + @rubygems ||= RubygemsIntegration.new end end |