Merge pull request #5619 from GauthamGoli/install-args

install: Use CLI::Parser to parse args
This commit is contained in:
Mike McQuaid 2019-01-29 14:11:22 +00:00 committed by GitHub
commit 618f75c071
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 289 additions and 247 deletions

View File

@ -99,22 +99,6 @@ class BuildOptions
@options - @args @options - @args
end end
# @private
def invalid_options
@args - @options - BuildOptions.formula_install_options
end
# @private
def invalid_option_names
invalid_options.map(&:flag).sort
end
def self.formula_install_options
@formula_install_options ||= ARGV.formula_install_option_names.map do |option_name|
Option.new option_name[2..-1]
end
end
private private
def option_defined?(name) def option_defined?(name)

View File

@ -2,7 +2,7 @@
#: Upload logs for a failed build of <formula> to a new Gist. #: Upload logs for a failed build of <formula> to a new Gist.
#: #:
#: <formula> is usually the name of the formula to install, but it can be specified #: <formula> is usually the name of the formula to install, but it can be specified
#: in several different ways. See [SPECIFYING FORMULAE](#specifying-formulae). #: in several different ways.
#: #:
#: If `--with-hostname` is passed, include the hostname in the Gist. #: If `--with-hostname` is passed, include the hostname in the Gist.
#: #:
@ -32,7 +32,7 @@ module Homebrew
Upload logs for a failed build of <formula> to a new Gist. Upload logs for a failed build of <formula> to a new Gist.
<formula> is usually the name of the formula to install, but it can be specified <formula> is usually the name of the formula to install, but it can be specified
in several different ways. See [SPECIFYING FORMULAE](#specifying-formulae). in several different ways.
If no logs are found, an error message is presented. If no logs are found, an error message is presented.
EOS EOS

View File

@ -2,7 +2,7 @@
#: Install <formula>. #: Install <formula>.
#: #:
#: <formula> is usually the name of the formula to install, but it can be specified #: <formula> is usually the name of the formula to install, but it can be specified
#: in several different ways. See [SPECIFYING FORMULAE](#specifying-formulae). #: in several different ways.
#: #:
#: If `--debug` (or `-d`) is passed and brewing fails, open an interactive debugging #: If `--debug` (or `-d`) is passed and brewing fails, open an interactive debugging
#: session with access to IRB or a shell inside the temporary build directory. #: session with access to IRB or a shell inside the temporary build directory.
@ -75,20 +75,98 @@ require "development_tools"
require "install" require "install"
require "search" require "search"
require "cleanup" require "cleanup"
require "cli_parser"
module Homebrew module Homebrew
module_function module_function
extend Search extend Search
def install def install_args
raise FormulaUnspecifiedError if ARGV.named.empty? Homebrew::CLI::Parser.new do
usage_banner <<~EOS
`install` [<options>] formula
if ARGV.include? "--head" Install <formula>.
raise "Specify `--HEAD` in uppercase to build from trunk."
<formula> is usually the name of the formula to install, but it can be specified
in several different ways.
EOS
switch :debug,
description: "If brewing fails, open an interactive debugging session with access to IRB "\
"or a shell inside the temporary build directory"
flag "--env=",
description: "If `std` is passed, use the standard build environment instead of superenv."\
"If `super` is passed, use superenv even if the formula specifies the "\
"standard build environment."
switch "--ignore-dependencies",
description: "Skip installing any dependencies of any kind. If they are not already "\
"present, the formula will probably fail to install."
switch "--only-dependencies",
description: "Install the dependencies with specified options but do not install the "\
"specified formula."
flag "--cc=",
description: "Attempt to compile using provided <compiler>. <compiler> should be the "\
"name of the compiler's executable, for instance `gcc-7` for GCC 7. "\
"In order to use LLVM's clang, use `llvm_clang`. To specify the "\
"Apple-provided clang, use `clang`. This parameter will only accept "\
"compilers that are provided by Homebrew or bundled with macOS. "\
"Please do not file issues if you encounter errors while using this flag."
switch "-s", "--build-from-source",
description: "Compile the specified <formula> from source even if a bottle is provided. "\
"Dependencies will still be installed from bottles if they are available."
switch "--force-bottle",
description: "Install from a bottle if it exists for the current or newest version of "\
"macOS, even if it would not normally be used for installation."
switch "--include-test",
description: "Install testing dependencies required to run `brew test`."
switch "--devel",
description: "If <formula> defines it, install the development version."
switch "--HEAD",
description: "If <formula> defines it, install the HEAD version, aka master, trunk, unstable."
switch "--fetch-HEAD",
description: "Fetch the upstream repository to detect if the HEAD installation of the "\
"formula is outdated. Otherwise, the repository's HEAD will be checked for "\
"updates when a new stable or devel version has been released."
switch "--keep-tmp",
description: "Dont delete the temporary files created during installation."
switch "--build-bottle",
description: "Prepare the formula for eventual bottling during installation."
switch :force,
description: "Install without checking for previously installed keg-only or "\
"non-migrated versions."
switch :verbose,
description: "Print the verification and postinstall steps."
switch "--display-times",
description: "Print install times for each formula at the end of the run."
switch "-i", "--interactive",
description: "Download and patch <formula>, then open a shell. This allows the user to "\
"run `./configure --help` and otherwise determine how to turn the software "\
"package into a Homebrew package."
switch "-g", "--git",
description: "Create a Git repository, useful for creating patches to the software."
ARGV.formulae.each do |f|
next if f.options.empty?
f.options.each do |o|
name = o.flag
description = "`#{f.name}`: #{o.description}"
if name.end_with? "="
flag name, description: description
else
switch name, description: description
end
end
end
end
end end
unless ARGV.force? def install
install_args.parse
raise FormulaUnspecifiedError if args.remaining.empty?
unless args.force?
ARGV.named.each do |name| ARGV.named.each do |name|
next if File.exist?(name) next if File.exist?(name)
if name !~ HOMEBREW_TAP_FORMULA_REGEX && name !~ HOMEBREW_CASK_TAP_CASK_REGEX if name !~ HOMEBREW_TAP_FORMULA_REGEX && name !~ HOMEBREW_CASK_TAP_CASK_REGEX
@ -100,18 +178,17 @@ module Homebrew
end end
end end
begin
formulae = [] formulae = []
unless ARGV.casks.empty? unless ARGV.casks.empty?
args = [] cask_args = []
args << "--force" if ARGV.force? cask_args << "--force" if args.force?
args << "--debug" if ARGV.debug? cask_args << "--debug" if args.debug?
args << "--verbose" if ARGV.verbose? cask_args << "--verbose" if args.verbose?
ARGV.casks.each do |c| ARGV.casks.each do |c|
ohai "brew cask install #{c} #{args.join " "}" ohai "brew cask install #{c} #{cask_args.join " "}"
system("#{HOMEBREW_PREFIX}/bin/brew", "cask", "install", c, *args) system("#{HOMEBREW_PREFIX}/bin/brew", "cask", "install", c, *cask_args)
end end
end end
@ -121,7 +198,7 @@ module Homebrew
ARGV.formulae.each do |f| ARGV.formulae.each do |f|
# head-only without --HEAD is an error # head-only without --HEAD is an error
if !ARGV.build_head? && f.stable.nil? && f.devel.nil? if !Homebrew.args.HEAD? && f.stable.nil? && f.devel.nil?
raise <<~EOS raise <<~EOS
#{f.full_name} is a head-only formula #{f.full_name} is a head-only formula
Install with `brew install --HEAD #{f.full_name}` Install with `brew install --HEAD #{f.full_name}`
@ -129,33 +206,35 @@ module Homebrew
end end
# devel-only without --devel is an error # devel-only without --devel is an error
if !ARGV.build_devel? && f.stable.nil? && f.head.nil? if !args.devel? && f.stable.nil? && f.head.nil?
raise <<~EOS raise <<~EOS
#{f.full_name} is a devel-only formula #{f.full_name} is a devel-only formula
Install with `brew install --devel #{f.full_name}` Install with `brew install --devel #{f.full_name}`
EOS EOS
end end
if ARGV.build_stable? && f.stable.nil? if !(args.HEAD? || args.devel?) && f.stable.nil?
raise "#{f.full_name} has no stable download, please choose --devel or --HEAD" raise "#{f.full_name} has no stable download, please choose --devel or --HEAD"
end end
# --HEAD, fail with no head defined # --HEAD, fail with no head defined
if ARGV.build_head? && f.head.nil? if args.head? && f.head.nil?
raise "No head is defined for #{f.full_name}" raise "No head is defined for #{f.full_name}"
end end
# --devel, fail with no devel defined # --devel, fail with no devel defined
if ARGV.build_devel? && f.devel.nil? if args.devel? && f.devel.nil?
raise "No devel block is defined for #{f.full_name}" raise "No devel block is defined for #{f.full_name}"
end end
installed_head_version = f.latest_head_version installed_head_version = f.latest_head_version
new_head_installed = installed_head_version && if installed_head_version &&
!f.head_version_outdated?(installed_head_version, fetch_head: ARGV.fetch_head?) !f.head_version_outdated?(installed_head_version, fetch_head: args.fetch_HEAD?)
new_head_installed = true
end
prefix_installed = f.prefix.exist? && !f.prefix.children.empty? prefix_installed = f.prefix.exist? && !f.prefix.children.empty?
if f.keg_only? && f.any_version_installed? && f.optlinked? && !ARGV.force? if f.keg_only? && f.any_version_installed? && f.optlinked? && !args.force?
# keg-only install is only possible when no other version is # keg-only install is only possible when no other version is
# linked to opt, because installing without any warnings can break # linked to opt, because installing without any warnings can break
# dependencies. Therefore before performing other checks we need to be # dependencies. Therefore before performing other checks we need to be
@ -166,7 +245,7 @@ module Homebrew
#{f.full_name} #{optlinked_version} is already installed #{f.full_name} #{optlinked_version} is already installed
To upgrade to #{f.version}, run `brew upgrade #{f.name}` To upgrade to #{f.version}, run `brew upgrade #{f.name}`
EOS EOS
elsif ARGV.only_deps? elsif args.only_dependencies?
formulae << f formulae << f
else else
opoo <<~EOS opoo <<~EOS
@ -174,12 +253,12 @@ module Homebrew
To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}` To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}`
EOS EOS
end end
elsif (ARGV.build_head? && new_head_installed) || prefix_installed elsif (args.HEAD? && new_head_installed) || prefix_installed
# After we're sure that --force flag is passed for linked to opt # After we're sure that --force flag is passed for linked to opt
# keg-only we need to be sure that the version we're attempting to # keg-only we need to be sure that the version we're attempting to
# install is not already installed. # install is not already installed.
installed_version = if ARGV.build_head? installed_version = if args.HEAD?
f.latest_head_version f.latest_head_version
else else
f.pkg_version f.pkg_version
@ -198,7 +277,7 @@ module Homebrew
#{msg}, it's just not linked #{msg}, it's just not linked
You can use `brew link #{f}` to link this version. You can use `brew link #{f}` to link this version.
EOS EOS
elsif ARGV.only_deps? elsif args.only_dependencies?
msg = nil msg = nil
formulae << f formulae << f
else else
@ -217,7 +296,7 @@ module Homebrew
EOS EOS
end end
opoo msg opoo msg
elsif f.migration_needed? && !ARGV.force? elsif f.migration_needed? && !args.force?
# Check if the formula we try to install is the same as installed # Check if the formula we try to install is the same as installed
# but not migrated one. If --force passed then install anyway. # but not migrated one. If --force passed then install anyway.
opoo <<~EOS opoo <<~EOS
@ -304,7 +383,6 @@ module Homebrew
puts "To install one of them, run (for example):\n brew install #{taps_search_results.first}" puts "To install one of them, run (for example):\n brew install #{taps_search_results.first}"
end end
end end
end
def install_formula(f) def install_formula(f)
f.print_tap_action f.print_tap_action
@ -312,12 +390,11 @@ module Homebrew
fi = FormulaInstaller.new(f) fi = FormulaInstaller.new(f)
fi.options = build_options.used_options fi.options = build_options.used_options
fi.invalid_option_names = build_options.invalid_option_names fi.ignore_deps = args.ignore_dependencies?
fi.ignore_deps = ARGV.ignore_deps? fi.only_deps = args.only_dependencies?
fi.only_deps = ARGV.only_deps? fi.build_bottle = args.build_bottle?
fi.build_bottle = ARGV.build_bottle? fi.interactive = args.interactive?
fi.interactive = ARGV.interactive? fi.git = args.git?
fi.git = ARGV.git?
fi.prelude fi.prelude
fi.install fi.install
fi.finish fi.finish

