class Gem::Commands::SetupCommand - Documentation for Ruby 4.0
Description: class Gem::Commands::SetupCommand: Installs RubyGems itself. This command is ordinarily only available from a RubyGems checkout or tarball.
Keywords: ruby,class,Gem::Commands::SetupCommand

🔍

No recent searches

# class Gem::Commands::SetupCommand

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

### Constants

ENV_PATHS
HISTORY_HEADER
VERSION_MATCHER

### Public Class Methods

new ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 15
    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 changelog processing" 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

Calls superclass method `Gem::Command::new`

### Public Instance Methods

bin_file_names ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 664
    def bin_file_names
      @bin_file_names ||= []
    end

default_dir ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 609
    def default_dir
      prefix = options[:prefix]

      if prefix.empty?
        dir = Gem.default_dir
      else
        dir = prefix
      end

      prepend_destdir_if_present(dir)
    end

execute ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 139
    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

      if options[:previous_version].empty?
        options[:previous_version] = Gem::VERSION.sub(/[0-9]+$/, "0")
      end

      options[:previous_version] = Gem::Version.new(options[:previous_version])

      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

files_in (dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 461
    def files_in(dir)
      Dir.chdir dir do
        Dir.glob(File.join("**", "*"), File::FNM_DOTMATCH).
          select {|f| !File.directory?(f) }
      end
    end

generate_default_dirs ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 446
    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

generate_default_man_dir ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 433
    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

install_default_bundler_gem (bin_dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 350
    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)

        # Remove previous default gem executables if they were not shadowed by a regular gem
        FileUtils.rm_rf current_default_spec.full_gem_path if all_specs_current_version.size == 1

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

      new_bundler_spec = Dir.chdir("bundler") { 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)

      bundler_spec = Gem::Specification.load(default_spec_path)

      # 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.
      if File.directory? bundler_spec.gems_dir
        Dir.entries(bundler_spec.gems_dir).
          select {|default_gem| File.basename(default_gem) == full_name }.
          each {|default_gem| rm_r File.join(bundler_spec.gems_dir, default_gem) }
      end

      require_relative "../installer"

      Dir.chdir("bundler") do
        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: default_dir,
            wrappers: true
          )
          # We need to install only executable and default spec files.
          # lib/bundler.rb and lib/bundler/* are available under the site_ruby directory.
          installer.extract_bin
          installer.generate_bin
          installer.write_default_spec
        ensure
          FileUtils.rm_f built_gem
        end
      end

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

      say "Bundler #{new_bundler_spec.version} installed"
    end

install_executables (bin_dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 234
      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

install_file (file, dest_dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 634
    def install_file(file, dest_dir)
      dest_file = File.join dest_dir, file
      dest_dir = File.dirname dest_file
      unless File.directory? dest_dir
        mkdir_p dest_dir, mode: 0o755
      end

      install file, dest_file, mode: options[:data_mode] || 0o644
    end

install_file_list (files, dest_dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 628
    def install_file_list(files, dest_dir)
      files.each do |file|
        install_file file, dest_dir
      end
    end

install_lib (lib_dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 293
    def install_lib(lib_dir)
      libs = { "RubyGems" => "lib" }
      libs["Bundler"] = "bundler/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

install_rdoc ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 307
    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
        Dir[File.join(Gem.dir, "doc", "rubygems-[0-9]*")].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

make_destination_dirs ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 420
    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

prepend_destdir_if_present (path)
Source

    # File lib/rubygems/commands/setup_command.rb, line 621
    def prepend_destdir_if_present(path)
      destdir = options[:destdir]
      return path if destdir.empty?

      File.join(options[:destdir], path.gsub(/^[a-zA-Z]:/, ""))
    end

regenerate_binstubs (bindir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 579
    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

regenerate_plugins (bindir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 595
    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

remove_file_list (files, dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 644
    def remove_file_list(files, dir)
      Dir.chdir dir do
        files.each do |file|
          FileUtils.rm_f file

          warn "unable to remove old file #{file} please remove it by hand" if
            File.exist? file
        end
      end
    end

remove_old_bin_files (bin_dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 468
      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

remove_old_lib_files (lib_dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 500
    def remove_old_lib_files(lib_dir)
      lib_dirs = { File.join(lib_dir, "rubygems") => "lib/rubygems" }
      lib_dirs[File.join(lib_dir, "bundler")] = "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

remove_old_man_files (old_man_dir)
Source

    # File lib/rubygems/commands/setup_command.rb, line 521
    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

shebang ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 283
    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

show_release_notes ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 539
      def show_release_notes
        release_notes = File.join Dir.pwd, "CHANGELOG.md"

        release_notes =
          if File.exist? release_notes
            history = File.read release_notes

            history.force_encoding Encoding::UTF_8

            text = history.split(HISTORY_HEADER)
            text.shift # correct an off-by-one generated by split
            version_lines = history.scan(HISTORY_HEADER)
            versions = history.scan(VERSION_MATCHER).flatten.map do |x|
              Gem::Version.new(x)
            end

            history_string = ""

            until versions.length == 0 ||
                  versions.shift <= options[:previous_version] do
              history_string += version_lines.shift + text.shift
            end

            history_string
          else
            "Oh-no! Unable to find release notes!"
          end

        say release_notes
      end

      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

      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

      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

      private

      def default_dir
        prefix = options[:prefix]

        if prefix.empty?
          dir = Gem.default_dir
        else
          dir = prefix
        end

        prepend_destdir_if_present(dir)
      end

      def prepend_destdir_if_present(path)
        destdir = options[:destdir]
        return path if destdir.empty?

        File.join(options[:destdir], path.gsub(/^[a-zA-Z]:/, ""))
      end

      def install_file_list(files, dest_dir)
        files.each do |file|
          install_file file, dest_dir
        end
      end

      def install_file(file, dest_dir)
        dest_file = File.join dest_dir, file
        dest_dir = File.dirname dest_file
        unless File.directory? dest_dir
          mkdir_p dest_dir, mode: 0o755
        end

        install file, dest_file, mode: options[:data_mode] || 0o644
      end

      def remove_file_list(files, dir)
        Dir.chdir dir do
          files.each do |file|
            FileUtils.rm_f file

            warn "unable to remove old file #{file} please remove it by hand" if
              File.exist? file
          end
        end
      end

      def target_bin_path(bin_dir, bin_file)
        bin_file_formatted = if options[:format_executable]
          Gem.default_exec_format % bin_file
        else
          bin_file
        end
        File.join bin_dir, bin_file_formatted
      end

      def bin_file_names
        @bin_file_names ||= []
      end
    end

target_bin_path (bin_dir, bin_file)
Source

    # File lib/rubygems/commands/setup_command.rb, line 655
    def target_bin_path(bin_dir, bin_file)
      bin_file_formatted = if options[:format_executable]
        Gem.default_exec_format % bin_file
      else
        bin_file
      end
      File.join bin_dir, bin_file_formatted
    end

uninstall_old_gemcutter ()
Source

    # File lib/rubygems/commands/setup_command.rb, line 570
    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