class Gem::Commands::SetupCommand

Installs RubyGems itself. This command is ordinarily only available from a RubyGems checkout or tarball.

Constants

ENV_PATHS

Public Class Methods

Calls superclass method Gem::Command::new
# File lib/rubygems/commands/setup_command.rb, line 12
def initialize
  super "setup", "Install RubyGems",
        format_executable: false, document: %w[ri],
        force: true,
        site_or_vendor: "sitelibdir",
        destdir: "", prefix: "", previous_version: "",
        regenerate_binstubs: true,
        regenerate_plugins: true

  add_option "--previous-version=VERSION",
             "Previous version of RubyGems",
             "Used for the release notes link" do |version, options|
    options[:previous_version] = version
  end

  add_option "--prefix=PREFIX",
             "Prefix path for installing RubyGems",
             "Will not affect gem repository location" do |prefix, options|
    options[:prefix] = File.expand_path prefix
  end

  add_option "--destdir=DESTDIR",
             "Root directory to install RubyGems into",
             "Mainly used for packaging RubyGems" do |destdir, options|
    options[:destdir] = File.expand_path destdir
  end

  add_option "--[no-]vendor",
             "Install into vendorlibdir not sitelibdir" do |vendor, options|
    options[:site_or_vendor] = vendor ? "vendorlibdir" : "sitelibdir"
  end

  add_option "--[no-]format-executable",
             "Makes `gem` match ruby",
             "If Ruby is ruby18, gem will be gem18" do |value, options|
    options[:format_executable] = value
  end

  add_option "--[no-]document [TYPES]", Array,
             "Generate documentation for RubyGems",
             "List the documentation types you wish to",
             "generate.  For example: rdoc,ri" do |value, options|
    options[:document] = case value
                         when nil   then %w[rdoc ri]
                         when false then []
                         else value
    end
  end

  add_option "--[no-]rdoc",
             "Generate RDoc documentation for RubyGems" do |value, options|
    if value
      options[:document] << "rdoc"
    else
      options[:document].delete "rdoc"
    end

    options[:document].uniq!
  end

  add_option "--[no-]ri",
             "Generate RI documentation for RubyGems" do |value, options|
    if value
      options[:document] << "ri"
    else
      options[:document].delete "ri"
    end

    options[:document].uniq!
  end

  add_option "--[no-]regenerate-binstubs",
             "Regenerate gem binstubs" do |value, options|
    options[:regenerate_binstubs] = value
  end

  add_option "--[no-]regenerate-plugins",
             "Regenerate gem plugins" do |value, options|
    options[:regenerate_plugins] = value
  end

  add_option "-f", "--[no-]force",
             "Forcefully overwrite binstubs" do |value, options|
    options[:force] = 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

  @verbose = nil
end

Public Instance Methods