View File

@ -33,7 +33,7 @@ class FormulaInstaller
end end
attr_reader :formula attr_reader :formula
attr_accessor :options, :build_bottle, :invalid_option_names attr_accessor :options, :build_bottle
attr_accessor :installed_as_dependency, :installed_on_request, :link_keg attr_accessor :installed_as_dependency, :installed_on_request, :link_keg
mode_attr_accessor :show_summary_heading, :show_header mode_attr_accessor :show_summary_heading, :show_header
mode_attr_accessor :build_from_source, :force_bottle, :include_test mode_attr_accessor :build_from_source, :force_bottle, :include_test
@ -58,7 +58,6 @@ class FormulaInstaller
@installed_as_dependency = false @installed_as_dependency = false
@installed_on_request = true @installed_on_request = true
@options = Options.new @options = Options.new
@invalid_option_names = []
@requirement_messages = [] @requirement_messages = []
@poured_bottle = false @poured_bottle = false
@pour_failed = false @pour_failed = false
@ -267,10 +266,6 @@ class FormulaInstaller
opoo "#{formula.full_name}: #{old_flag} was deprecated; using #{new_flag} instead!" opoo "#{formula.full_name}: #{old_flag} was deprecated; using #{new_flag} instead!"
end end
invalid_option_names.each do |option|
opoo "#{formula.full_name}: this formula has no #{option} option so it will be ignored!"
end
options = display_options(formula) options = display_options(formula)
if show_header? if show_header?
oh1 "Installing #{Formatter.identifier(formula.full_name)} #{options}".strip oh1 "Installing #{Formatter.identifier(formula.full_name)} #{options}".strip

