brew/Library/Homebrew/cmd/install.rb

355 lines
14 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
require "missing_formula"
2014-06-16 13:36:17 -05:00
require "formula_installer"
require "development_tools"
require "install"
2018-06-05 15:39:09 +02:00
require "search"
require "cleanup"
2019-04-17 18:25:08 +09:00
require "cli/parser"
require "upgrade"
module Homebrew
2018-06-05 15:39:09 +02:00
extend Search
module_function
2019-01-26 16:44:03 +05:30
def install_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
`install` [<options>] <formula>
2019-01-26 16:44:03 +05:30
Install <formula>. Additional options specific to <formula> may be appended to the command.
2019-01-26 16:44:03 +05:30
Unless `HOMEBREW_NO_INSTALL_CLEANUP` is set, `brew cleanup` will then be run for the
2019-08-06 13:23:19 -04:00
installed formulae or, every 30 days, for all formulae.
2019-01-26 16:44:03 +05:30
EOS
switch :debug,
2019-04-30 08:44:35 +01:00
description: "If brewing fails, open an interactive debugging session with access to IRB "\
2019-08-06 14:22:24 -04:00
"or a shell inside the temporary build directory."
2019-08-06 13:23:19 -04:00
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."
2019-01-26 16:44:03 +05:30
switch "--ignore-dependencies",
2019-04-30 08:44:35 +01:00
description: "An unsupported Homebrew development flag to skip installing any dependencies of "\
"any kind. If the dependencies are not already present, the formula will have issues. "\
"If you're not developing Homebrew, consider adjusting your PATH rather than "\
"using this flag."
2019-01-26 16:44:03 +05:30
switch "--only-dependencies",
2019-04-30 08:44:35 +01:00
description: "Install the dependencies with specified options but do not install the "\
"formula itself."
2019-08-06 13:23:19 -04:00
flag "--cc=",
description: "Attempt to compile using the specified <compiler>, which should be the "\
"name of the compiler's executable, e.g. `gcc-7` for GCC 7. "\
"In order to use LLVM's clang, specify `llvm_clang`. To use the "\
"Apple-provided clang, specify `clang`. This option will only accept "\
2019-08-06 13:23:19 -04:00
"compilers that are provided by Homebrew or bundled with macOS. "\
"Please do not file issues if you encounter errors while using this option."
2019-01-26 16:44:03 +05:30
switch "-s", "--build-from-source",
description: "Compile <formula> from source even if a bottle is provided. "\
2019-04-30 08:44:35 +01:00
"Dependencies will still be installed from bottles if they are available."
2019-01-26 16:44:03 +05:30
switch "--force-bottle",
2019-04-30 08:44:35 +01:00
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."
2019-01-26 16:44:03 +05:30
switch "--include-test",
description: "Install testing dependencies required to run `brew test` <formula>."
2019-01-26 16:44:03 +05:30
switch "--devel",
2019-04-30 08:44:35 +01:00
description: "If <formula> defines it, install the development version."
2019-01-26 16:44:03 +05:30
switch "--HEAD",
2019-04-30 08:44:35 +01:00
description: "If <formula> defines it, install the HEAD version, aka. master, trunk, unstable."
switch "--fetch-HEAD",
2019-04-30 08:44:35 +01:00
description: "Fetch the upstream repository to detect if the HEAD installation of the "\
"formula is outdated. Otherwise, the repository's HEAD will only be checked for "\
2019-04-30 08:44:35 +01:00
"updates when a new stable or development version has been released."
2019-01-26 16:44:03 +05:30
switch "--keep-tmp",
description: "Retain the temporary files created during installation."
2019-01-26 16:44:03 +05:30
switch "--build-bottle",
description: "Prepare the formula for eventual bottling during installation, skipping any "\
"post-install steps."
2019-08-06 13:23:19 -04:00
flag "--bottle-arch=",
depends_on: "--build-bottle",
description: "Optimise bottles for the specified architecture rather than the oldest "\
2019-08-06 13:23:19 -04:00
"architecture supported by the version of macOS the bottles are built on."
switch "-f", "--force",
2019-04-30 08:44:35 +01:00
description: "Install without checking for previously installed keg-only or "\
"non-migrated versions."
2019-01-26 16:44:03 +05:30
switch :verbose,
2019-04-30 08:44:35 +01:00
description: "Print the verification and postinstall steps."
2019-01-26 16:44:03 +05:30
switch "--display-times",
env: :display_install_times,
2019-04-30 08:44:35 +01:00
description: "Print install times for each formula at the end of the run."
2019-01-26 16:44:03 +05:30
switch "-i", "--interactive",
2019-04-30 08:44:35 +01:00
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."
2019-01-26 16:44:03 +05:30
switch "-g", "--git",
2019-04-30 08:44:35 +01:00
description: "Create a Git repository, useful for creating patches to the software."
conflicts "--ignore-dependencies", "--only-dependencies"
conflicts "--devel", "--HEAD"
conflicts "--build-from-source", "--build-bottle", "--force-bottle"
formula_options
min_named :formula
end
2019-01-26 16:44:03 +05:30
end
def install
args = install_args.parse
args.named.each do |name|
next if File.exist?(name)
next if name !~ HOMEBREW_TAP_FORMULA_REGEX && name !~ HOMEBREW_CASK_TAP_CASK_REGEX
tap = Tap.fetch(Regexp.last_match(1), Regexp.last_match(2))
tap.install unless tap.installed?
end
if args.ignore_dependencies?
opoo <<~EOS
#{Tty.bold}--ignore-dependencies is an unsupported Homebrew developer flag!#{Tty.reset}
Adjust your PATH to put any preferred versions of applications earlier in the
PATH rather than using this unsupported flag!
EOS
end
formulae = []
unless args.casks.empty?
cask_args = []
cask_args << "--force" if args.force?
cask_args << "--debug" if args.debug?
cask_args << "--verbose" if args.verbose?
args.casks.each do |c|
ohai "brew cask install #{c} #{cask_args.join " "}"
system("#{HOMEBREW_PREFIX}/bin/brew", "cask", "install", c, *cask_args)
end
end
# if the user's flags will prevent bottle only-installations when no
# developer tools are available, we need to stop them early on
FormulaInstaller.prevent_build_flags(args)
args.formulae.each do |f|
# head-only without --HEAD is an error
if !args.HEAD? && f.stable.nil? && f.devel.nil?
raise <<~EOS
#{f.full_name} is a head-only formula
Install with `brew install --HEAD #{f.full_name}`
EOS
end
# devel-only without --devel is an error
if !args.devel? && f.stable.nil? && f.head.nil?
raise <<~EOS
#{f.full_name} is a devel-only formula
Install with `brew install --devel #{f.full_name}`
EOS
end
if !(args.HEAD? || args.devel?) && f.stable.nil?
raise "#{f.full_name} has no stable download, please choose --devel or --HEAD"
end
# --HEAD, fail with no head defined
raise "No head is defined for #{f.full_name}" if args.HEAD? && f.head.nil?
# --devel, fail with no devel defined
raise "No devel block is defined for #{f.full_name}" if args.devel? && f.devel.nil?
installed_head_version = f.latest_head_version
2019-01-29 09:38:20 +00:00
if installed_head_version &&
!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?
if f.keg_only? && f.any_version_installed? && f.optlinked? && !args.force?
# keg-only install is only possible when no other version is
# linked to opt, because installing without any warnings can break
# dependencies. Therefore before performing other checks we need to be
# sure --force flag is passed.
if f.outdated?
optlinked_version = Keg.for(f.opt_prefix).version
onoe <<~EOS
#{f.full_name} #{optlinked_version} is already installed
To upgrade to #{f.version}, run `brew upgrade #{f.full_name}`
EOS
elsif args.only_dependencies?
formulae << f
else
2017-10-15 02:28:32 +02:00
opoo <<~EOS
#{f.full_name} #{f.pkg_version} is already installed and up-to-date
To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}`
EOS
end
elsif (args.HEAD? && new_head_installed) || prefix_installed
# 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
# install is not already installed.
installed_version = if args.HEAD?
f.latest_head_version
else
f.pkg_version
end
msg = "#{f.full_name} #{installed_version} is already installed"
linked_not_equals_installed = f.linked_version != installed_version
if f.linked? && linked_not_equals_installed
msg = <<~EOS
#{msg}
The currently linked version is #{f.linked_version}
You can use `brew switch #{f} #{installed_version}` to link this version.
EOS
elsif !f.linked? || f.keg_only?
msg = <<~EOS
#{msg}, it's just not linked
You can use `brew link #{f}` to link this version.
EOS
elsif args.only_dependencies?
msg = nil
formulae << f
else
msg = <<~EOS
#{msg} and up-to-date
To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}`
EOS
end
opoo msg if msg
elsif !f.any_version_installed? && old_formula = f.old_installed_formulae.first
msg = "#{old_formula.full_name} #{old_formula.installed_version} already installed"
if !old_formula.linked? && !old_formula.keg_only?
msg = <<~EOS
#{msg}, it's just not linked.
You can use `brew link #{old_formula.full_name}` to link this version.
EOS
end
opoo msg
elsif f.migration_needed? && !args.force?
# Check if the formula we try to install is the same as installed
2019-08-06 14:22:24 -04:00
# but not migrated one. If --force is passed then install anyway.
opoo <<~EOS
2019-04-08 12:47:15 -04:00
#{f.oldname} is already installed, it's just not migrated
You can migrate this formula with `brew migrate #{f}`
Or you can force install it with `brew install #{f} --force`
EOS
else
# If none of the above is true and the formula is linked, then
# FormulaInstaller will handle this case.
formulae << f
end
# Even if we don't install this formula mark it as no longer just
# installed as a dependency.
next unless f.opt_prefix.directory?
2018-09-17 02:45:00 +02:00
keg = Keg.new(f.opt_prefix.resolved_path)
tab = Tab.for_keg(keg)
unless tab.installed_on_request
tab.installed_on_request = true
tab.write
end
end
return if formulae.empty?
2018-09-17 02:45:00 +02:00
2020-07-27 11:37:07 +02:00
Install.perform_preinstall_checks(cc: args.cc)
formulae.each do |f|
Migrator.migrate_if_needed(f, force: args.force?)
install_formula(f, args: args)
Cleanup.install_formula_clean!(f)
end
check_installed_dependents(args: args)
Homebrew.messages.display_messages(display_times: args.display_times?)
rescue FormulaUnreadableError, FormulaClassUnavailableError,
2019-01-29 09:38:20 +00:00
TapFormulaUnreadableError, TapFormulaClassUnavailableError => e
# Need to rescue before `FormulaUnavailableError` (superclass of this)
# is handled, as searching for a formula doesn't make sense here (the
# formula was found, but there's a problem with its implementation).
$stderr.puts e.backtrace if Homebrew::EnvConfig.developer?
ofail e.message
rescue FormulaUnavailableError => e
if e.name == "updog"
ofail "What's updog?"
return
end
ofail e.message
if (reason = MissingFormula.reason(e.name))
$stderr.puts reason
return
end
ohai "Searching for similarly named formulae..."
formulae_search_results = search_formulae(e.name)
case formulae_search_results.length
when 0
ofail "No similarly named formulae found."
when 1
puts "This similarly named formula was found:"
puts formulae_search_results
puts "To install it, run:\n brew install #{formulae_search_results.first}"
else
puts "These similarly named formulae were found:"
puts Formatter.columns(formulae_search_results)
puts "To install one of them, run (for example):\n brew install #{formulae_search_results.first}"
end
# Do not search taps if the formula name is qualified
return if e.name.include?("/")
ohai "Searching taps..."
taps_search_results = search_taps(e.name)[:formulae]
case taps_search_results.length
when 0
ofail "No formulae found in taps."
when 1
puts "This formula was found in a tap:"
puts taps_search_results
puts "To install it, run:\n brew install #{taps_search_results.first}"
else
puts "These formulae were found in taps:"
puts Formatter.columns(taps_search_results)
puts "To install one of them, run (for example):\n brew install #{taps_search_results.first}"
end
end
def install_formula(f, args:)
f.print_tap_action
2016-11-12 12:31:35 +00:00
build_options = f.build
fi = FormulaInstaller.new(f, force_bottle: args.force_bottle?,
include_test: args.include_test?,
include_test_formulae: args.include_test_formulae,
build_from_source: args.build_from_source?,
build_from_source_formulae: args.build_from_source_formulae,
args: args)
2016-11-12 12:31:35 +00:00
fi.options = build_options.used_options
fi.env = args.env
fi.force = args.force?
fi.keep_tmp = args.keep_tmp?
2019-01-26 16:44:03 +05:30
fi.ignore_deps = args.ignore_dependencies?
fi.only_deps = args.only_dependencies?
fi.build_bottle = args.build_bottle?
fi.bottle_arch = args.bottle_arch
2019-01-26 16:44:03 +05:30
fi.interactive = args.interactive?
fi.git = args.git?
fi.cc = args.cc
fi.prelude
fi.fetch
fi.install
fi.finish
rescue FormulaInstallationAlreadyAttemptedError
# We already attempted to install f as part of the dependency tree of
# another formula. In that case, don't generate an error, just move on.
2018-03-06 09:36:49 +00:00
nil
rescue CannotInstallFormulaError => e
ofail e.message
end
end