class Gem::Installer

def self.at(path, options = {})

def self.at(path, options = {})
  security_policy = options[:security_policy]
  package = Gem::Package.new path, security_policy
  new package, options
end

def self.for_spec(spec, options = {})

def self.for_spec(spec, options = {})
  # FIXME: we should have a real Package class for this
  new FakePackage.new(spec), options
end

def app_script_text(bin_file_name)

def app_script_text(bin_file_name)
  # NOTE: that the `load` lines cannot be indented, as old RG versions match
  # against the beginning of the line
  <<~TEXT
    #{shebang bin_file_name}
    #
    # This file was generated by RubyGems.
    #
    # The application '#{spec.name}' is installed as part of a gem, and
    # this file is here to facilitate running it.
    #
    require 'rubygems'
    #{gemdeps_load(spec.name)}
    version = "#{Gem::Requirement.default_prerelease}"
    str = ARGV.first
    if str
      str = str.b[/\\A_(.*)_\\z/, 1]
      if str and Gem::Version.correct?(str)
        #{explicit_version_requirement(spec.name)}
        ARGV.shift
      end
    end
    if Gem.respond_to?(:activate_and_load_bin_path)
      Gem.activate_and_load_bin_path('#{spec.name}', '#{bin_file_name}', version)
    else
      load Gem.activate_bin_path('#{spec.name}', '#{bin_file_name}', version)
    end
  TEXT
end

def bash_prolog_script

def bash_prolog_script
  if load_relative_enabled?
    <<~EOS
      #!/bin/sh
      # -*- ruby -*-
      _=_\\
      =begin
      bindir="${0%/*}"
      ruby="$bindir/#{ruby_install_name}"
      if [ ! -f "$ruby" ]; then
        ruby="#{ruby_install_name}"
      fi
      exec "$ruby" "-x" "$0" "$@"
      =end
    EOS
  else
    ""
  end
end

def build_args

def build_args
  @build_args ||= begin
                    require_relative "command"
                    Gem::Command.build_args
                  end
end

def build_extensions

def build_extensions
  builder = Gem::Ext::Builder.new spec, build_args, Gem.target_rbconfig, build_jobs
  builder.build_extensions
end

def build_jobs

def build_jobs
  @build_jobs ||= begin
                    require "etc"
                    Etc.nprocessors + 1
                  rescue LoadError
                    1
                  end
end

def check_executable_overwrite(filename) # :nodoc:

:nodoc:
def check_executable_overwrite(filename) # :nodoc:
  return if @force
  generated_bin = File.join @bin_dir, formatted_program_filename(filename)
  return unless File.exist? generated_bin
  ruby_executable = false
  existing = nil
  File.open generated_bin, "rb" do |io|
    line = io.gets
    shebang = /^#!.*ruby/o
    # TruffleRuby uses a bash prelude in default launchers
    if load_relative_enabled? || RUBY_ENGINE == "truffleruby"
      until line.nil? || shebang.match?(line) do
        line = io.gets
      end
    end
    next unless line&.match?(shebang)
    io.gets # blankline
    # TODO: detect a specially formatted comment instead of trying
    # to find a string inside Ruby code.
    next unless io.gets&.include?("This file was generated by RubyGems")
    ruby_executable = true
    existing = io.read.slice(/
        ^\s*(
          Gem\.activate_and_load_bin_path\( |
          load \s Gem\.activate_bin_path\(
        )
        (['"])(.*?)(\2),
      /x, 3)
  end
  return if spec.name == existing
  # somebody has written to RubyGems' directory, overwrite, too bad
  return if Gem.default_bindir != @bin_dir && !ruby_executable
  question = "#{spec.name}'s executable \"#{filename}\" conflicts with ".dup
  if ruby_executable
    question << (existing || "an unknown executable")
    return if ask_yes_no "#{question}\nOverwrite the executable?", false
    conflict = "installed executable from #{existing}"
  else
    question << generated_bin
    return if ask_yes_no "#{question}\nOverwrite the executable?", false
    conflict = generated_bin
  end
  raise Gem::InstallError,
    "\"#{filename}\" from #{spec.name} conflicts with #{conflict}"
end

def check_that_user_bin_dir_is_in_path(executables) # :nodoc:

:nodoc:
def check_that_user_bin_dir_is_in_path(executables) # :nodoc:
  user_bin_dir = @bin_dir || Gem.bindir(gem_home)
  user_bin_dir = user_bin_dir.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
  path = ENV["PATH"]
  path = path.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
  if Gem.win_platform?
    path = path.downcase
    user_bin_dir = user_bin_dir.downcase
  end
  path = path.split(File::PATH_SEPARATOR)
  unless path.include? user_bin_dir
    unless !Gem.win_platform? && (path.include? user_bin_dir.sub(ENV["HOME"], "~"))
      alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t  gem executables (#{executables.join(", ")}) will not run."
    end
  end
end

def default_spec_dir

def default_spec_dir
  dir = File.join(gem_home, "specifications", "default")
  FileUtils.mkdir_p dir
  dir
end

def default_spec_file

def default_spec_file
  File.join default_spec_dir, "#{spec.full_name}.gemspec"
end

def dir

def dir
  gem_dir.to_s
end

def ensure_dependencies_met # :nodoc:

:nodoc:
def ensure_dependencies_met # :nodoc:
  deps = spec.runtime_dependencies
  deps |= spec.development_dependencies if @development
  deps.each do |dep_gem|
    ensure_dependency spec, dep_gem
  end
end

def ensure_dependency(spec, dependency)

def ensure_dependency(spec, dependency)
  unless installation_satisfies_dependency? dependency
    raise Gem::InstallError, "#{spec.name} requires #{dependency}"
  end
  true
end

def ensure_loadable_spec

def ensure_loadable_spec
  ruby = spec.to_ruby_for_cache
  begin
    eval ruby
  rescue StandardError, SyntaxError => e
    raise Gem::InstallError,
          "The specification for #{spec.full_name} is corrupt (#{e.class})"
  end
end

def ensure_writable_dir(dir) # :nodoc:

:nodoc:
def ensure_writable_dir(dir) # :nodoc:
  require "fileutils"
  FileUtils.mkdir_p dir, mode: options[:dir_mode] && 0o755
  raise Gem::FilePermissionError.new(dir) unless File.writable? dir
end

def exec_format

Defaults to use Ruby's program prefix and suffix.
def exec_format
  @exec_format ||= Gem.default_exec_format
end

def explicit_version_requirement(name)

def explicit_version_requirement(name)
  code = "version = str"
  return code unless name == "bundler"
  code += <<~TEXT
    ENV['BUNDLER_VERSION'] = str
  TEXT
end

def extract_bin

def extract_bin
  @package.extract_files gem_dir, "#{spec.bindir}/*"
end

def extract_files

def extract_files
  @package.extract_files gem_dir
end

def formatted_program_filename(filename)

def formatted_program_filename(filename)
  if @format_executable
    self.class.exec_format % File.basename(filename)
  else
    filename
  end
end

def gem

def gem
  @package.gem.path
end

def gem_dir

def gem_dir
  @gem_dir ||= File.join(gem_home, "gems", spec.full_name)
end

def gemdeps_load(name)

def gemdeps_load(name)
  return "" if name == "bundler"
  <<~TEXT
    Gem.use_gemdeps
  TEXT
end

def generate_bin # :nodoc:

:nodoc:
def generate_bin # :nodoc:
  executables = spec.executables
  return if executables.nil? || executables.empty?
  if @gem_home == Gem.user_dir
    # If we get here, then one of the following likely happened:
    # - `--user-install` was specified
    # - `Gem::PathSupport#home` fell back to `Gem.user_dir`
    # - GEM_HOME was manually set to `Gem.user_dir`
    check_that_user_bin_dir_is_in_path(executables)
  end
  ensure_writable_dir @bin_dir
  executables.each do |filename|
    bin_path = File.join gem_dir, spec.bindir, filename
    next unless File.exist? bin_path
    mode = File.stat(bin_path).mode
    dir_mode = options[:prog_mode] || (mode | 0o111)
    unless dir_mode == mode
      File.chmod dir_mode, bin_path
    end
    check_executable_overwrite filename
    if @wrappers
      generate_bin_script filename, @bin_dir
    else
      generate_bin_symlink filename, @bin_dir
    end
  end
end

def generate_bin_script(filename, bindir)

def generate_bin_script(filename, bindir)
  bin_script_path = File.join bindir, formatted_program_filename(filename)
  Gem.open_file_with_lock(bin_script_path) do
    require "fileutils"
    FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
    File.open(bin_script_path, "wb", 0o755) do |file|
      file.write app_script_text(filename)
      file.chmod(options[:prog_mode] || 0o755)
    end
  end
  verbose bin_script_path
  generate_windows_script filename, bindir
end

def generate_bin_symlink(filename, bindir)

def generate_bin_symlink(filename, bindir)
  src = File.join gem_dir, spec.bindir, filename
  dst = File.join bindir, formatted_program_filename(filename)
  if File.exist? dst
    if File.symlink? dst
      link = File.readlink(dst).split File::SEPARATOR
      cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ""))
      return if spec.version < cur_version
    end
    File.unlink dst
  end
  FileUtils.symlink src, dst, verbose: Gem.configuration.really_verbose
rescue NotImplementedError, SystemCallError
  alert_warning "Unable to use symlinks, installing wrapper"
  generate_bin_script filename, bindir
end

def generate_plugins # :nodoc:

:nodoc:
def generate_plugins # :nodoc:
  latest = Gem::Specification.latest_spec_for(spec.name)
  return if latest && latest.version > spec.version
  ensure_writable_dir @plugins_dir
  if spec.plugins.empty?
    remove_plugins_for(spec, @plugins_dir)
  else
    regenerate_plugins_for(spec, @plugins_dir)
  end
rescue ArgumentError => e
  raise e, "#{latest.name} #{latest.version} #{spec.name} #{spec.version}: #{e.message}"
end

def generate_windows_script(filename, bindir)

def generate_windows_script(filename, bindir)
  if Gem.win_platform?
    script_name = formatted_program_filename(filename) + ".bat"
    script_path = File.join bindir, File.basename(script_name)
    File.open script_path, "w" do |file|
      file.puts windows_stub_script(bindir, filename)
    end
    verbose script_path
  end
end

def initialize(package, options = {})

def initialize(package, options = {})
  require "fileutils"
  @options = options
  @package = package
  process_options
  @package.dir_mode = options[:dir_mode]
  @package.prog_mode = options[:prog_mode]
  @package.data_mode = options[:data_mode]
end

def install

def install
  pre_install_checks
  run_pre_install_hooks
  # Set loaded_from to ensure extension_dir is correct
  spec.loaded_from = spec_file
  # Completely remove any previous gem files
  FileUtils.rm_rf gem_dir
  FileUtils.rm_rf spec.extension_dir
  dir_mode = options[:dir_mode]
  FileUtils.mkdir_p gem_dir, mode: dir_mode && 0o755
  extract_files
  build_extensions
  write_build_info_file
  run_post_build_hooks
  generate_bin
  generate_plugins
  write_spec
  write_cache_file
  File.chmod(dir_mode, gem_dir) if dir_mode
  say spec.post_install_message if options[:post_install_message] && !spec.post_install_message.nil?
  Gem::Specification.add_spec(spec) unless @install_dir
  load_plugin
  run_post_install_hooks
  spec
rescue Errno::EACCES => e
  # Permission denied - /path/to/foo
  raise Gem::FilePermissionError, e.message.split(" - ").last
end

def installation_satisfies_dependency?(dependency)

def installation_satisfies_dependency?(dependency)
  return true if @options[:development] && dependency.type == :development
  return true if installed_specs.detect {|s| dependency.matches_spec? s }
  return false if @only_install_dir
  !dependency.matching_specs.empty?
end

def installed_specs

def installed_specs
  @installed_specs ||= begin
    specs = []
    Gem::Util.glob_files_in_dir("*.gemspec", File.join(gem_home, "specifications")).each do |path|
      spec = Gem::Specification.load path
      specs << spec if spec
    end
    specs
  end
end

def load_plugin

def load_plugin
  specs = Gem::Specification.find_all_by_name(spec.name)
  # If old version already exists, this plugin isn't loaded
  # immediately. It's for avoiding a case that multiple versions
  # are loaded at the same time.
  return unless specs.size == 1
  plugin_files = spec.plugins.map do |plugin|
    File.join(@plugins_dir, "#{spec.name}_plugin#{File.extname(plugin)}")
  end
  Gem.load_plugin_files(plugin_files)
end

def load_relative_enabled?

def load_relative_enabled?
  rb_config["LIBRUBY_RELATIVE"] == "yes"
end

def pre_install_checks

def pre_install_checks
  verify_gem_home
  # The name and require_paths must be verified first, since it could contain
  # ruby code that would be eval'ed in #ensure_loadable_spec
  verify_spec
  ensure_loadable_spec
  Gem.ensure_gem_subdirectories gem_home
  return true if @force
  ensure_dependencies_met unless @ignore_dependencies
  true
end

def process_options # :nodoc:

:nodoc:
def process_options # :nodoc:
  @options = {
    bin_dir: nil,
    env_shebang: false,
    force: false,
    only_install_dir: false,
    post_install_message: true,
  }.merge options
  @env_shebang         = options[:env_shebang]
  @force               = options[:force]
  @install_dir         = options[:install_dir]
  @user_install        = options[:user_install]
  @ignore_dependencies = options[:ignore_dependencies]
  @format_executable   = options[:format_executable]
  @wrappers            = options[:wrappers]
  @only_install_dir    = options[:only_install_dir]
  @bin_dir             = options[:bin_dir]
  @development         = options[:development]
  @build_root          = options[:build_root]
  @build_args = options[:build_args]
  @build_jobs = options[:build_jobs]
  @gem_home = @install_dir || user_install_dir || Gem.dir
  # If the user has asked for the gem to be installed in a directory that is
  # the system gem directory, then use the system bin directory, else create
  # (or use) a new bin dir under the gem_home.
  @bin_dir ||= Gem.bindir(@gem_home)
  @plugins_dir = Gem.plugindir(@gem_home)
  unless @build_root.nil?
    @bin_dir = File.join(@build_root, @bin_dir.gsub(/^[a-zA-Z]:/, ""))
    @gem_home = File.join(@build_root, @gem_home.gsub(/^[a-zA-Z]:/, ""))
    @plugins_dir = File.join(@build_root, @plugins_dir.gsub(/^[a-zA-Z]:/, ""))
    alert_warning "You build with buildroot.\n  Build root: #{@build_root}\n  Bin dir: #{@bin_dir}\n  Gem home: #{@gem_home}\n  Plugins dir: #{@plugins_dir}"
  end
end

def rb_config

def rb_config
  Gem.target_rbconfig
end

def ruby_install_name

def ruby_install_name
  rb_config["ruby_install_name"]
end

def run_post_build_hooks # :nodoc:

:nodoc:
def run_post_build_hooks # :nodoc:
  Gem.post_build_hooks.each do |hook|
    next unless hook.call(self) == false
    FileUtils.rm_rf gem_dir
    location = " at #{$1}" if hook.inspect =~ /[ @](.*:\d+)/
    message = "post-build hook#{location} failed for #{spec.full_name}"
    raise Gem::InstallError, message
  end
end

def run_post_install_hooks # :nodoc:

:nodoc:
def run_post_install_hooks # :nodoc:
  Gem.post_install_hooks.each do |hook|
    hook.call self
  end
end

def run_pre_install_hooks # :nodoc:

:nodoc:
def run_pre_install_hooks # :nodoc:
  Gem.pre_install_hooks.each do |hook|
    next unless hook.call(self) == false
    location = " at #{$1}" if hook.inspect =~ /[ @](.*:\d+)/
    message = "pre-install hook#{location} failed for #{spec.full_name}"
    raise Gem::InstallError, message
  end
end

def shebang(bin_file_name)

def shebang(bin_file_name)
  path = File.join gem_dir, spec.bindir, bin_file_name
  first_line = File.open(path, "rb", &:gets) || ""
  if first_line.start_with?("#!")
    # Preserve extra words on shebang line, like "-w".  Thanks RPA.
    shebang = first_line.sub(/\A\#!.*?ruby\S*((\s+\S+)+)/, "#!#{Gem.ruby}")
    opts = $1
    shebang.strip! # Avoid nasty ^M issues.
  end
  if which = Gem.configuration[:custom_shebang]
    # replace bin_file_name with "ruby" to avoid endless loops
    which = which.gsub(/ #{bin_file_name}$/," #{ruby_install_name}")
    which = which.gsub(/\$(\w+)/) do
      case $1
      when "env"
        @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
      when "ruby"
        "#{Gem.ruby}#{opts}"
      when "exec"
        bin_file_name
      when "name"
        spec.name
      end
    end
    "#!#{which}"
  elsif @env_shebang
    # Create a plain shebang line.
    @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
    "#!#{@env_path} #{ruby_install_name}"
  else
    "#{bash_prolog_script}#!#{Gem.ruby}#{opts}"
  end
end

def spec

def spec
  @package.spec
end

def spec_file

def spec_file
  File.join gem_home, "specifications", "#{spec.full_name}.gemspec"
end

def user_install_dir

def user_install_dir
  # never install to user home in --build-root mode
  return unless @build_root.nil?
  # Please note that @user_install might have three states:
  # * `true`: `--user-install`
  # * `false`: `--no-user-install` and
  # * `nil`: option was not specified
  if @user_install || (@user_install.nil? && Gem.default_user_install)
    Gem.user_dir
  end
end

def verify_gem_home # :nodoc:

:nodoc:
def verify_gem_home # :nodoc:
  FileUtils.mkdir_p gem_home, mode: options[:dir_mode] && 0o755
end

def verify_spec

def verify_spec
  unless Gem::Specification::VALID_NAME_PATTERN.match?(spec.name)
    raise Gem::InstallError, "#{spec} has an invalid name"
  end
  if spec.raw_require_paths.any? {|path| path =~ /\R/ }
    raise Gem::InstallError, "#{spec} has an invalid require_paths"
  end
  if spec.extensions.any? {|ext| ext =~ /\R/ }
    raise Gem::InstallError, "#{spec} has an invalid extensions"
  end
  if /\R/.match?(spec.platform.to_s)
    raise Gem::InstallError, "#{spec.platform} is an invalid platform"
  end
  unless /\A\d+\z/.match?(spec.specification_version.to_s)
    raise Gem::InstallError, "#{spec} has an invalid specification_version"
  end
  if spec.dependencies.any? {|dep| dep.type != :runtime && dep.type != :development }
    raise Gem::InstallError, "#{spec} has an invalid dependencies"
  end
  if spec.dependencies.any? {|dep| dep.name =~ /(?:\R|[<>])/ }
    raise Gem::InstallError, "#{spec} has an invalid dependencies"
  end
end

def windows_stub_script(bindir, bin_file_name)

def windows_stub_script(bindir, bin_file_name)
  rb_topdir = RbConfig::TOPDIR || File.dirname(rb_config["bindir"])
  # get ruby executable file name from RbConfig
  ruby_exe = "#{rb_config["RUBY_INSTALL_NAME"]}#{rb_config["EXEEXT"]}"
  ruby_exe = "ruby.exe" if ruby_exe.empty?
  if File.exist?(File.join(bindir, ruby_exe))
    # stub & ruby.exe within same folder.  Portable
    <<~TEXT
      @ECHO OFF
      @"%~dp0#{ruby_exe}" "%~dpn0" %*
    TEXT
  elsif bindir.downcase.start_with? rb_topdir.downcase
    # stub within ruby folder, but not standard bin.  Portable
    require "pathname"
    from = Pathname.new bindir
    to   = Pathname.new "#{rb_topdir}/bin"
    rel  = to.relative_path_from from
    <<~TEXT
      @ECHO OFF
      @"%~dp0#{rel}/#{ruby_exe}" "%~dpn0" %*
    TEXT
  else
    # outside ruby folder, maybe -user-install or bundler.  Portable, but ruby
    # is dependent on PATH
    <<~TEXT
      @ECHO OFF
      @#{ruby_exe} "%~dpn0" %*
    TEXT
  end
end

def write_build_info_file

def write_build_info_file
  return if build_args.empty?
  build_info_dir = File.join gem_home, "build_info"
  dir_mode = options[:dir_mode]
  FileUtils.mkdir_p build_info_dir, mode: dir_mode && 0o755
  build_info_file = File.join build_info_dir, "#{spec.full_name}.info"
  File.open build_info_file, "w" do |io|
    build_args.each do |arg|
      io.puts arg
    end
  end
  File.chmod(dir_mode, build_info_dir) if dir_mode
end

def write_cache_file

def write_cache_file
  cache_file = File.join gem_home, "cache", spec.file_name
  @package.copy_to cache_file
end

def write_default_spec

def write_default_spec
  Gem.write_binary(default_spec_file, spec.to_ruby)
end

def write_spec

def write_spec
  spec.installed_by_version = Gem.rubygems_version
  Gem.write_binary(spec_file, spec.to_ruby_for_cache)
end