View File

@ -20,7 +20,6 @@ module Homebrew
fi = FormulaInstaller.new(f) fi = FormulaInstaller.new(f)
fi.options = options fi.options = options
fi.invalid_option_names = build_options.invalid_option_names
fi.build_bottle = ARGV.build_bottle? || (!f.bottle_defined? && f.build.bottle?) fi.build_bottle = ARGV.build_bottle? || (!f.bottle_defined? && f.build.bottle?)
fi.interactive = ARGV.interactive? fi.interactive = ARGV.interactive?
fi.git = ARGV.git? fi.git = ARGV.git?

View File

@ -37,17 +37,4 @@ describe BuildOptions do
specify "#unused_options" do specify "#unused_options" do
expect(subject.unused_options).to include("--without-baz") expect(subject.unused_options).to include("--without-baz")
end end
specify "#invalid_options" do
expect(subject.invalid_options).to be_empty
expect(bad_build.invalid_options).to include("--without-bas")
expect(bad_build.invalid_options).to include("--without-abc")
expect(bad_build.invalid_options).not_to include("--with-foo")
expect(bad_build.invalid_options).not_to include("--with-baz")
end
specify "#invalid_option_names" do
expect(subject.invalid_option_names).to be_empty
expect(bad_build.invalid_option_names).to eq(%w[--without-abc --without-bas])
end
end end