# File lib/rubygems/commands/setup_command.rb, line 136
def execute
  @verbose = Gem.configuration.really_verbose

  require "fileutils"
  if Gem.configuration.really_verbose
    extend FileUtils::Verbose
  else
    extend FileUtils
  end
  extend MakeDirs

  lib_dir, bin_dir = make_destination_dirs
  man_dir = generate_default_man_dir

  install_lib lib_dir

  install_executables bin_dir

  remove_old_bin_files bin_dir

  remove_old_lib_files lib_dir

  # Can be removed one we drop support for bundler 2.2.3 (the last version installing man files to man_dir)
  remove_old_man_files man_dir if man_dir && File.exist?(man_dir)

  install_default_bundler_gem bin_dir

  if mode = options[:dir_mode]
    @mkdirs.uniq!
    File.chmod(mode, @mkdirs)
  end

  say "RubyGems #{Gem::VERSION} installed"

  regenerate_binstubs(bin_dir) if options[:regenerate_binstubs]
  regenerate_plugins(bin_dir) if options[:regenerate_plugins]

  uninstall_old_gemcutter

  documentation_success = install_rdoc

  say
  if @verbose
    say "-" * 78
    say
  end

  show_release_notes

  say
  say "-" * 78
  say

  say "RubyGems installed the following executables:"
  say bin_file_names.map {|name| "\t#{name}\n" }
  say

  unless bin_file_names.grep(/#{File::SEPARATOR}gem$/)
    say "If `gem` was installed by a previous RubyGems installation, you may need"
    say "to remove it by hand."
    say
  end

  if documentation_success
    if options[:document].include? "rdoc"
      say "Rdoc documentation was installed. You may now invoke:"
      say "  gem server"
      say "and then peruse beautifully formatted documentation for your gems"
      say "with your web browser."
      say "If you do not wish to install this documentation in the future, use the"
      say "--no-document flag, or set it as the default in your ~/.gemrc file. See"
      say "'gem help env' for details."
      say
    end

    if options[:document].include? "ri"
      say "Ruby Interactive (ri) documentation was installed. ri is kind of like man "
      say "pages for Ruby libraries. You may access it like this:"
      say "  ri Classname"
      say "  ri Classname.class_method"
      say "  ri Classname#instance_method"
      say "If you do not wish to install this documentation in the future, use the"
      say "--no-document flag, or set it as the default in your ~/.gemrc file. See"
      say "'gem help env' for details."
      say
    end
  end
end
# File lib/rubygems/commands/setup_command.rb, line 464
def files_in(dir)
  Dir.chdir dir do
    Dir.glob(File.join("**", "*"), File::FNM_DOTMATCH).
      select {|f| !File.directory?(f) }
  end
end
# File lib/rubygems/commands/setup_command.rb, line 449
def generate_default_dirs
  prefix = options[:prefix]
  site_or_vendor = options[:site_or_vendor]

  if prefix.empty?
    lib_dir = RbConfig::CONFIG[site_or_vendor]
    bin_dir = RbConfig::CONFIG["bindir"]
  else
    lib_dir = File.join prefix, "lib"
    bin_dir = File.join prefix, "bin"
  end

  [prepend_destdir_if_present(lib_dir), prepend_destdir_if_present(bin_dir)]
end
# File lib/rubygems/commands/setup_command.rb, line 436
def generate_default_man_dir
  prefix = options[:prefix]

  if prefix.empty?
    man_dir = RbConfig::CONFIG["mandir"]
    return unless man_dir
  else
    man_dir = File.join prefix, "man"
  end

  prepend_destdir_if_present(man_dir)
end
# File lib/rubygems/commands/setup_command.rb, line 340
def install_default_bundler_gem(bin_dir)
  current_default_spec = Gem::Specification.default_stubs.find {|s| s.name == "bundler" }
  specs_dir = if current_default_spec && default_dir == Gem.default_dir
    all_specs_current_version = Gem::Specification.stubs.select {|s| s.full_name == current_default_spec.full_name }

    Gem::Specification.remove_spec current_default_spec
    loaded_from = current_default_spec.loaded_from
    File.delete(loaded_from)

    previous_specs_dir = File.dirname(loaded_from)

    # Remove previous default gem executables if they were not shadowed by a
    # regular gem. They live under the same root as the previous default
    # gemspec, which is not necessarily default_dir.
    if all_specs_current_version.size == 1
      previous_root = File.dirname(File.dirname(previous_specs_dir))
      FileUtils.rm_rf File.join(previous_root, "gems", current_default_spec.full_name)
    end

    previous_specs_dir
  else
    target_specs_dir = File.join(default_dir, "specifications", "default")
    mkdir_p target_specs_dir, mode: 0o755
    target_specs_dir
  end

  # Root directory that specs_dir belongs to. It may differ from default_dir
  # when Gem.default_specifications_dir is customized to live under a
  # different root, like Homebrew does. Executables must be extracted under
  # the same root as the default gemspec, since that's where activation of
  # the default gem will look for them.
  bundler_install_dir = File.dirname(File.dirname(specs_dir))

  new_bundler_spec = Gem::Specification.load("bundler.gemspec")
  full_name = new_bundler_spec.full_name
  gemspec_path = "#{full_name}.gemspec"

  default_spec_path = File.join(specs_dir, gemspec_path)
  Gem.write_binary(default_spec_path, new_bundler_spec.to_ruby)

  # Remove gemspec that was same version of vendored bundler.
  normal_gemspec = File.join(default_dir, "specifications", gemspec_path)
  if File.file? normal_gemspec
    File.delete normal_gemspec
  end

  # Remove gem files that were same version of vendored bundler. A regular
  # gem lives under default_dir, which is not necessarily the same root as
  # the default gemspec.
  normal_gems_dir = File.join(default_dir, "gems")
  if File.directory? normal_gems_dir
    Dir.entries(normal_gems_dir).
      select {|normal_gem| File.basename(normal_gem) == full_name }.
      each {|normal_gem| rm_r File.join(normal_gems_dir, normal_gem) }
  end

  require_relative "../installer"

  built_gem = Gem::Package.build(new_bundler_spec)
  begin
    installer = Gem::Installer.at(
      built_gem,
      env_shebang: options[:env_shebang],
      format_executable: options[:format_executable],
      force: options[:force],
      bin_dir: bin_dir,
      install_dir: bundler_install_dir,
      wrappers: true
    )
    # We only need to install the executables here. The default spec was
    # already written above, and lib/bundler.rb and lib/bundler/* are
    # available under the site_ruby directory.
    installer.extract_bin
    installer.generate_bin
  ensure
    FileUtils.rm_f built_gem
  end

  new_bundler_spec.executables.each {|executable| bin_file_names << target_bin_path(bin_dir, executable) }

  say "Bundler #{new_bundler_spec.version} installed"
end
# File lib/rubygems/commands/setup_command.rb, line 225
  def install_executables(bin_dir)
    prog_mode = options[:prog_mode] || 0o755

    executables = { "gem" => "exe" }
    executables.each do |tool, path|
      say "Installing #{tool} executable" if @verbose

      Dir.chdir path do
        bin_file = "gem"

        require "tmpdir"

        dest_file = target_bin_path(bin_dir, bin_file)
        bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"

        begin
          bin = File.readlines bin_file
          bin[0] = shebang

          File.open bin_tmp_file, "w" do |fp|
            fp.puts bin.join
          end

          install bin_tmp_file, dest_file, mode: prog_mode
          bin_file_names << dest_file
        ensure
          rm bin_tmp_file
        end

        next unless Gem.win_platform?

        begin
          bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat"

          File.open bin_cmd_file, "w" do |file|
            file.puts <<-TEXT
  @ECHO OFF
  @"%~dp0#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
  TEXT
          end

          install bin_cmd_file, "#{dest_file}.bat", mode: prog_mode
        ensure
          rm bin_cmd_file
        end
      end
    end
  end
# File lib/rubygems/commands/setup_command.rb, line 284
def install_lib(lib_dir)
  libs = { "RubyGems" => "lib" }
  libs.each do |tool, path|
    say "Installing #{tool}" if @verbose

    lib_files = files_in path

    Dir.chdir path do
      install_file_list(lib_files, lib_dir)
    end
  end
end
# File lib/rubygems/commands/setup_command.rb, line 297
def install_rdoc
  gem_doc_dir = File.join Gem.dir, "doc"
  rubygems_name = "rubygems-#{Gem::VERSION}"
  rubygems_doc_dir = File.join gem_doc_dir, rubygems_name

  begin
    Gem.ensure_gem_subdirectories Gem.dir
  rescue SystemCallError
    # ignore
  end

  if File.writable?(gem_doc_dir) &&
     (!File.exist?(rubygems_doc_dir) ||
      File.writable?(rubygems_doc_dir))
    say "Removing old RubyGems RDoc and ri" if @verbose
    Gem::Util.glob_files_in_dir("rubygems-[0-9]*", gem_doc_dir).each do |dir|
      rm_rf dir
    end

    require_relative "../rdoc"

    return false unless defined?(Gem::RDoc)

    fake_spec = Gem::Specification.new "rubygems", Gem::VERSION
    def fake_spec.full_gem_path
      File.expand_path "../../..", __dir__
    end

    generate_ri   = options[:document].include? "ri"
    generate_rdoc = options[:document].include? "rdoc"

    rdoc = Gem::RDoc.new fake_spec, generate_rdoc, generate_ri
    rdoc.generate

    return true
  elsif @verbose
    say "Skipping RDoc generation, #{gem_doc_dir} not writable"
    say "Set the GEM_HOME environment variable if you want RDoc generated"
  end

  false
end
# File lib/rubygems/commands/setup_command.rb, line 423
def make_destination_dirs
  lib_dir, bin_dir = Gem.default_rubygems_dirs

  unless lib_dir
    lib_dir, bin_dir = generate_default_dirs
  end

  mkdir_p lib_dir, mode: 0o755
  mkdir_p bin_dir, mode: 0o755

  [lib_dir, bin_dir]
end
# File lib/rubygems/commands/setup_command.rb, line 564
def regenerate_binstubs(bindir)
  require_relative "pristine_command"
  say "Regenerating binstubs"

  args = %w[--all --only-executables --silent]
  args << "--bindir=#{bindir}"
  args << "--install-dir=#{default_dir}"

  if options[:env_shebang]
    args << "--env-shebang"
  end

  command = Gem::Commands::PristineCommand.new
  command.invoke(*args)
end
# File lib/rubygems/commands/setup_command.rb, line 580
def regenerate_plugins(bindir)
  require_relative "pristine_command"
  say "Regenerating plugins"

  args = %w[--all --only-plugins --silent]
  args << "--bindir=#{bindir}"
  args << "--install-dir=#{default_dir}"

  command = Gem::Commands::PristineCommand.new
  command.invoke(*args)
end
# File lib/rubygems/commands/setup_command.rb, line 471
  def remove_old_bin_files(bin_dir)
    old_bin_files = {
      "gem_mirror" => "gem mirror",
      "gem_server" => "gem server",
      "gemlock" => "gem lock",
      "gemri" => "ri",
      "gemwhich" => "gem which",
      "index_gem_repository.rb" => "gem generate_index",
    }

    old_bin_files.each do |old_bin_file, new_name|
      old_bin_path = File.join bin_dir, old_bin_file
      next unless File.exist? old_bin_path

      deprecation_message = "`#{old_bin_file}` has been deprecated. Use `#{new_name}` instead."

      File.open old_bin_path, "w" do |fp|
        fp.write <<-EOF
#!#{Gem.ruby}

abort "#{deprecation_message}"
    EOF
      end

      next unless Gem.win_platform?

      File.open "#{old_bin_path}.bat", "w" do |fp|
        fp.puts %(@ECHO.#{deprecation_message})
      end
    end
  end
# File lib/rubygems/commands/setup_command.rb, line 503
def remove_old_lib_files(lib_dir)
  lib_dirs = { File.join(lib_dir, "rubygems") => "lib/rubygems" }
  lib_dirs[File.join(lib_dir, "bundler")] = "lib/bundler"
  lib_dirs.each do |old_lib_dir, new_lib_dir|
    lib_files = files_in(new_lib_dir)

    old_lib_files = files_in(old_lib_dir)

    to_remove = old_lib_files - lib_files

    gauntlet_rubygems = File.join(lib_dir, "gauntlet_rubygems.rb")
    to_remove << gauntlet_rubygems if File.exist? gauntlet_rubygems

    to_remove.delete_if do |file|
      file.start_with? "defaults"
    end

    remove_file_list(to_remove, old_lib_dir)
  end
end
# File lib/rubygems/commands/setup_command.rb, line 524
def remove_old_man_files(old_man_dir)
  old_man1_dir = "#{old_man_dir}/man1"

  if File.exist?(old_man1_dir)
    man1_to_remove = Dir.chdir(old_man1_dir) { Dir["bundle*.1{,.txt,.ronn}"] }

    remove_file_list(man1_to_remove, old_man1_dir)
  end

  old_man5_dir = "#{old_man_dir}/man5"

  if File.exist?(old_man5_dir)
    man5_to_remove = Dir.chdir(old_man5_dir) { Dir["gemfile.5{,.txt,.ronn}"] }

    remove_file_list(man5_to_remove, old_man5_dir)
  end
end
# File lib/rubygems/commands/setup_command.rb, line 274
def shebang
  if options[:env_shebang]
    ruby_name = RbConfig::CONFIG["ruby_install_name"]
    @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
    "#!#{@env_path} #{ruby_name}\n"
  else
    "#!#{Gem.ruby}\n"
  end
end
# File lib/rubygems/commands/setup_command.rb, line 542
def show_release_notes
  ref = Gem::VERSION.include?(".dev") ? "master" : "v#{Gem::VERSION}"
  link = "https://github.com/ruby/rubygems/blob/#{ref}/CHANGELOG.md"

  previous = options[:previous_version].to_s.strip
  if previous.empty? || !Gem::Version.correct?(previous) ||
     Gem::Version.new(previous) >= Gem::Version.new(Gem::VERSION)
    say "See #{link} for the changes."
  else
    say "See #{link} for the changes since #{previous}."
  end
end
# File lib/rubygems/commands/setup_command.rb, line 555
def uninstall_old_gemcutter
  require_relative "../uninstaller"

  ui = Gem::Uninstaller.new("gemcutter", all: true, ignore: true,
                                         version: "< 0.4")
  ui.uninstall
rescue Gem::InstallError
end