Mike McQuaid 20a1199375
Refactor CLI to remove unless args_parsed
Refactor the CLI::Args module so it doesn't have different paths to
check arguments depending on whether the arguments have been parsed or
not. Instead, set the values we need from the global ARGV at
first, global initialisation time where they will be thrown away when
the actual arguments are parsed.

To do this some other general refactoring was needed:
- more methods made private when possible
- e.g. `HEAD?` used consistently instead of `head` before arguments
  are parsed.
- formula options are only parsed after named arguments are extracted
2020-05-05 17:47:51 +01:00

220 lines
6.3 KiB
Ruby

# frozen_string_literal: true
require "ostruct"
module Homebrew
module CLI
class Args < OpenStruct
# undefine tap to allow --tap argument
undef tap
def initialize(argv = ARGV.dup.freeze, set_default_args: false)
super()
@processed_options = []
# Set values needed before Parser#parse has been run.
return unless set_default_args
self[:build_from_source?] = argv.include?("--build-from-source") || argv.include?("-s")
self[:build_bottle?] = argv.include?("--build-bottle")
self[:force_bottle?] = argv.include?("--force-bottle")
self[:HEAD?] = argv.include?("--HEAD")
self[:devel?] = argv.include?("--devel")
self[:universal?] = argv.include?("--universal")
self[:named_args] = argv.reject { |arg| arg.start_with?("-") }
end
def freeze_named_args!(named_args)
self[:named_args] = named_args
self[:named_args].freeze
end
def freeze_processed_options!(processed_options)
@processed_options += processed_options
@processed_options.freeze
end
def options_only
@options_only ||= cli_args.select { |arg| arg.start_with?("-") }
.freeze
end
def flags_only
@flags_only ||= cli_args.select { |arg| arg.start_with?("--") }
.freeze
end
def passthrough
options_only - CLI::Parser.global_options.values.map(&:first).flatten
end
def named
named_args || []
end
def no_named?
named.blank?
end
# If the user passes any flags that trigger building over installing from
# a bottle, they are collected here and returned as an Array for checking.
def collect_build_args
build_flags = []
build_flags << "--HEAD" if HEAD?
build_flags << "--universal" if build_universal?
build_flags << "--build-bottle" if build_bottle?
build_flags << "--build-from-source" if build_from_source?
build_flags
end
def formulae
require "formula"
@formulae ||= (downcased_unique_named - casks).map do |name|
if name.include?("/") || File.exist?(name)
Formulary.factory(name, spec)
else
Formulary.find_with_priority(name, spec)
end
end.uniq(&:name)
.freeze
end
def resolved_formulae
require "formula"
@resolved_formulae ||= (downcased_unique_named - casks).map do |name|
Formulary.resolve(name, spec: spec(nil))
end.uniq(&:name)
.freeze
end
def formulae_paths
@formulae_paths ||= (downcased_unique_named - casks).map do |name|
Formulary.path(name)
end.uniq
.freeze
end
def casks
@casks ||= downcased_unique_named.grep(HOMEBREW_CASK_TAP_CASK_REGEX)
.freeze
end
def kegs
require "keg"
require "formula"
require "missing_formula"
@kegs ||= downcased_unique_named.map do |name|
raise UsageError if name.empty?
rack = Formulary.to_rack(name.downcase)
dirs = rack.directory? ? rack.subdirs : []
if dirs.empty?
if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall"))
$stderr.puts reason
end
raise NoSuchKegError, rack.basename
end
linked_keg_ref = HOMEBREW_LINKED_KEGS/rack.basename
opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}"
begin
if opt_prefix.symlink? && opt_prefix.directory?
Keg.new(opt_prefix.resolved_path)
elsif linked_keg_ref.symlink? && linked_keg_ref.directory?
Keg.new(linked_keg_ref.resolved_path)
elsif dirs.length == 1
Keg.new(dirs.first)
else
f = if name.include?("/") || File.exist?(name)
Formulary.factory(name)
else
Formulary.from_rack(rack)
end
unless (prefix = f.installed_prefix).directory?
raise MultipleVersionsInstalledError, rack.basename
end
Keg.new(prefix)
end
rescue FormulaUnavailableError
raise <<~EOS
Multiple kegs installed to #{rack}
However we don't know which one you refer to.
Please delete (with rm -rf!) all but one and then try again.
EOS
end
end.freeze
end
def build_stable?
!(HEAD? || devel?)
end
# Whether a given formula should be built from source during the current
# installation run.
def build_formula_from_source?(f)
return false if !build_from_source? && !build_bottle?
formulae.any? { |args_f| args_f.full_name == f.full_name }
end
private
def option_to_name(option)
option.sub(/\A--?/, "")
.tr("-", "_")
end
def cli_args
return @cli_args if @cli_args
@cli_args = []
@processed_options.each do |short, long|
option = long || short
switch = "#{option_to_name(option)}?".to_sym
flag = option_to_name(option).to_sym
if @table[switch] == true || @table[flag] == true
@cli_args << option
elsif @table[flag].instance_of? String
@cli_args << option + "=" + @table[flag]
elsif @table[flag].instance_of? Array
@cli_args << option + "=" + @table[flag].join(",")
end
end
@cli_args.freeze
end
def downcased_unique_named
# Only lowercase names, not paths, bottle filenames or URLs
named.map do |arg|
if arg.include?("/") || arg.end_with?(".tar.gz") || File.exist?(arg)
arg
else
arg.downcase
end
end.uniq
end
def spec(default = :stable)
if HEAD?
:head
elsif devel?
:devel
else
default
end
end
end
end
end