View File

@ -166,12 +166,12 @@ describe "brew install", :integration_test do
.and be_a_success .and be_a_success
end end
it "ignores invalid options" do it "rejects invalid options" do
setup_test_formula "testball1" setup_test_formula "testball1"
expect { brew "install", "testball1", "--with-fo" } expect { brew "install", "testball1", "--invalid" }
.to output(/testball1: this formula has no \-\-with\-fo option so it will be ignored!/).to_stderr .to output(/Error: invalid option: --invalid/).to_stderr
.and output(/Downloading file/).to_stdout .and not_to_output.to_stdout
.and be_a_success .and be_a_failure
end end
it "succeeds when a non-fatal requirement isn't satisfied" do it "succeeds when a non-fatal requirement isn't satisfied" do

View File

@ -182,7 +182,7 @@ these flags should only appear after a command.
Upload logs for a failed build of *`formula`* to a new Gist. Upload logs for a failed build of *`formula`* to a new Gist.
*`formula`* is usually the name of the formula to install, but it can be specified *`formula`* is usually the name of the formula to install, but it can be specified
in several different ways. See [SPECIFYING FORMULAE](#specifying-formulae). in several different ways.
If `--with-hostname` is passed, include the hostname in the Gist. If `--with-hostname` is passed, include the hostname in the Gist.
@ -240,7 +240,7 @@ these flags should only appear after a command.
Install *`formula`*. Install *`formula`*.
*`formula`* is usually the name of the formula to install, but it can be specified *`formula`* is usually the name of the formula to install, but it can be specified
in several different ways. See [SPECIFYING FORMULAE](#specifying-formulae). in several different ways.
If `--debug` (or `-d`) is passed and brewing fails, open an interactive debugging If `--debug` (or `-d`) is passed and brewing fails, open an interactive debugging
session with access to IRB or a shell inside the temporary build directory. session with access to IRB or a shell inside the temporary build directory.

View File

@ -184,7 +184,7 @@ If \fB\-\-force\-bottle\fR is passed, download a bottle if it exists for the cur
Upload logs for a failed build of \fIformula\fR to a new Gist\. Upload logs for a failed build of \fIformula\fR to a new Gist\.
. .
.IP .IP
\fIformula\fR is usually the name of the formula to install, but it can be specified in several different ways\. See \fISPECIFYING FORMULAE\fR\. \fIformula\fR is usually the name of the formula to install, but it can be specified in several different ways\.
. .
.IP .IP
If \fB\-\-with\-hostname\fR is passed, include the hostname in the Gist\. If \fB\-\-with\-hostname\fR is passed, include the hostname in the Gist\.
@ -252,7 +252,7 @@ See the docs for examples of using the JSON output: \fIhttps://docs\.brew\.sh/Qu
Install \fIformula\fR\. Install \fIformula\fR\.
. .
.IP .IP
\fIformula\fR is usually the name of the formula to install, but it can be specified in several different ways\. See \fISPECIFYING FORMULAE\fR\. \fIformula\fR is usually the name of the formula to install, but it can be specified in several different ways\.
. .
.IP .IP
If \fB\-\-debug\fR (or \fB\-d\fR) is passed and brewing fails, open an interactive debugging session with access to IRB or a shell inside the temporary build directory\. If \fB\-\-debug\fR (or \fB\-d\fR) is passed and brewing fails, open an interactive debugging session with access to IRB or a shell inside the temporary build directory\.