diff options
Diffstat (limited to 'lib/rubygems/commands/pristine_command.rb')
| -rw-r--r-- | lib/rubygems/commands/pristine_command.rb | 196 |
1 files changed, 120 insertions, 76 deletions
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb index fafe35bec1..10978c2af7 100644 --- a/lib/rubygems/commands/pristine_command.rb +++ b/lib/rubygems/commands/pristine_command.rb @@ -1,51 +1,73 @@ # frozen_string_literal: true -require 'rubygems/command' -require 'rubygems/package' -require 'rubygems/installer' -require 'rubygems/version_option' -class Gem::Commands::PristineCommand < Gem::Command +require_relative "../command" +require_relative "../package" +require_relative "../installer" +require_relative "../version_option" +class Gem::Commands::PristineCommand < Gem::Command include Gem::VersionOption def initialize - super 'pristine', - 'Restores installed gems to pristine condition from files located in the gem cache', - :version => Gem::Requirement.default, - :extensions => true, - :extensions_set => false, - :all => false - - add_option('--all', - 'Restore all installed gems to pristine', - 'condition') do |value, options| + super "pristine", + "Restores installed gems to pristine condition from files located in the gem cache", + version: Gem::Requirement.default, + extensions: true, + extensions_set: false, + all: false + + add_option("--all", + "Restore all installed gems to pristine", + "condition") do |value, options| options[:all] = value end - add_option('--skip=gem_name', - 'used on --all, skip if name == gem_name') do |value, options| - options[:skip] = value + add_option("--skip=gem_name", + "used on --all, skip if name == gem_name") do |value, options| + options[:skip] ||= [] + options[:skip] << value end - add_option('--[no-]extensions', - 'Restore gems with extensions', - 'in addition to regular gems') do |value, options| + add_option("--[no-]extensions", + "Restore gems with extensions", + "in addition to regular gems") do |value, options| options[:extensions_set] = true options[:extensions] = value end - add_option('--only-executables', - 'Only restore executables') do |value, options| + add_option("--only-missing-extensions", + "Only restore gems with missing extensions") do |value, options| + options[:only_missing_extensions] = value + end + + add_option("--only-executables", + "Only restore executables") do |value, options| options[:only_executables] = value end - add_option('-E', '--[no-]env-shebang', - 'Rewrite executables with a shebang', - 'of /usr/bin/env') do |value, options| + add_option("--only-plugins", + "Only restore plugins") do |value, options| + options[:only_plugins] = value + end + + add_option("-E", "--[no-]env-shebang", + "Rewrite executables with a shebang", + "of /usr/bin/env") do |value, options| options[:env_shebang] = value end - add_version_option('restore to', 'pristine condition') + add_option("-i", "--install-dir DIR", + "Gem repository to get gems restored") do |value, options| + options[:install_dir] = File.expand_path(value) + end + + add_option("-n", "--bindir DIR", + "Directory where executables are", + "located") do |value, options| + options[:bin_dir] = File.expand_path(value) + end + + add_version_option("restore to", "pristine condition") end def arguments # :nodoc: @@ -53,7 +75,7 @@ class Gem::Commands::PristineCommand < Gem::Command end def defaults_str # :nodoc: - '--extensions' + "--extensions" end def description # :nodoc: @@ -66,6 +88,10 @@ If you have made modifications to an installed gem, the pristine command will revert them. All extensions are rebuilt and all bin stubs for the gem are regenerated after checking for modifications. +Rebuilding extensions also refreshes C-extension gems against updated system +libraries (for example after OS or package upgrades) to avoid mismatches like +outdated library version warnings. + If the cached gem cannot be found it will be downloaded. If --no-extensions is provided pristine will not attempt to restore a gem @@ -81,61 +107,73 @@ extensions will be restored. end def execute - specs = if options[:all] then - Gem::Specification.map - - # `--extensions` must be explicitly given to pristine only gems - # with extensions. - elsif options[:extensions_set] and - options[:extensions] and options[:args].empty? then - Gem::Specification.select do |spec| - spec.extensions and not spec.extensions.empty? - end - else - get_all_gem_names.sort.map do |gem_name| - Gem::Specification.find_all_by_name(gem_name, options[:version]).reverse - end.flatten - end - - if specs.to_a.empty? then - raise Gem::Exception, - "Failed to find gems #{options[:args]} #{options[:version]}" + install_dir = options[:install_dir] + + specification_record = install_dir ? Gem::SpecificationRecord.from_path(install_dir) : Gem::Specification.specification_record + + specs = if options[:all] + specification_record.map + + # `--extensions` must be explicitly given to pristine only gems + # with extensions. + elsif options[:extensions_set] && + options[:extensions] && options[:args].empty? + specification_record.select do |spec| + spec.extensions && !spec.extensions.empty? + end + elsif options[:only_missing_extensions] + specification_record.select(&:missing_extensions?) + else + get_all_gem_names.sort.flat_map do |gem_name| + specification_record.find_all_by_name(gem_name, options[:version]).reverse + end end - install_dir = Gem.dir # TODO use installer option + specs = specs.select {|spec| spec.platform == RUBY_ENGINE || Gem::Platform.local === spec.platform || spec.platform == Gem::Platform::RUBY } + + if specs.to_a.empty? + if options[:only_missing_extensions] + say "No gems with missing extensions to restore" + return + end - raise Gem::FilePermissionError.new(install_dir) unless - File.writable?(install_dir) + raise Gem::Exception, + "Failed to find gems #{options[:args]} #{options[:version]}" + end say "Restoring gems to pristine condition..." - specs.each do |spec| - if spec.default_gem? - say "Skipped #{spec.full_name}, it is a default gem" - next - end + specs.group_by(&:full_name_with_location).values.each do |grouped_specs| + spec = grouped_specs.find {|s| !s.default_gem? } || grouped_specs.first - if spec.name == options[:skip] - say "Skipped #{spec.full_name}, it was given through options" - next + only_executables = options[:only_executables] + only_plugins = options[:only_plugins] + + unless only_executables || only_plugins + # Default gemspecs include changes provided by ruby-core installer that + # can't currently be pristined (inclusion of compiled extension targets in + # the file list). So stick to resetting executables if it's a default gem. + only_executables = true if spec.default_gem? end - if spec.bundled_gem_in_old_ruby? - say "Skipped #{spec.full_name}, it is bundled with old Ruby" - next + if options.key? :skip + if options[:skip].include? spec.name + say "Skipped #{spec.full_name}, it was given through options" + next + end end - unless spec.extensions.empty? or options[:extensions] or options[:only_executables] then - say "Skipped #{spec.full_name}, it needs to compile an extension" + unless spec.extensions.empty? || options[:extensions] || only_executables || only_plugins + say "Skipped #{spec.full_name_with_location}, it needs to compile an extension" next end gem = spec.cache_file - unless File.exist? gem or options[:only_executables] then - require 'rubygems/remote_fetcher' + unless File.exist?(gem) || only_executables || only_plugins + require_relative "../remote_fetcher" - say "Cached gem for #{spec.full_name} not found, attempting to fetch..." + say "Cached gem for #{spec.full_name_with_location} not found, attempting to fetch..." dep = Gem::Dependency.new spec.name, spec.version found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep @@ -150,30 +188,36 @@ extensions will be restored. end env_shebang = - if options.include? :env_shebang then + if options.include? :env_shebang options[:env_shebang] else - install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install'] - install_defaults.to_s['--env-shebang'] + install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS["install"] + install_defaults.to_s["--env-shebang"] end + bin_dir = options[:bin_dir] if options[:bin_dir] + installer_options = { - :wrappers => true, - :force => true, - :install_dir => spec.base_dir, - :env_shebang => env_shebang, - :build_args => spec.build_args, + wrappers: true, + force: true, + install_dir: install_dir || spec.base_dir, + env_shebang: env_shebang, + build_args: spec.build_args, + bin_dir: bin_dir, } - if options[:only_executables] then + if only_executables installer = Gem::Installer.for_spec(spec, installer_options) installer.generate_bin + elsif only_plugins + installer = Gem::Installer.for_spec(spec, installer_options) + installer.generate_plugins else installer = Gem::Installer.at(gem, installer_options) installer.install end - say "Restored #{spec.full_name}" + say "Restored #{spec.full_name_with_location}" end end end |
