2024-09-08 16:33:11 -07:00
|
|
|
# typed: strict
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
require "formula"
|
|
|
|
require "keg"
|
|
|
|
require "tab"
|
2016-04-25 17:57:51 +01:00
|
|
|
require "utils/bottles"
|
2015-08-03 13:09:07 +01:00
|
|
|
require "caveats"
|
|
|
|
require "cleaner"
|
|
|
|
require "formula_cellar_checks"
|
|
|
|
require "install_renamed"
|
|
|
|
require "sandbox"
|
2016-07-29 20:31:32 -06:00
|
|
|
require "development_tools"
|
2018-05-15 12:49:48 -04:00
|
|
|
require "cache_store"
|
|
|
|
require "linkage_checker"
|
2018-06-11 20:45:06 -04:00
|
|
|
require "install"
|
2018-06-20 02:10:54 -04:00
|
|
|
require "messages"
|
2019-03-27 09:45:48 +00:00
|
|
|
require "cask/cask_loader"
|
2020-07-07 16:20:05 +08:00
|
|
|
require "cmd/install"
|
2019-11-06 17:24:56 +00:00
|
|
|
require "find"
|
2020-08-18 10:58:32 -04:00
|
|
|
require "utils/spdx"
|
2020-09-07 13:00:02 -04:00
|
|
|
require "deprecate_disable"
|
2020-11-02 11:20:09 +00:00
|
|
|
require "unlink"
|
2020-12-11 23:14:50 +01:00
|
|
|
require "service"
|
2024-04-08 16:18:15 -04:00
|
|
|
require "attestation"
|
2024-05-09 13:10:35 +01:00
|
|
|
require "sbom"
|
2024-07-14 08:49:39 -04:00
|
|
|
require "utils/fork"
|
2009-10-26 18:13:38 +00:00
|
|
|
|
2020-08-17 06:05:08 +02:00
|
|
|
# Installer for a formula.
|
2023-02-21 00:25:02 +00:00
|
|
|
class FormulaInstaller
|
2013-07-15 19:28:10 -07:00
|
|
|
include FormulaCellarChecks
|
2023-12-28 11:45:18 -08:00
|
|
|
extend Attrable
|
2013-07-15 19:28:10 -07:00
|
|
|
|
2024-09-09 08:22:43 -07:00
|
|
|
ETC_VAR_DIRS = T.let([HOMEBREW_PREFIX/"etc", HOMEBREW_PREFIX/"var"].freeze, T::Array[Pathname])
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { override.returns(Formula) }
|
|
|
|
attr_reader :formula
|
2020-05-12 08:32:27 +01:00
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { returns(T::Hash[String, T::Hash[String, String]]) }
|
|
|
|
attr_reader :bottle_tab_runtime_dependencies
|
|
|
|
|
|
|
|
sig { returns(Options) }
|
|
|
|
attr_accessor :options
|
|
|
|
|
|
|
|
sig { returns(T::Boolean) }
|
|
|
|
attr_accessor :link_keg
|
2020-11-18 04:21:41 +01:00
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
attr_predicate :installed_as_dependency?, :installed_on_request?
|
|
|
|
attr_predicate :show_summary_heading?, :show_header?
|
2022-01-10 10:05:44 -06:00
|
|
|
attr_predicate :force_bottle?, :ignore_deps?, :only_deps?, :interactive?, :git?, :force?, :overwrite?, :keep_tmp?
|
2022-07-26 00:00:45 +01:00
|
|
|
attr_predicate :debug_symbols?
|
2020-11-18 04:21:41 +01:00
|
|
|
attr_predicate :verbose?, :debug?, :quiet?
|
2009-10-26 18:13:38 +00:00
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig {
|
2024-09-09 09:05:34 -07:00
|
|
|
params(
|
|
|
|
formula: Formula,
|
|
|
|
link_keg: T::Boolean,
|
|
|
|
installed_as_dependency: T::Boolean,
|
|
|
|
installed_on_request: T::Boolean,
|
|
|
|
show_header: T::Boolean,
|
|
|
|
build_bottle: T::Boolean,
|
|
|
|
skip_post_install: T::Boolean,
|
2025-01-23 14:42:03 +00:00
|
|
|
skip_link: T::Boolean,
|
2024-09-09 09:05:34 -07:00
|
|
|
force_bottle: T::Boolean,
|
|
|
|
bottle_arch: T.nilable(String),
|
|
|
|
ignore_deps: T::Boolean,
|
|
|
|
only_deps: T::Boolean,
|
|
|
|
include_test_formulae: T::Array[Formula],
|
|
|
|
build_from_source_formulae: T::Array[Formula],
|
|
|
|
env: T.nilable(String),
|
|
|
|
git: T::Boolean,
|
|
|
|
interactive: T::Boolean,
|
|
|
|
keep_tmp: T::Boolean,
|
|
|
|
debug_symbols: T::Boolean,
|
|
|
|
cc: T.nilable(String),
|
|
|
|
options: Options,
|
|
|
|
force: T::Boolean,
|
|
|
|
overwrite: T::Boolean,
|
|
|
|
debug: T::Boolean,
|
|
|
|
quiet: T::Boolean,
|
|
|
|
verbose: T::Boolean,
|
|
|
|
).void
|
2024-09-08 16:33:11 -07:00
|
|
|
}
|
2020-11-18 05:37:12 +01:00
|
|
|
def initialize(
|
|
|
|
formula,
|
|
|
|
link_keg: false,
|
|
|
|
installed_as_dependency: false,
|
2024-11-14 15:00:33 +00:00
|
|
|
installed_on_request: false,
|
2020-11-18 05:37:12 +01:00
|
|
|
show_header: false,
|
|
|
|
build_bottle: false,
|
2023-03-23 15:22:27 +00:00
|
|
|
skip_post_install: false,
|
2025-01-23 14:42:03 +00:00
|
|
|
skip_link: false,
|
2020-11-18 05:37:12 +01:00
|
|
|
force_bottle: false,
|
|
|
|
bottle_arch: nil,
|
|
|
|
ignore_deps: false,
|
|
|
|
only_deps: false,
|
|
|
|
include_test_formulae: [],
|
|
|
|
build_from_source_formulae: [],
|
|
|
|
env: nil,
|
|
|
|
git: false,
|
|
|
|
interactive: false,
|
|
|
|
keep_tmp: false,
|
2022-07-26 12:15:53 +01:00
|
|
|
debug_symbols: false,
|
2020-11-18 05:37:12 +01:00
|
|
|
cc: nil,
|
|
|
|
options: Options.new,
|
|
|
|
force: false,
|
2022-01-10 10:05:44 -06:00
|
|
|
overwrite: false,
|
2020-11-18 05:37:12 +01:00
|
|
|
debug: false,
|
|
|
|
quiet: false,
|
|
|
|
verbose: false
|
|
|
|
)
|
2014-10-29 22:38:49 -05:00
|
|
|
@formula = formula
|
2020-11-18 05:37:12 +01:00
|
|
|
@env = env
|
|
|
|
@force = force
|
2022-01-10 10:05:44 -06:00
|
|
|
@overwrite = overwrite
|
2020-11-18 05:37:12 +01:00
|
|
|
@keep_tmp = keep_tmp
|
2022-07-26 12:15:53 +01:00
|
|
|
@debug_symbols = debug_symbols
|
2024-09-08 16:33:11 -07:00
|
|
|
@link_keg = T.let(!formula.keg_only? || link_keg, T::Boolean)
|
2020-11-18 05:37:12 +01:00
|
|
|
@show_header = show_header
|
|
|
|
@ignore_deps = ignore_deps
|
|
|
|
@only_deps = only_deps
|
2020-07-28 14:08:40 +02:00
|
|
|
@build_from_source_formulae = build_from_source_formulae
|
2020-11-18 05:37:12 +01:00
|
|
|
@build_bottle = build_bottle
|
2023-03-23 15:22:27 +00:00
|
|
|
@skip_post_install = skip_post_install
|
2025-01-23 14:42:03 +00:00
|
|
|
@skip_link = skip_link
|
2020-11-18 05:37:12 +01:00
|
|
|
@bottle_arch = bottle_arch
|
2020-11-06 11:25:31 +00:00
|
|
|
@formula.force_bottle ||= force_bottle
|
2024-09-08 16:33:11 -07:00
|
|
|
@force_bottle = T.let(@formula.force_bottle, T::Boolean)
|
2020-07-28 14:08:40 +02:00
|
|
|
@include_test_formulae = include_test_formulae
|
2020-11-18 05:37:12 +01:00
|
|
|
@interactive = interactive
|
|
|
|
@git = git
|
2020-07-25 03:48:33 +02:00
|
|
|
@cc = cc
|
2020-08-02 03:53:30 +02:00
|
|
|
@verbose = verbose
|
|
|
|
@quiet = quiet
|
|
|
|
@debug = debug
|
2020-11-18 05:37:12 +01:00
|
|
|
@installed_as_dependency = installed_as_dependency
|
|
|
|
@installed_on_request = installed_on_request
|
|
|
|
@options = options
|
2024-09-08 16:33:11 -07:00
|
|
|
@requirement_messages = T.let([], T::Array[String])
|
|
|
|
@poured_bottle = T.let(false, T::Boolean)
|
|
|
|
@start_time = T.let(nil, T.nilable(Time))
|
|
|
|
@bottle_tab_runtime_dependencies = T.let({}.freeze, T::Hash[String, T::Hash[String, String]])
|
|
|
|
@hold_locks = T.let(false, T::Boolean)
|
|
|
|
@show_summary_heading = T.let(false, T::Boolean)
|
|
|
|
@etc_var_preinstall = T.let([], T::Array[Pathname])
|
2023-07-28 02:12:09 +01:00
|
|
|
|
|
|
|
# Take the original formula instance, which might have been swapped from an API instance to a source instance
|
2024-09-08 16:33:11 -07:00
|
|
|
@formula = T.let(T.must(previously_fetched_formula), Formula) if previously_fetched_formula
|
2012-03-07 11:16:27 +00:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { returns(T::Set[Formula]) }
|
2017-10-07 00:31:28 +02:00
|
|
|
def self.attempted
|
2024-09-08 16:33:11 -07:00
|
|
|
@attempted ||= T.let(Set.new, T.nilable(T::Set[Formula]))
|
2017-10-07 00:31:28 +02:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2019-03-27 09:45:48 +00:00
|
|
|
def self.clear_attempted
|
2024-09-08 16:33:11 -07:00
|
|
|
@attempted = T.let(Set.new, T.nilable(T::Set[Formula]))
|
2019-03-27 09:45:48 +00:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { returns(T::Set[Formula]) }
|
2020-07-02 12:53:52 +01:00
|
|
|
def self.installed
|
2024-09-08 16:33:11 -07:00
|
|
|
@installed ||= T.let(Set.new, T.nilable(T::Set[Formula]))
|
2020-07-02 12:53:52 +01:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2020-07-02 12:53:52 +01:00
|
|
|
def self.clear_installed
|
2024-09-08 16:33:11 -07:00
|
|
|
@installed = T.let(Set.new, T.nilable(T::Set[Formula]))
|
2020-07-02 12:53:52 +01:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { returns(T::Set[Formula]) }
|
2021-10-31 10:52:40 -07:00
|
|
|
def self.fetched
|
2024-09-08 16:33:11 -07:00
|
|
|
@fetched ||= T.let(Set.new, T.nilable(T::Set[Formula]))
|
2021-10-31 10:52:40 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
sig { void }
|
|
|
|
def self.clear_fetched
|
2024-09-08 16:33:11 -07:00
|
|
|
@fetched = T.let(Set.new, T.nilable(T::Set[Formula]))
|
2021-10-31 10:52:40 -07:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { returns(T::Boolean) }
|
2020-08-02 00:46:17 +01:00
|
|
|
def build_from_source?
|
2020-11-18 05:37:12 +01:00
|
|
|
@build_from_source_formulae.include?(formula.full_name)
|
2020-08-02 00:46:17 +01:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { returns(T::Boolean) }
|
2020-08-02 00:46:17 +01:00
|
|
|
def include_test?
|
2020-11-18 05:37:12 +01:00
|
|
|
@include_test_formulae.include?(formula.full_name)
|
2020-08-02 00:46:17 +01:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { returns(T::Boolean) }
|
2015-09-14 19:59:00 +08:00
|
|
|
def build_bottle?
|
2022-05-30 14:59:14 +01:00
|
|
|
@build_bottle.present?
|
2015-09-14 19:59:00 +08:00
|
|
|
end
|
|
|
|
|
2023-03-23 15:22:27 +00:00
|
|
|
sig { returns(T::Boolean) }
|
|
|
|
def skip_post_install?
|
|
|
|
@skip_post_install.present?
|
|
|
|
end
|
|
|
|
|
2025-01-23 14:42:03 +00:00
|
|
|
sig { returns(T::Boolean) }
|
|
|
|
def skip_link?
|
|
|
|
@skip_link.present?
|
|
|
|
end
|
|
|
|
|
2020-12-15 12:54:03 +00:00
|
|
|
sig { params(output_warning: T::Boolean).returns(T::Boolean) }
|
|
|
|
def pour_bottle?(output_warning: false)
|
2020-12-09 13:53:10 +00:00
|
|
|
return false if !formula.bottle_tag? && !formula.local_bottle_path
|
2016-01-09 11:07:07 +00:00
|
|
|
return true if force_bottle?
|
2020-04-21 13:26:37 +01:00
|
|
|
return false if build_from_source? || build_bottle? || interactive?
|
2020-11-18 05:37:12 +01:00
|
|
|
return false if @cc
|
2014-03-10 14:56:02 -05:00
|
|
|
return false unless options.empty?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-01-09 11:07:40 +00:00
|
|
|
unless formula.pour_bottle?
|
2020-12-15 12:54:03 +00:00
|
|
|
if output_warning && formula.pour_bottle_check_unsatisfied_reason
|
2017-10-15 02:28:32 +02:00
|
|
|
opoo <<~EOS
|
2016-01-09 11:07:40 +00:00
|
|
|
Building #{formula.full_name} from source:
|
|
|
|
#{formula.pour_bottle_check_unsatisfied_reason}
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
2014-03-10 14:56:02 -05:00
|
|
|
|
2021-09-22 19:36:53 +01:00
|
|
|
return true if formula.local_bottle_path.present?
|
|
|
|
|
2023-04-14 15:33:40 +02:00
|
|
|
bottle = formula.bottle_for_tag(Utils::Bottles.tag)
|
2021-09-20 20:18:19 +01:00
|
|
|
return false if bottle.nil?
|
|
|
|
|
|
|
|
unless bottle.compatible_locations?
|
2020-12-15 12:54:03 +00:00
|
|
|
if output_warning
|
2023-11-05 09:20:34 -08:00
|
|
|
prefix = Pathname(bottle.cellar.to_s).parent
|
2017-10-15 02:28:32 +02:00
|
|
|
opoo <<~EOS
|
2020-12-09 13:53:10 +00:00
|
|
|
Building #{formula.full_name} from source as the bottle needs:
|
|
|
|
- HOMEBREW_CELLAR: #{bottle.cellar} (yours is #{HOMEBREW_CELLAR})
|
2021-09-16 18:56:47 +01:00
|
|
|
- HOMEBREW_PREFIX: #{prefix} (yours is #{HOMEBREW_PREFIX})
|
2016-01-09 11:07:28 +00:00
|
|
|
EOS
|
2014-03-10 14:56:02 -05:00
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2020-11-18 04:21:41 +01:00
|
|
|
sig { params(dep: Formula, build: BuildOptions).returns(T::Boolean) }
|
2014-10-29 00:28:29 -05:00
|
|
|
def install_bottle_for?(dep, build)
|
2014-10-29 22:38:49 -05:00
|
|
|
return pour_bottle? if dep == formula
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2021-01-07 13:49:05 -08:00
|
|
|
@build_from_source_formulae.exclude?(dep.full_name) &&
|
|
|
|
dep.bottle.present? &&
|
|
|
|
dep.pour_bottle? &&
|
|
|
|
build.used_options.empty? &&
|
|
|
|
dep.bottle&.compatible_locations?
|
2013-01-23 00:26:27 -06:00
|
|
|
end
|
|
|
|
|
2020-11-18 04:21:41 +01:00
|
|
|
sig { void }
|
2014-03-04 14:06:25 -06:00
|
|
|
def prelude
|
2023-12-04 00:30:49 -05:00
|
|
|
deprecate_disable_type = DeprecateDisable.type(formula)
|
|
|
|
if deprecate_disable_type.present?
|
|
|
|
message = "#{formula.full_name} has been #{DeprecateDisable.message(formula)}"
|
|
|
|
|
2023-12-16 20:08:42 -05:00
|
|
|
case deprecate_disable_type
|
|
|
|
when :deprecated
|
2023-12-04 00:30:49 -05:00
|
|
|
opoo message
|
2023-12-16 20:08:42 -05:00
|
|
|
when :disabled
|
2024-11-25 08:57:31 -08:00
|
|
|
if force?
|
|
|
|
opoo message
|
|
|
|
else
|
|
|
|
GitHub::Actions.puts_annotation_if_env_set(:error, message)
|
|
|
|
raise CannotInstallFormulaError, message
|
|
|
|
end
|
2021-03-24 15:46:29 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-02-09 12:53:47 +08:00
|
|
|
Tab.clear_cache
|
2023-09-02 21:46:07 -04:00
|
|
|
|
2016-12-10 19:21:17 +00:00
|
|
|
verify_deps_exist unless ignore_deps?
|
2024-04-08 16:38:32 +01:00
|
|
|
|
2020-07-09 11:38:23 +08:00
|
|
|
forbidden_license_check
|
2024-04-08 16:38:32 +01:00
|
|
|
forbidden_tap_check
|
|
|
|
forbidden_formula_check
|
2020-07-07 16:09:20 +08:00
|
|
|
|
2014-03-04 14:06:25 -06:00
|
|
|
check_install_sanity
|
2021-11-03 18:26:11 -04:00
|
|
|
install_fetch_deps unless ignore_deps?
|
2014-03-04 14:06:25 -06:00
|
|
|
end
|
|
|
|
|
2020-11-18 04:21:41 +01:00
|
|
|
sig { void }
|
2014-02-28 11:16:55 -06:00
|
|
|
def verify_deps_exist
|
2014-06-17 00:04:21 -05:00
|
|
|
begin
|
2016-03-23 09:20:50 -07:00
|
|
|
compute_dependencies
|
2014-06-17 00:04:21 -05:00
|
|
|
rescue TapFormulaUnavailableError => e
|
2021-10-31 00:27:05 -04:00
|
|
|
raise if e.tap.installed?
|
2016-09-23 22:02:23 +02:00
|
|
|
|
2023-07-13 19:45:28 +01:00
|
|
|
e.tap.ensure_installed!
|
|
|
|
retry if e.tap.installed? # It may have not installed if it's a core tap.
|
2014-06-17 00:04:21 -05:00
|
|
|
end
|
2014-02-28 11:16:55 -06:00
|
|
|
rescue FormulaUnavailableError => e
|
2015-05-27 21:51:48 +08:00
|
|
|
e.dependent = formula.full_name
|
2014-02-28 11:16:55 -06:00
|
|
|
raise
|
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { void }
|
2021-09-10 06:01:37 -07:00
|
|
|
def check_installation_already_attempted
|
2017-10-07 00:31:28 +02:00
|
|
|
raise FormulaInstallationAlreadyAttemptedError, formula if self.class.attempted.include?(formula)
|
2021-09-10 06:01:37 -07:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { void }
|
2021-09-10 06:01:37 -07:00
|
|
|
def check_install_sanity
|
|
|
|
check_installation_already_attempted
|
2013-01-08 19:54:32 -06:00
|
|
|
|
2020-11-06 11:25:31 +00:00
|
|
|
if force_bottle? && !pour_bottle?
|
|
|
|
raise CannotInstallFormulaError, "--force-bottle passed but #{formula.full_name} has no bottle!"
|
|
|
|
end
|
2020-12-12 00:10:34 +01:00
|
|
|
|
2021-03-05 12:06:34 +00:00
|
|
|
if Homebrew.default_prefix? &&
|
2022-05-30 14:59:14 +01:00
|
|
|
!build_from_source? && !build_bottle? && !formula.head? && formula.tap&.core_tap? &&
|
2020-12-16 12:38:52 +00:00
|
|
|
# Integration tests override homebrew-core locations
|
2022-11-09 14:45:43 +00:00
|
|
|
ENV["HOMEBREW_INTEGRATION_TEST"].nil? &&
|
2020-12-16 12:38:52 +00:00
|
|
|
!pour_bottle?
|
2021-01-01 19:51:40 +00:00
|
|
|
message = if !formula.pour_bottle? && formula.pour_bottle_check_unsatisfied_reason
|
|
|
|
formula_message = formula.pour_bottle_check_unsatisfied_reason
|
|
|
|
formula_message[0] = formula_message[0].downcase
|
|
|
|
|
2021-02-17 13:51:53 +00:00
|
|
|
<<~EOS
|
|
|
|
#{formula}: #{formula_message}
|
|
|
|
EOS
|
|
|
|
# don't want to complain about no bottle available if doing an
|
|
|
|
# upgrade/reinstall/dependency install (but do in the case the bottle
|
|
|
|
# check fails)
|
2022-11-21 22:08:11 -08:00
|
|
|
elsif fresh_install?(formula)
|
2021-01-01 19:51:40 +00:00
|
|
|
<<~EOS
|
|
|
|
#{formula}: no bottle available!
|
|
|
|
EOS
|
2020-12-30 14:11:45 +00:00
|
|
|
end
|
2021-02-17 13:51:53 +00:00
|
|
|
|
|
|
|
if message
|
|
|
|
message += <<~EOS
|
2022-10-06 10:01:02 +01:00
|
|
|
If you're feeling brave, you can try to install from source with:
|
2021-02-17 13:51:53 +00:00
|
|
|
brew install --build-from-source #{formula}
|
2022-10-06 10:01:02 +01:00
|
|
|
|
|
|
|
It is expected behaviour that most formulae will fail to build from source.
|
|
|
|
It is expected behaviour that Homebrew will be buggy and slow when building from source.
|
|
|
|
Do not create any issues about failures building from source on Homebrew's GitHub repositories.
|
|
|
|
Do not create any issues building from source even if you think this message is unrelated.
|
|
|
|
Any opened issues will be immediately closed without response.
|
2023-02-17 14:33:53 +00:00
|
|
|
Do not ask for help from Homebrew or its maintainers on social media.
|
2022-10-06 10:01:02 +01:00
|
|
|
You may ask for help building from source in Homebrew's discussions but are unlikely to receive a response.
|
|
|
|
If building from source fails, try to figure out the problem yourself and submit a fix as a pull request.
|
|
|
|
We will review it but may or may not accept it.
|
2021-02-17 13:51:53 +00:00
|
|
|
EOS
|
|
|
|
raise CannotInstallFormulaError, message
|
|
|
|
end
|
2020-12-12 00:10:34 +01:00
|
|
|
end
|
2020-11-06 11:25:31 +00:00
|
|
|
|
2016-12-10 19:21:17 +00:00
|
|
|
return if ignore_deps?
|
2016-09-04 00:01:01 +03:00
|
|
|
|
2021-03-09 14:28:52 +00:00
|
|
|
if Homebrew::EnvConfig.developer?
|
|
|
|
# `recursive_dependencies` trims cyclic dependencies, so we do one level and take the recursive deps of that.
|
|
|
|
# Mapping direct dependencies to deeper dependencies in a hash is also useful for the cyclic output below.
|
|
|
|
recursive_dep_map = formula.deps.to_h { |dep| [dep, dep.to_formula.recursive_dependencies] }
|
|
|
|
|
|
|
|
cyclic_dependencies = []
|
|
|
|
recursive_dep_map.each do |dep, recursive_deps|
|
|
|
|
if [formula.name, formula.full_name].include?(dep.name)
|
|
|
|
cyclic_dependencies << "#{formula.full_name} depends on itself directly"
|
|
|
|
elsif recursive_deps.any? { |rdep| [formula.name, formula.full_name].include?(rdep.name) }
|
|
|
|
cyclic_dependencies << "#{formula.full_name} depends on itself via #{dep.name}"
|
|
|
|
end
|
2017-02-21 21:29:32 +00:00
|
|
|
end
|
2020-11-12 17:06:47 +00:00
|
|
|
|
2021-03-09 14:28:52 +00:00
|
|
|
if cyclic_dependencies.present?
|
|
|
|
raise CannotInstallFormulaError, <<~EOS
|
|
|
|
#{formula.full_name} contains a recursive dependency on itself:
|
|
|
|
#{cyclic_dependencies.join("\n ")}
|
|
|
|
EOS
|
2020-11-12 17:06:47 +00:00
|
|
|
end
|
2017-02-21 21:29:32 +00:00
|
|
|
|
2021-03-09 14:28:52 +00:00
|
|
|
# Merge into one list
|
|
|
|
recursive_deps = recursive_dep_map.flat_map { |dep, rdeps| [dep] + rdeps }
|
|
|
|
Dependency.merge_repeats(recursive_deps)
|
|
|
|
else
|
|
|
|
recursive_deps = formula.recursive_dependencies
|
2017-02-21 21:29:32 +00:00
|
|
|
end
|
|
|
|
|
2021-03-09 14:28:52 +00:00
|
|
|
invalid_arch_dependencies = []
|
|
|
|
pinned_unsatisfied_deps = []
|
|
|
|
recursive_deps.each do |dep|
|
|
|
|
if (tab = Tab.for_formula(dep.to_formula)) && tab.arch.present? && tab.arch.to_s != Hardware::CPU.arch.to_s
|
|
|
|
invalid_arch_dependencies << "#{dep} was built for #{tab.arch}"
|
|
|
|
end
|
|
|
|
|
2021-04-30 12:17:05 +01:00
|
|
|
next unless dep.to_formula.pinned?
|
|
|
|
next if dep.satisfied?(inherited_options_for(dep))
|
|
|
|
next if dep.build? && pour_bottle?
|
2024-10-21 02:41:07 -07:00
|
|
|
next if dep.test?
|
2021-04-30 12:17:05 +01:00
|
|
|
|
|
|
|
pinned_unsatisfied_deps << dep
|
2017-02-21 21:29:32 +00:00
|
|
|
end
|
|
|
|
|
2021-03-09 14:28:52 +00:00
|
|
|
if invalid_arch_dependencies.present?
|
2020-11-12 17:06:47 +00:00
|
|
|
raise CannotInstallFormulaError, <<~EOS
|
|
|
|
#{formula.full_name} dependencies not built for the #{Hardware::CPU.arch} CPU architecture:
|
|
|
|
#{invalid_arch_dependencies.join("\n ")}
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
2016-09-23 22:02:23 +02:00
|
|
|
return if pinned_unsatisfied_deps.empty?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-09-23 22:02:23 +02:00
|
|
|
raise CannotInstallFormulaError,
|
2019-04-30 08:44:35 +01:00
|
|
|
"You must `brew unpin #{pinned_unsatisfied_deps * " "}` as installing " \
|
|
|
|
"#{formula.full_name} requires the latest version of pinned dependencies"
|
2009-10-26 18:13:38 +00:00
|
|
|
end
|
|
|
|
|
2022-11-21 22:08:11 -08:00
|
|
|
sig { params(_formula: Formula).returns(T.nilable(T::Boolean)) }
|
2024-09-09 08:22:43 -07:00
|
|
|
def fresh_install?(_formula) = false
|
2022-11-21 22:08:11 -08:00
|
|
|
|
2021-11-03 18:26:11 -04:00
|
|
|
sig { void }
|
|
|
|
def install_fetch_deps
|
|
|
|
return if @compute_dependencies.blank?
|
|
|
|
|
|
|
|
compute_dependencies(use_cache: false) if @compute_dependencies.any? do |dep, options|
|
2023-10-18 10:29:41 -04:00
|
|
|
next false unless dep.implicit?
|
2021-11-03 18:26:11 -04:00
|
|
|
|
|
|
|
fetch_dependencies
|
|
|
|
install_dependency(dep, options)
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { void }
|
2013-10-05 20:32:05 +01:00
|
|
|
def build_bottle_preinstall
|
2024-09-09 08:22:43 -07:00
|
|
|
@etc_var_preinstall = Find.find(*ETC_VAR_DIRS.select(&:directory?)).to_a
|
2013-10-05 20:32:05 +01:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { void }
|
2013-10-05 20:32:05 +01:00
|
|
|
def build_bottle_postinstall
|
2024-09-09 09:20:43 -07:00
|
|
|
etc_var_postinstall = Find.find(*ETC_VAR_DIRS.select(&:directory?)).to_a
|
|
|
|
(etc_var_postinstall - @etc_var_preinstall).each do |file|
|
2014-10-29 22:38:49 -05:00
|
|
|
Pathname.new(file).cp_path_sub(HOMEBREW_PREFIX, formula.bottle_prefix)
|
2013-10-05 20:32:05 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2011-08-23 23:30:52 +01:00
|
|
|
def install
|
2020-05-23 13:40:13 +01:00
|
|
|
lock
|
|
|
|
|
2018-06-20 00:54:14 -04:00
|
|
|
start_time = Time.now
|
2022-05-30 14:59:14 +01:00
|
|
|
Homebrew::Install.perform_build_from_source_checks if !pour_bottle? && DevelopmentTools.installed?
|
2018-06-11 00:45:25 -04:00
|
|
|
|
2017-09-28 11:36:56 -07:00
|
|
|
# Warn if a more recent version of this formula is available in the tap.
|
|
|
|
begin
|
2025-01-23 14:06:39 +00:00
|
|
|
if !quiet? &&
|
|
|
|
formula.pkg_version < (v = Formulary.factory(formula.full_name, force_bottle: force_bottle?).pkg_version)
|
2017-09-28 11:36:56 -07:00
|
|
|
opoo "#{formula.full_name} #{v} is available and more recent than version #{formula.pkg_version}."
|
|
|
|
end
|
|
|
|
rescue FormulaUnavailableError
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2013-06-09 13:44:59 -05:00
|
|
|
check_conflicts
|
|
|
|
|
2022-05-30 14:59:14 +01:00
|
|
|
raise UnbottledError, [formula] if !pour_bottle? && !DevelopmentTools.installed?
|
2015-06-29 14:09:57 -04:00
|
|
|
|
2016-12-10 19:21:17 +00:00
|
|
|
unless ignore_deps?
|
2021-09-08 07:24:34 -07:00
|
|
|
deps = compute_dependencies(use_cache: false)
|
2020-12-11 16:57:47 +00:00
|
|
|
if ((pour_bottle? && !DevelopmentTools.installed?) || build_bottle?) &&
|
|
|
|
(unbottled = unbottled_dependencies(deps)).presence
|
|
|
|
# Check that each dependency in deps has a bottle available, terminating
|
|
|
|
# abnormally with a UnbottledError if one or more don't.
|
|
|
|
raise UnbottledError, unbottled
|
|
|
|
end
|
|
|
|
|
2015-06-23 19:21:55 -04:00
|
|
|
install_dependencies(deps)
|
|
|
|
end
|
2010-01-13 09:00:24 +00:00
|
|
|
|
2014-03-13 10:11:00 -05:00
|
|
|
return if only_deps?
|
2013-12-16 16:37:59 -08:00
|
|
|
|
2014-12-27 14:26:56 -05:00
|
|
|
formula.deprecated_flags.each do |deprecated_option|
|
2014-10-16 13:02:04 +01:00
|
|
|
old_flag = deprecated_option.old_flag
|
|
|
|
new_flag = deprecated_option.current_flag
|
2015-05-27 21:51:48 +08:00
|
|
|
opoo "#{formula.full_name}: #{old_flag} was deprecated; using #{new_flag} instead!"
|
2014-10-16 13:02:04 +01:00
|
|
|
end
|
|
|
|
|
2020-05-18 12:46:23 +01:00
|
|
|
options = display_options(formula).join(" ")
|
2019-02-19 13:11:32 +00:00
|
|
|
oh1 "Installing #{Formatter.identifier(formula.full_name)} #{options}".strip if show_header?
|
2011-08-23 23:30:52 +01:00
|
|
|
|
2023-03-20 15:26:47 +00:00
|
|
|
if (tap = formula.tap) && tap.should_report_analytics?
|
2024-03-07 15:19:04 +00:00
|
|
|
Utils::Analytics.report_package_event(:formula_install, package_name: formula.name, tap_name: tap.name,
|
2024-03-07 16:20:20 +00:00
|
|
|
on_request: installed_on_request?, options:)
|
2016-04-12 11:02:22 +01:00
|
|
|
end
|
2016-03-28 09:21:02 +01:00
|
|
|
|
2017-10-07 00:31:28 +02:00
|
|
|
self.class.attempted << formula
|
2013-01-13 15:39:50 -06:00
|
|
|
|
2020-07-02 11:57:11 +01:00
|
|
|
if pour_bottle?
|
2014-10-17 22:41:26 -05:00
|
|
|
begin
|
2013-02-18 07:28:06 +00:00
|
|
|
pour
|
2025-01-12 16:56:48 +00:00
|
|
|
# Catch any other types of exceptions as they leave us with nothing installed.
|
2021-02-17 13:51:53 +00:00
|
|
|
rescue Exception # rubocop:disable Lint/RescueException
|
2015-09-25 21:16:17 +08:00
|
|
|
ignore_interrupts do
|
2019-05-24 16:46:54 +01:00
|
|
|
begin
|
2024-09-24 10:15:34 +01:00
|
|
|
FileUtils.rm_r(formula.prefix) if formula.prefix.directory?
|
2019-05-24 16:46:54 +01:00
|
|
|
rescue Errno::EACCES, Errno::ENOTEMPTY
|
|
|
|
odie <<~EOS
|
|
|
|
Could not remove #{formula.prefix.basename} keg! Do so manually:
|
|
|
|
sudo rm -rf #{formula.prefix}
|
|
|
|
EOS
|
|
|
|
end
|
2015-09-25 21:16:17 +08:00
|
|
|
formula.rack.rmdir_if_possible
|
|
|
|
end
|
2021-02-17 13:51:53 +00:00
|
|
|
raise
|
2014-10-17 22:41:26 -05:00
|
|
|
else
|
2013-03-11 18:56:26 +00:00
|
|
|
@poured_bottle = true
|
2013-02-18 07:28:06 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-12-10 17:58:41 +00:00
|
|
|
puts_requirement_messages
|
|
|
|
|
2014-03-13 10:11:00 -05:00
|
|
|
build_bottle_preinstall if build_bottle?
|
2013-10-05 20:32:05 +01:00
|
|
|
|
2013-03-11 18:56:26 +00:00
|
|
|
unless @poured_bottle
|
2011-08-23 23:30:52 +01:00
|
|
|
build
|
|
|
|
clean
|
2016-09-20 14:42:29 -07:00
|
|
|
|
|
|
|
# Store the formula used to build the keg in the keg.
|
2017-09-25 22:46:51 -07:00
|
|
|
formula_contents = if formula.local_bottle_path
|
|
|
|
Utils::Bottles.formula_contents formula.local_bottle_path, name: formula.name
|
|
|
|
else
|
|
|
|
formula.path.read
|
|
|
|
end
|
|
|
|
s = formula_contents.gsub(/ bottle do.+?end\n\n?/m, "")
|
2016-09-20 14:42:29 -07:00
|
|
|
brew_prefix = formula.prefix/".brew"
|
2022-04-12 17:34:48 -04:00
|
|
|
brew_prefix.mkpath
|
2016-09-20 14:42:29 -07:00
|
|
|
Pathname(brew_prefix/"#{formula.name}.rb").atomic_write(s)
|
2017-01-09 21:07:55 +00:00
|
|
|
|
|
|
|
keg = Keg.new(formula.prefix)
|
2024-04-28 03:23:21 +02:00
|
|
|
tab = keg.tab
|
2020-11-18 05:37:12 +01:00
|
|
|
tab.installed_as_dependency = installed_as_dependency?
|
|
|
|
tab.installed_on_request = installed_on_request?
|
2017-01-09 21:07:55 +00:00
|
|
|
tab.write
|
2010-02-09 11:30:16 -08:00
|
|
|
end
|
2011-08-23 23:30:52 +01:00
|
|
|
|
2014-03-13 10:11:00 -05:00
|
|
|
build_bottle_postinstall if build_bottle?
|
2013-10-05 20:32:05 +01:00
|
|
|
|
2019-12-03 11:42:09 +00:00
|
|
|
opoo "Nothing was installed to #{formula.prefix}" unless formula.latest_version_installed?
|
2018-06-20 00:54:14 -04:00
|
|
|
end_time = Time.now
|
2021-09-02 08:42:48 -07:00
|
|
|
Homebrew.messages.package_installed(formula.name, end_time - start_time)
|
2010-02-09 11:30:16 -08:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { void }
|
2013-06-09 13:44:59 -05:00
|
|
|
def check_conflicts
|
2020-07-25 03:48:33 +02:00
|
|
|
return if force?
|
2013-06-09 13:44:59 -05:00
|
|
|
|
2014-11-13 22:56:31 +09:00
|
|
|
conflicts = formula.conflicts.select do |c|
|
2019-10-13 10:03:26 +01:00
|
|
|
f = Formulary.factory(c.name)
|
|
|
|
rescue TapFormulaUnavailableError
|
|
|
|
# If the formula name is a fully-qualified name let's silently
|
|
|
|
# ignore it as we don't care about things used in taps that aren't
|
|
|
|
# currently tapped.
|
|
|
|
false
|
|
|
|
rescue FormulaUnavailableError => e
|
|
|
|
# If the formula name doesn't exist any more then complain but don't
|
|
|
|
# stop installation from continuing.
|
|
|
|
opoo <<~EOS
|
2019-10-13 10:11:35 +01:00
|
|
|
#{formula}: #{e.message}
|
2022-12-13 10:54:22 +00:00
|
|
|
'conflicts_with "#{c.name}"' should be removed from #{formula.path.basename}.
|
2019-10-13 10:12:01 +01:00
|
|
|
EOS
|
2016-09-23 22:02:23 +02:00
|
|
|
|
2020-04-05 15:44:50 +01:00
|
|
|
raise if Homebrew::EnvConfig.developer?
|
2016-09-23 22:02:23 +02:00
|
|
|
|
2024-05-09 13:19:14 +01:00
|
|
|
$stderr.puts "Please report this issue to the #{formula.tap&.full_name} tap".squeeze(" ")
|
|
|
|
$stderr.puts " (not Homebrew/brew or Homebrew/homebrew-core)!" unless formula.core_formula?
|
2019-10-13 10:03:26 +01:00
|
|
|
false
|
2021-02-17 13:51:53 +00:00
|
|
|
else
|
2019-10-14 09:03:02 +01:00
|
|
|
f.linked_keg.exist? && f.opt_prefix.exist?
|
2013-06-09 13:44:59 -05:00
|
|
|
end
|
|
|
|
|
2014-10-29 22:38:49 -05:00
|
|
|
raise FormulaConflictError.new(formula, conflicts) unless conflicts.empty?
|
2013-06-09 13:44:59 -05:00
|
|
|
end
|
|
|
|
|
2015-08-12 14:57:54 -04:00
|
|
|
# Compute and collect the dependencies needed by the formula currently
|
|
|
|
# being installed.
|
2024-09-08 17:33:54 -07:00
|
|
|
sig { params(use_cache: T::Boolean).returns(T::Array[[Dependency, Options]]) }
|
2021-09-08 07:24:34 -07:00
|
|
|
def compute_dependencies(use_cache: true)
|
2024-09-08 17:33:54 -07:00
|
|
|
@compute_dependencies = T.let(nil, T.nilable(T::Array[[Dependency, Options]])) unless use_cache
|
2021-03-06 00:42:26 +00:00
|
|
|
@compute_dependencies ||= begin
|
2023-09-02 21:46:07 -04:00
|
|
|
# Needs to be done before expand_dependencies
|
|
|
|
fetch_bottle_tab if pour_bottle?
|
|
|
|
|
2021-03-29 13:20:18 +01:00
|
|
|
check_requirements(expand_requirements)
|
|
|
|
expand_dependencies
|
2021-03-06 00:42:26 +00:00
|
|
|
end
|
2015-06-23 19:21:55 -04:00
|
|
|
end
|
|
|
|
|
2024-09-08 17:33:54 -07:00
|
|
|
sig { params(deps: T::Array[[Dependency, Options]]).returns(T::Array[Formula]) }
|
2020-12-11 16:57:47 +00:00
|
|
|
def unbottled_dependencies(deps)
|
2023-12-14 02:52:30 +00:00
|
|
|
deps.map { |(dep, _options)| dep.to_formula }.reject do |dep_f|
|
2020-12-11 16:57:47 +00:00
|
|
|
next false unless dep_f.pour_bottle?
|
2015-06-23 19:21:55 -04:00
|
|
|
|
2022-05-30 14:59:14 +01:00
|
|
|
dep_f.bottled?
|
2020-12-11 16:57:47 +00:00
|
|
|
end
|
2015-06-23 19:21:55 -04:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { void }
|
2015-06-23 19:21:55 -04:00
|
|
|
def compute_and_install_dependencies
|
|
|
|
deps = compute_dependencies
|
|
|
|
install_dependencies(deps)
|
2013-12-02 12:44:28 -06:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { params(req_map: T::Hash[Formula, T::Array[Requirement]]).void }
|
2013-12-09 14:36:10 -06:00
|
|
|
def check_requirements(req_map)
|
2016-11-19 02:03:30 +09:00
|
|
|
@requirement_messages = []
|
2013-12-09 14:36:10 -06:00
|
|
|
fatals = []
|
|
|
|
|
|
|
|
req_map.each_pair do |dependent, reqs|
|
|
|
|
reqs.each do |req|
|
2024-09-08 16:33:11 -07:00
|
|
|
next if dependent.latest_version_installed? && req.is_a?(MacOSRequirement) && req.comparator == "<="
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-11-19 02:03:30 +09:00
|
|
|
@requirement_messages << "#{dependent}: #{req.message}"
|
2013-12-09 14:36:10 -06:00
|
|
|
fatals << req if req.fatal?
|
2013-01-27 23:57:34 -06:00
|
|
|
end
|
2013-01-23 00:26:24 -06:00
|
|
|
end
|
|
|
|
|
2016-11-19 02:03:30 +09:00
|
|
|
return if fatals.empty?
|
|
|
|
|
2016-11-20 19:45:33 +09:00
|
|
|
puts_requirement_messages
|
2016-11-19 02:03:30 +09:00
|
|
|
raise UnsatisfiedRequirements, fatals
|
2013-01-23 00:26:24 -06:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { params(formula: Formula).returns(T::Array[Requirement]) }
|
2017-06-23 00:45:08 -07:00
|
|
|
def runtime_requirements(formula)
|
2018-07-16 16:46:39 +01:00
|
|
|
runtime_deps = formula.runtime_formula_dependencies(undeclared: false)
|
2017-06-23 00:45:08 -07:00
|
|
|
recursive_requirements = formula.recursive_requirements do |dependent, _|
|
|
|
|
Requirement.prune unless runtime_deps.include?(dependent)
|
|
|
|
end
|
|
|
|
(recursive_requirements.to_a + formula.requirements.to_a).reject(&:build?).uniq
|
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { returns(T::Hash[Formula, T::Array[Requirement]]) }
|
2013-12-09 14:36:10 -06:00
|
|
|
def expand_requirements
|
|
|
|
unsatisfied_reqs = Hash.new { |h, k| h[k] = [] }
|
2014-10-29 22:38:49 -05:00
|
|
|
formulae = [formula]
|
2024-01-22 10:41:48 -08:00
|
|
|
formula_deps_map = formula.recursive_dependencies
|
|
|
|
.each_with_object({}) { |dep, h| h[dep.name] = dep }
|
2013-12-09 14:36:10 -06:00
|
|
|
|
2021-02-12 18:33:37 +05:30
|
|
|
while (f = formulae.pop)
|
2017-06-23 00:45:08 -07:00
|
|
|
runtime_requirements = runtime_requirements(f)
|
2014-06-19 21:35:47 -05:00
|
|
|
f.recursive_requirements do |dependent, req|
|
|
|
|
build = effective_build_options_for(dependent)
|
2017-07-30 16:56:24 +01:00
|
|
|
install_bottle_for_dependent = install_bottle_for?(dependent, build)
|
2014-06-19 21:35:47 -05:00
|
|
|
|
2020-05-20 13:13:55 +01:00
|
|
|
keep_build_test = false
|
|
|
|
keep_build_test ||= runtime_requirements.include?(req)
|
|
|
|
keep_build_test ||= req.test? && include_test? && dependent == f
|
2020-05-22 23:26:23 +01:00
|
|
|
keep_build_test ||= req.build? && !install_bottle_for_dependent && !dependent.latest_version_installed?
|
2020-05-20 13:13:55 +01:00
|
|
|
|
2020-11-13 10:07:02 -05:00
|
|
|
if req.prune_from_option?(build) ||
|
2020-11-18 05:37:12 +01:00
|
|
|
req.satisfied?(env: @env, cc: @cc, build_bottle: @build_bottle, bottle_arch: @bottle_arch) ||
|
2020-11-13 10:07:02 -05:00
|
|
|
((req.build? || req.test?) && !keep_build_test) ||
|
2022-03-06 21:21:21 +00:00
|
|
|
formula_deps_map[dependent.name]&.build? ||
|
|
|
|
(only_deps? && f == dependent)
|
2020-05-20 23:38:09 +01:00
|
|
|
Requirement.prune
|
2014-06-19 21:35:47 -05:00
|
|
|
else
|
|
|
|
unsatisfied_reqs[dependent] << req
|
2013-12-09 14:36:10 -06:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-03-29 13:20:18 +01:00
|
|
|
unsatisfied_reqs
|
2013-12-09 14:36:10 -06:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { params(formula: Formula, inherited_options: T::Hash[String, Options]).returns(T::Array[Dependency]) }
|
2022-06-30 04:11:15 +01:00
|
|
|
def expand_dependencies_for_formula(formula, inherited_options)
|
2021-03-29 13:20:18 +01:00
|
|
|
# Cache for this expansion only. FormulaInstaller has a lot of inputs which can alter expansion.
|
|
|
|
cache_key = "FormulaInstaller-#{formula.full_name}-#{Time.now.to_f}"
|
2024-03-07 16:20:20 +00:00
|
|
|
Dependency.expand(formula, cache_key:) do |dependent, dep|
|
2016-06-02 02:09:35 -07:00
|
|
|
inherited_options[dep.name] |= inherited_options_for(dep)
|
2014-06-19 21:35:47 -05:00
|
|
|
build = effective_build_options_for(
|
|
|
|
dependent,
|
2024-09-08 16:33:11 -07:00
|
|
|
inherited_options.fetch(dependent.name, Options.new),
|
2014-06-19 21:35:47 -05:00
|
|
|
)
|
|
|
|
|
2020-05-20 13:13:55 +01:00
|
|
|
keep_build_test = false
|
2020-11-18 05:37:12 +01:00
|
|
|
keep_build_test ||= dep.test? && include_test? && @include_test_formulae.include?(dependent.full_name)
|
2021-06-02 11:21:44 -04:00
|
|
|
keep_build_test ||= dep.build? && !install_bottle_for?(dependent, build) &&
|
|
|
|
(formula.head? || !dependent.latest_version_installed?)
|
2020-05-20 13:13:55 +01:00
|
|
|
|
2023-11-10 17:17:24 +00:00
|
|
|
bottle_runtime_version = @bottle_tab_runtime_dependencies.dig(dep.name, "version").presence
|
|
|
|
bottle_runtime_version = Version.new(bottle_runtime_version) if bottle_runtime_version
|
|
|
|
bottle_runtime_revision = @bottle_tab_runtime_dependencies.dig(dep.name, "revision")
|
2023-09-02 21:46:07 -04:00
|
|
|
|
2020-11-13 17:21:51 +01:00
|
|
|
if dep.prune_from_option?(build) || ((dep.build? || dep.test?) && !keep_build_test)
|
2020-05-20 13:13:55 +01:00
|
|
|
Dependency.prune
|
2023-11-10 17:17:24 +00:00
|
|
|
elsif dep.satisfied?(inherited_options[dep.name], minimum_version: bottle_runtime_version,
|
|
|
|
minimum_revision: bottle_runtime_revision)
|
2014-06-19 21:35:47 -05:00
|
|
|
Dependency.skip
|
2013-06-03 22:50:11 -05:00
|
|
|
end
|
2013-01-23 00:26:25 -06:00
|
|
|
end
|
2022-06-30 04:11:15 +01:00
|
|
|
end
|
|
|
|
|
2024-09-08 17:33:54 -07:00
|
|
|
sig { returns(T::Array[[Dependency, Options]]) }
|
2022-06-30 04:11:15 +01:00
|
|
|
def expand_dependencies
|
|
|
|
inherited_options = Hash.new { |hash, key| hash[key] = Options.new }
|
|
|
|
|
2022-08-25 11:04:37 +01:00
|
|
|
expanded_deps = expand_dependencies_for_formula(formula, inherited_options)
|
2018-06-22 16:56:37 -07:00
|
|
|
|
2014-08-24 16:14:16 -05:00
|
|
|
expanded_deps.map { |dep| [dep, inherited_options[dep.name]] }
|
2014-02-27 14:22:43 -06:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { params(dependent: Formula, inherited_options: Options).returns(BuildOptions) }
|
|
|
|
def effective_build_options_for(dependent, inherited_options = Options.new)
|
2014-08-11 17:48:30 -05:00
|
|
|
args = dependent.build.used_options
|
2017-05-29 18:24:52 +01:00
|
|
|
args |= (dependent == formula) ? options : inherited_options
|
2014-08-24 14:46:34 -05:00
|
|
|
args |= Tab.for_formula(dependent).used_options
|
2016-01-09 12:00:35 +00:00
|
|
|
args &= dependent.options
|
2014-08-11 17:48:30 -05:00
|
|
|
BuildOptions.new(args, dependent.options)
|
2014-03-02 14:02:18 -06:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { params(formula: Formula).returns(T::Array[String]) }
|
2017-06-09 14:53:01 +03:00
|
|
|
def display_options(formula)
|
2020-05-18 12:46:23 +01:00
|
|
|
options = if formula.head?
|
|
|
|
["--HEAD"]
|
|
|
|
else
|
|
|
|
[]
|
2017-06-09 14:53:01 +03:00
|
|
|
end
|
|
|
|
options += effective_build_options_for(formula).used_options.to_a
|
2020-05-18 12:46:23 +01:00
|
|
|
options
|
2017-06-09 14:53:01 +03:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { params(dep: Dependency).returns(Options) }
|
2014-03-02 14:02:17 -06:00
|
|
|
def inherited_options_for(dep)
|
2014-04-04 10:12:09 -05:00
|
|
|
inherited_options = Options.new
|
2014-07-31 19:37:39 -05:00
|
|
|
u = Option.new("universal")
|
2014-10-29 22:38:49 -05:00
|
|
|
if (options.include?(u) || formula.require_universal_deps?) && !dep.build? && dep.to_formula.option_defined?(u)
|
2014-07-31 19:37:39 -05:00
|
|
|
inherited_options << u
|
2014-03-01 18:54:00 -06:00
|
|
|
end
|
2014-04-04 10:12:09 -05:00
|
|
|
inherited_options
|
2013-01-23 00:26:25 -06:00
|
|
|
end
|
|
|
|
|
2024-09-08 17:33:54 -07:00
|
|
|
sig { params(deps: T::Array[[Dependency, Options]]).void }
|
2013-12-09 14:36:10 -06:00
|
|
|
def install_dependencies(deps)
|
2015-06-23 19:21:55 -04:00
|
|
|
if deps.empty? && only_deps?
|
|
|
|
puts "All dependencies for #{formula.full_name} are satisfied."
|
2016-08-10 09:29:20 -04:00
|
|
|
elsif !deps.empty?
|
2018-09-02 16:15:09 +01:00
|
|
|
oh1 "Installing dependencies for #{formula.full_name}: " \
|
2024-03-03 18:55:56 -08:00
|
|
|
"#{deps.map(&:first).map { Formatter.identifier(_1) }.to_sentence}",
|
2018-09-02 16:15:09 +01:00
|
|
|
truncate: false
|
2015-06-23 19:21:55 -04:00
|
|
|
deps.each { |dep, options| install_dependency(dep, options) }
|
2013-10-16 15:34:43 -05:00
|
|
|
end
|
|
|
|
|
2013-12-09 14:36:10 -06:00
|
|
|
@show_header = true unless deps.empty?
|
2013-01-23 00:26:25 -06:00
|
|
|
end
|
|
|
|
|
2020-11-20 21:51:07 +11:00
|
|
|
sig { params(dep: Dependency).void }
|
2020-05-03 14:53:50 +01:00
|
|
|
def fetch_dependency(dep)
|
|
|
|
df = dep.to_formula
|
2020-11-18 05:37:12 +01:00
|
|
|
fi = FormulaInstaller.new(
|
|
|
|
df,
|
|
|
|
force_bottle: false,
|
|
|
|
# When fetching we don't need to recurse the dependency tree as it's already
|
|
|
|
# been done for us in `compute_dependencies` and there's no requirement to
|
|
|
|
# fetch in a particular order.
|
2023-09-08 14:46:15 -04:00
|
|
|
# Note, this tree can vary when pouring bottles so we need to check it then.
|
2023-09-05 08:22:55 -04:00
|
|
|
ignore_deps: !pour_bottle?,
|
2020-12-18 14:55:59 +00:00
|
|
|
installed_as_dependency: true,
|
2020-11-18 05:37:12 +01:00
|
|
|
include_test_formulae: @include_test_formulae,
|
|
|
|
build_from_source_formulae: @build_from_source_formulae,
|
|
|
|
keep_tmp: keep_tmp?,
|
2022-07-26 12:15:53 +01:00
|
|
|
debug_symbols: debug_symbols?,
|
2020-11-18 05:37:12 +01:00
|
|
|
force: force?,
|
|
|
|
debug: debug?,
|
|
|
|
quiet: quiet?,
|
|
|
|
verbose: verbose?,
|
|
|
|
)
|
2021-03-05 12:06:34 +00:00
|
|
|
fi.prelude
|
2020-05-03 14:53:50 +01:00
|
|
|
fi.fetch
|
|
|
|
end
|
|
|
|
|
2024-09-08 17:33:54 -07:00
|
|
|
sig { params(dep: Dependency, inherited_options: Options).void }
|
2014-02-27 14:22:43 -06:00
|
|
|
def install_dependency(dep, inherited_options)
|
2014-02-27 14:22:42 -06:00
|
|
|
df = dep.to_formula
|
2013-01-23 00:26:27 -06:00
|
|
|
|
2014-04-05 10:48:54 -05:00
|
|
|
if df.linked_keg.directory?
|
2014-04-05 12:17:19 -05:00
|
|
|
linked_keg = Keg.new(df.linked_keg.resolved_path)
|
2024-04-28 03:23:21 +02:00
|
|
|
tab = linked_keg.tab
|
2017-07-30 21:44:43 +01:00
|
|
|
keg_had_linked_keg = true
|
2017-07-30 20:04:41 +01:00
|
|
|
keg_was_linked = linked_keg.linked?
|
2014-04-05 10:48:54 -05:00
|
|
|
linked_keg.unlink
|
2024-09-08 17:33:54 -07:00
|
|
|
else
|
|
|
|
keg_had_linked_keg = false
|
2014-04-05 10:48:54 -05:00
|
|
|
end
|
|
|
|
|
2020-05-18 13:50:43 +01:00
|
|
|
if df.latest_version_installed?
|
2014-04-05 10:48:54 -05:00
|
|
|
installed_keg = Keg.new(df.prefix)
|
2024-04-28 03:23:21 +02:00
|
|
|
tab ||= installed_keg.tab
|
2014-04-05 10:48:54 -05:00
|
|
|
tmp_keg = Pathname.new("#{installed_keg}.tmp")
|
|
|
|
installed_keg.rename(tmp_keg)
|
|
|
|
end
|
2012-03-07 13:48:04 +00:00
|
|
|
|
2021-02-23 10:19:45 +00:00
|
|
|
if df.tap.present? && tab.present? && (tab_tap = tab.source["tap"].presence) &&
|
|
|
|
df.tap.to_s != tab_tap.to_s
|
2018-11-10 12:04:26 +00:00
|
|
|
odie <<~EOS
|
|
|
|
#{df} is already installed from #{tab_tap}!
|
|
|
|
Please `brew uninstall #{df}` first."
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
options = Options.new
|
2021-02-23 10:19:45 +00:00
|
|
|
options |= tab.used_options if tab.present?
|
2020-11-18 05:37:12 +01:00
|
|
|
options |= Tab.remap_deprecated_options(df.deprecated_options, dep.options)
|
|
|
|
options |= inherited_options
|
|
|
|
options &= df.options
|
|
|
|
|
2025-02-28 09:59:32 +00:00
|
|
|
installed_on_request = df.any_version_installed? && tab.present? && tab.installed_on_request
|
|
|
|
installed_on_request ||= false
|
2025-01-13 09:24:42 +00:00
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
fi = FormulaInstaller.new(
|
|
|
|
df,
|
2024-03-07 16:20:20 +00:00
|
|
|
options:,
|
2024-09-08 16:43:25 -07:00
|
|
|
link_keg: keg_had_linked_keg && keg_was_linked,
|
2022-12-22 23:52:26 -05:00
|
|
|
installed_as_dependency: true,
|
2025-01-13 09:24:42 +00:00
|
|
|
installed_on_request:,
|
2022-12-22 23:52:26 -05:00
|
|
|
force_bottle: false,
|
|
|
|
include_test_formulae: @include_test_formulae,
|
|
|
|
build_from_source_formulae: @build_from_source_formulae,
|
|
|
|
keep_tmp: keep_tmp?,
|
|
|
|
debug_symbols: debug_symbols?,
|
|
|
|
force: force?,
|
|
|
|
debug: debug?,
|
|
|
|
quiet: quiet?,
|
|
|
|
verbose: verbose?,
|
2020-11-18 05:37:12 +01:00
|
|
|
)
|
2016-08-30 21:38:13 +02:00
|
|
|
oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}"
|
2011-11-26 21:03:46 -08:00
|
|
|
fi.install
|
|
|
|
fi.finish
|
2025-01-12 16:56:48 +00:00
|
|
|
# Handle all possible exceptions installing deps.
|
2020-03-21 02:49:17 -07:00
|
|
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
2014-04-05 10:48:54 -05:00
|
|
|
ignore_interrupts do
|
2023-03-25 17:39:39 -07:00
|
|
|
tmp_keg.rename(installed_keg.to_path) if tmp_keg && !installed_keg.directory?
|
2020-08-02 04:46:32 +02:00
|
|
|
linked_keg.link(verbose: verbose?) if keg_was_linked
|
2014-04-05 10:48:54 -05:00
|
|
|
end
|
2020-03-21 02:49:17 -07:00
|
|
|
raise unless e.is_a? FormulaInstallationAlreadyAttemptedError
|
|
|
|
|
|
|
|
# We already attempted to install f as part of another formula's
|
|
|
|
# dependency tree. In that case, don't generate an error, just move on.
|
|
|
|
nil
|
2014-04-05 10:48:54 -05:00
|
|
|
else
|
2024-09-24 10:15:34 +01:00
|
|
|
ignore_interrupts { FileUtils.rm_r(tmp_keg) if tmp_keg&.directory? }
|
2011-11-26 21:03:46 -08:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2011-08-23 23:30:52 +01:00
|
|
|
def caveats
|
2014-03-13 10:11:00 -05:00
|
|
|
return if only_deps?
|
2013-12-16 16:37:59 -08:00
|
|
|
|
2020-04-05 15:44:50 +01:00
|
|
|
audit_installed if Homebrew::EnvConfig.developer?
|
2012-07-18 03:22:00 -05:00
|
|
|
|
2021-05-19 12:11:01 +01:00
|
|
|
return if !installed_on_request? || installed_as_dependency?
|
2025-01-23 14:06:39 +00:00
|
|
|
return if quiet?
|
2021-05-19 12:11:01 +01:00
|
|
|
|
2017-06-26 07:30:28 +02:00
|
|
|
caveats = Caveats.new(formula)
|
2013-01-12 13:08:29 -06:00
|
|
|
|
2017-06-26 07:30:28 +02:00
|
|
|
return if caveats.empty?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-09-23 22:02:23 +02:00
|
|
|
@show_summary_heading = true
|
2017-06-26 07:30:28 +02:00
|
|
|
ohai "Caveats", caveats.to_s
|
2021-06-08 23:43:14 +01:00
|
|
|
Homebrew.messages.record_caveats(formula.name, caveats)
|
2011-08-23 23:30:52 +01:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2011-08-23 23:30:52 +01:00
|
|
|
def finish
|
2014-03-13 10:11:00 -05:00
|
|
|
return if only_deps?
|
2013-12-16 16:37:59 -08:00
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
ohai "Finishing up" if verbose?
|
2011-08-23 23:30:52 +01:00
|
|
|
|
2014-10-29 22:38:49 -05:00
|
|
|
keg = Keg.new(formula.prefix)
|
2025-01-23 14:42:03 +00:00
|
|
|
if skip_link?
|
|
|
|
unless quiet?
|
|
|
|
ohai "Skipping 'link' on request"
|
|
|
|
puts "You can run it manually using:"
|
|
|
|
puts " brew link #{formula.full_name}"
|
|
|
|
end
|
|
|
|
else
|
|
|
|
link(keg)
|
|
|
|
end
|
2015-06-05 22:42:56 -04:00
|
|
|
|
2021-07-26 21:13:53 +02:00
|
|
|
install_service
|
|
|
|
|
2021-01-07 13:49:05 -08:00
|
|
|
fix_dynamic_linkage(keg) if !@poured_bottle || !formula.bottle_specification.skip_relocation?
|
2011-08-23 23:30:52 +01:00
|
|
|
|
2022-09-09 20:55:59 +01:00
|
|
|
Homebrew::Install.global_post_install
|
|
|
|
|
2023-03-23 15:22:27 +00:00
|
|
|
if build_bottle? || skip_post_install?
|
2025-01-23 14:06:39 +00:00
|
|
|
unless quiet?
|
|
|
|
if build_bottle?
|
|
|
|
ohai "Not running 'post_install' as we're building a bottle"
|
|
|
|
elsif skip_post_install?
|
|
|
|
ohai "Skipping 'post_install' on request"
|
|
|
|
end
|
|
|
|
puts "You can run it manually using:"
|
|
|
|
puts " brew postinstall #{formula.full_name}"
|
2023-03-23 15:22:27 +00:00
|
|
|
end
|
2017-05-03 09:00:17 +01:00
|
|
|
else
|
2023-06-22 03:06:45 +01:00
|
|
|
formula.install_etc_var
|
|
|
|
post_install if formula.post_install_defined?
|
2014-11-23 13:43:35 +00:00
|
|
|
end
|
2013-12-05 10:09:14 -06:00
|
|
|
|
2022-07-30 11:08:52 +01:00
|
|
|
keg.prepare_debug_symbols if debug_symbols?
|
2022-07-26 00:00:45 +01:00
|
|
|
|
2018-05-15 12:49:48 -04:00
|
|
|
# Updates the cache for a particular formula after doing an install
|
2018-05-22 14:46:14 +01:00
|
|
|
CacheStoreDatabase.use(:linkage) do |db|
|
|
|
|
break unless db.created?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2018-06-06 13:27:59 +01:00
|
|
|
LinkageChecker.new(keg, formula, cache_db: db, rebuild_cache: true)
|
2018-05-15 12:49:48 -04:00
|
|
|
end
|
|
|
|
|
2018-06-01 13:29:46 +01:00
|
|
|
# Update tab with actual runtime dependencies
|
2024-04-28 03:23:21 +02:00
|
|
|
tab = keg.tab
|
2018-06-01 13:29:46 +01:00
|
|
|
Tab.clear_cache
|
2018-06-01 20:39:39 +01:00
|
|
|
f_runtime_deps = formula.runtime_dependencies(read_from_tab: false)
|
2021-04-29 17:51:13 +01:00
|
|
|
tab.runtime_dependencies = Tab.runtime_deps_hash(formula, f_runtime_deps)
|
2018-06-01 13:29:46 +01:00
|
|
|
tab.write
|
|
|
|
|
2024-05-13 07:36:51 +01:00
|
|
|
# write/update a SBOM file (if we aren't bottling)
|
|
|
|
unless build_bottle?
|
2024-05-09 13:10:35 +01:00
|
|
|
sbom = SBOM.create(formula, tab)
|
|
|
|
sbom.write(validate: Homebrew::EnvConfig.developer?)
|
|
|
|
end
|
|
|
|
|
2020-08-23 06:32:26 +02:00
|
|
|
# let's reset Utils::Git.available? if we just installed git
|
|
|
|
Utils::Git.clear_available_cache if formula.name == "git"
|
2017-09-16 12:41:08 +01:00
|
|
|
|
2021-10-01 15:06:04 +01:00
|
|
|
# use installed ca-certificates when it's needed and available
|
|
|
|
if formula.name == "ca-certificates" &&
|
|
|
|
!DevelopmentTools.ca_file_handles_most_https_certificates?
|
2024-09-08 16:33:11 -07:00
|
|
|
ENV["SSL_CERT_FILE"] = ENV["GIT_SSL_CAINFO"] = (formula.pkgetc/"cert.pem").to_s
|
|
|
|
ENV["GIT_SSL_CAPATH"] = formula.pkgetc.to_s
|
2021-10-01 15:06:04 +01:00
|
|
|
end
|
|
|
|
|
2017-09-16 12:41:08 +01:00
|
|
|
# use installed curl when it's needed and available
|
|
|
|
if formula.name == "curl" &&
|
|
|
|
!DevelopmentTools.curl_handles_most_https_certificates?
|
2024-09-08 16:33:11 -07:00
|
|
|
ENV["HOMEBREW_CURL"] = (formula.opt_bin/"curl").to_s
|
2022-05-30 04:10:25 +01:00
|
|
|
Utils::Curl.clear_path_cache
|
2017-09-16 12:41:08 +01:00
|
|
|
end
|
2018-06-01 13:29:46 +01:00
|
|
|
|
|
|
|
caveats
|
|
|
|
|
|
|
|
ohai "Summary" if verbose? || show_summary_heading?
|
|
|
|
puts summary
|
2020-07-02 12:53:52 +01:00
|
|
|
|
|
|
|
self.class.installed << formula
|
2013-06-11 15:35:30 -05:00
|
|
|
ensure
|
2015-07-17 16:08:49 +08:00
|
|
|
unlock
|
2009-10-26 18:13:38 +00:00
|
|
|
end
|
2010-07-20 21:03:25 -07:00
|
|
|
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(String) }
|
2013-12-12 15:42:31 -06:00
|
|
|
def summary
|
2019-04-20 14:07:29 +09:00
|
|
|
s = +""
|
2020-04-05 15:44:50 +01:00
|
|
|
s << "#{Homebrew::EnvConfig.install_badge} " unless Homebrew::EnvConfig.no_emoji?
|
2016-12-31 16:38:05 +00:00
|
|
|
s << "#{formula.prefix.resolved_path}: #{formula.prefix.abv}"
|
2013-12-12 15:42:31 -06:00
|
|
|
s << ", built in #{pretty_duration build_time}" if build_time
|
2019-04-18 17:33:02 +09:00
|
|
|
s.freeze
|
2013-12-12 15:42:31 -06:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { returns(T.nilable(Float)) }
|
2011-08-23 23:30:52 +01:00
|
|
|
def build_time
|
2024-09-08 16:33:11 -07:00
|
|
|
@build_time ||= T.let(Time.now - @start_time, T.nilable(Float)) if @start_time && !interactive?
|
2011-08-23 23:30:52 +01:00
|
|
|
end
|
2009-10-26 18:13:38 +00:00
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { returns(T::Array[String]) }
|
2016-09-21 09:07:04 +02:00
|
|
|
def sanitized_argv_options
|
2014-03-13 10:10:59 -05:00
|
|
|
args = []
|
2014-03-13 19:10:41 -05:00
|
|
|
args << "--ignore-dependencies" if ignore_deps?
|
2014-03-13 10:10:59 -05:00
|
|
|
|
2014-03-13 10:11:00 -05:00
|
|
|
if build_bottle?
|
2014-03-13 10:10:59 -05:00
|
|
|
args << "--build-bottle"
|
2020-11-18 05:37:12 +01:00
|
|
|
args << "--bottle-arch=#{@bottle_arch}" if @bottle_arch
|
2014-03-13 10:10:59 -05:00
|
|
|
end
|
|
|
|
|
2014-11-03 21:34:41 -06:00
|
|
|
args << "--git" if git?
|
|
|
|
args << "--interactive" if interactive?
|
2014-03-13 10:11:00 -05:00
|
|
|
args << "--verbose" if verbose?
|
2014-03-13 10:11:00 -05:00
|
|
|
args << "--debug" if debug?
|
2020-11-18 05:37:12 +01:00
|
|
|
args << "--cc=#{@cc}" if @cc
|
2020-07-25 03:48:33 +02:00
|
|
|
args << "--keep-tmp" if keep_tmp?
|
2022-07-26 16:15:26 +01:00
|
|
|
|
|
|
|
if debug_symbols?
|
|
|
|
args << "--debug-symbols"
|
|
|
|
args << "--build-from-source"
|
|
|
|
end
|
2015-04-16 20:33:32 -04:00
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
if @env.present?
|
|
|
|
args << "--env=#{@env}"
|
2016-09-13 12:36:56 -04:00
|
|
|
elsif formula.env.std? || formula.deps.select(&:build?).any? { |d| d.name == "scons" }
|
2015-04-16 20:33:32 -04:00
|
|
|
args << "--env=std"
|
|
|
|
end
|
2014-06-19 21:35:47 -05:00
|
|
|
|
2020-09-03 10:34:22 +01:00
|
|
|
args << "--HEAD" if formula.head?
|
2014-04-26 19:09:49 -05:00
|
|
|
|
2014-03-08 16:54:19 -06:00
|
|
|
args
|
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { returns(T::Array[String]) }
|
2024-09-09 08:22:43 -07:00
|
|
|
def build_argv = sanitized_argv_options + options.as_flags
|
2013-01-23 00:26:28 -06:00
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2011-08-23 23:30:52 +01:00
|
|
|
def build
|
2015-04-25 22:07:06 -04:00
|
|
|
FileUtils.rm_rf(formula.logs)
|
2012-09-11 20:59:59 -04:00
|
|
|
|
2024-09-08 16:43:25 -07:00
|
|
|
@start_time = Time.now
|
2009-10-26 18:13:38 +00:00
|
|
|
|
|
|
|
# 1. formulae can modify ENV, so we must ensure that each
|
2010-11-09 12:57:41 +00:00
|
|
|
# installation has a pristine ENV when it starts, forking now is
|
2009-10-26 18:13:38 +00:00
|
|
|
# the easiest way to do this
|
2021-02-25 16:29:05 +00:00
|
|
|
args = [
|
|
|
|
"nice",
|
|
|
|
*HOMEBREW_RUBY_EXEC_ARGS,
|
|
|
|
"--",
|
|
|
|
HOMEBREW_LIBRARY_PATH/"build.rb",
|
|
|
|
formula.specified_path,
|
2013-04-05 17:42:53 -05:00
|
|
|
].concat(build_argv)
|
|
|
|
|
2024-08-28 03:45:26 +01:00
|
|
|
if Sandbox.available?
|
|
|
|
sandbox = Sandbox.new
|
|
|
|
formula.logs.mkpath
|
|
|
|
sandbox.record_log(formula.logs/"build.sandbox.log")
|
|
|
|
sandbox.allow_write_path(Dir.home) if interactive?
|
|
|
|
sandbox.allow_write_temp_and_cache
|
|
|
|
sandbox.allow_write_log(formula)
|
|
|
|
sandbox.allow_cvs
|
|
|
|
sandbox.allow_fossil
|
|
|
|
sandbox.allow_write_xcode
|
|
|
|
sandbox.allow_write_cellar(formula)
|
|
|
|
sandbox.deny_all_network unless formula.network_access_allowed?(:build)
|
|
|
|
sandbox.run(*args)
|
|
|
|
else
|
|
|
|
Utils.safe_fork do
|
2015-04-13 18:02:46 +08:00
|
|
|
exec(*args)
|
2015-04-08 14:15:38 +08:00
|
|
|
end
|
2012-08-10 16:04:56 -04:00
|
|
|
end
|
|
|
|
|
2016-07-13 10:11:59 +03:00
|
|
|
formula.update_head_version
|
|
|
|
|
2019-02-19 13:11:32 +00:00
|
|
|
raise "Empty installation" if !formula.prefix.directory? || Keg.new(formula.prefix).empty_installation?
|
2025-01-12 16:56:48 +00:00
|
|
|
# Handle all possible exceptions when building.
|
2017-10-07 00:31:28 +02:00
|
|
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
2018-08-17 22:42:37 -04:00
|
|
|
if e.is_a? BuildError
|
|
|
|
e.formula = formula
|
2020-05-18 12:46:23 +01:00
|
|
|
e.options = display_options(formula)
|
2018-08-17 22:42:37 -04:00
|
|
|
end
|
|
|
|
|
2012-08-10 16:04:56 -04:00
|
|
|
ignore_interrupts do
|
|
|
|
# any exceptions must leave us with nothing installed
|
2016-07-13 10:11:59 +03:00
|
|
|
formula.update_head_version
|
2024-09-24 10:15:34 +01:00
|
|
|
FileUtils.rm_r(formula.prefix) if formula.prefix.directory?
|
2014-10-29 22:38:49 -05:00
|
|
|
formula.rack.rmdir_if_possible
|
2010-11-09 12:57:41 +00:00
|
|
|
end
|
2018-08-17 22:42:37 -04:00
|
|
|
raise e
|
2009-10-26 18:13:38 +00:00
|
|
|
end
|
2011-08-23 23:30:52 +01:00
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { params(keg: Keg).void }
|
2014-07-24 19:39:09 -05:00
|
|
|
def link(keg)
|
2020-10-29 08:31:04 +00:00
|
|
|
Formula.clear_cache
|
|
|
|
|
2017-07-30 21:44:43 +01:00
|
|
|
unless link_keg
|
2014-07-24 19:39:09 -05:00
|
|
|
begin
|
2022-01-10 10:05:44 -06:00
|
|
|
keg.optlink(verbose: verbose?, overwrite: overwrite?)
|
2014-07-24 19:39:09 -05:00
|
|
|
rescue Keg::LinkError => e
|
2020-07-29 19:41:39 -04:00
|
|
|
ofail "Failed to create #{formula.opt_prefix}"
|
2015-05-27 21:51:48 +08:00
|
|
|
puts "Things that depend on #{formula.full_name} will probably not build."
|
2014-07-24 19:39:09 -05:00
|
|
|
puts e
|
2014-07-24 19:39:09 -05:00
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2019-01-25 20:10:02 +00:00
|
|
|
cask_installed_with_formula_name = begin
|
2024-05-09 17:08:12 +01:00
|
|
|
Cask::CaskLoader.load(formula.name, warn: false).installed?
|
2019-02-11 15:11:19 -08:00
|
|
|
rescue Cask::CaskUnavailableError, Cask::CaskInvalidError
|
2019-01-25 20:10:02 +00:00
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
if cask_installed_with_formula_name
|
|
|
|
ohai "#{formula.name} cask is installed, skipping link."
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2014-06-27 16:20:17 -05:00
|
|
|
if keg.linked?
|
2012-02-21 11:40:06 +00:00
|
|
|
opoo "This keg was marked linked already, continuing anyway"
|
2014-06-27 16:20:17 -05:00
|
|
|
keg.remove_linked_keg_record
|
2012-02-21 11:40:06 +00:00
|
|
|
end
|
|
|
|
|
2020-11-02 11:20:09 +00:00
|
|
|
Homebrew::Unlink.unlink_versioned_formulae(formula, verbose: verbose?)
|
|
|
|
|
2015-09-05 14:36:50 +08:00
|
|
|
link_overwrite_backup = {} # Hash: conflict file -> backup file
|
2015-08-23 16:35:51 +08:00
|
|
|
backup_dir = HOMEBREW_CACHE/"Backup"
|
|
|
|
|
2012-08-22 15:50:27 -04:00
|
|
|
begin
|
2022-01-10 10:05:44 -06:00
|
|
|
keg.link(verbose: verbose?, overwrite: overwrite?)
|
2014-06-27 16:10:42 -05:00
|
|
|
rescue Keg::ConflictError => e
|
2015-08-23 16:35:51 +08:00
|
|
|
conflict_file = e.dst
|
|
|
|
if formula.link_overwrite?(conflict_file) && !link_overwrite_backup.key?(conflict_file)
|
|
|
|
backup_file = backup_dir/conflict_file.relative_path_from(HOMEBREW_PREFIX).to_s
|
|
|
|
backup_file.parent.mkpath
|
2020-07-09 15:06:15 +01:00
|
|
|
FileUtils.mv conflict_file, backup_file
|
2015-08-23 16:35:51 +08:00
|
|
|
link_overwrite_backup[conflict_file] = backup_file
|
|
|
|
retry
|
|
|
|
end
|
2020-07-29 19:41:39 -04:00
|
|
|
ofail "The `brew link` step did not complete successfully"
|
2012-08-22 15:50:27 -04:00
|
|
|
puts "The formula built, but is not symlinked into #{HOMEBREW_PREFIX}"
|
2014-06-27 16:10:42 -05:00
|
|
|
puts e
|
2013-06-23 16:32:31 -07:00
|
|
|
puts
|
|
|
|
puts "Possible conflicting files are:"
|
2020-08-02 04:46:32 +02:00
|
|
|
keg.link(dry_run: true, overwrite: true, verbose: verbose?)
|
2012-08-22 15:50:27 -04:00
|
|
|
@show_summary_heading = true
|
2014-06-27 16:10:42 -05:00
|
|
|
rescue Keg::LinkError => e
|
2020-07-29 19:41:39 -04:00
|
|
|
ofail "The `brew link` step did not complete successfully"
|
2014-06-27 16:10:42 -05:00
|
|
|
puts "The formula built, but is not symlinked into #{HOMEBREW_PREFIX}"
|
|
|
|
puts e
|
|
|
|
puts
|
|
|
|
puts "You can try again using:"
|
2014-10-29 22:38:49 -05:00
|
|
|
puts " brew link #{formula.name}"
|
2014-06-27 16:10:42 -05:00
|
|
|
@show_summary_heading = true
|
2025-01-12 16:56:48 +00:00
|
|
|
# Handle all other possible exceptions when linking.
|
2017-10-07 00:31:28 +02:00
|
|
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
2020-07-29 19:41:39 -04:00
|
|
|
ofail "An unexpected error occurred during the `brew link` step"
|
2014-04-21 09:40:24 -05:00
|
|
|
puts "The formula built, but is not symlinked into #{HOMEBREW_PREFIX}"
|
|
|
|
puts e
|
2024-07-15 13:44:01 -04:00
|
|
|
|
|
|
|
if debug?
|
|
|
|
require "utils/backtrace"
|
|
|
|
puts Utils::Backtrace.clean(e)
|
|
|
|
end
|
|
|
|
|
2014-04-21 09:40:24 -05:00
|
|
|
@show_summary_heading = true
|
2015-08-23 16:35:51 +08:00
|
|
|
ignore_interrupts do
|
|
|
|
keg.unlink
|
2015-09-05 14:36:50 +08:00
|
|
|
link_overwrite_backup.each do |origin, backup|
|
|
|
|
origin.parent.mkpath
|
2020-07-09 15:06:15 +01:00
|
|
|
FileUtils.mv backup, origin
|
2015-08-23 16:35:51 +08:00
|
|
|
end
|
|
|
|
end
|
2014-04-21 09:40:24 -05:00
|
|
|
raise
|
2012-08-22 15:50:27 -04:00
|
|
|
end
|
2015-08-23 16:35:51 +08:00
|
|
|
|
2016-09-23 22:02:23 +02:00
|
|
|
return if link_overwrite_backup.empty?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2021-01-26 15:21:24 -05:00
|
|
|
opoo "These files were overwritten during the `brew link` step:"
|
2016-09-23 22:02:23 +02:00
|
|
|
puts link_overwrite_backup.keys
|
|
|
|
puts
|
2021-01-26 15:21:24 -05:00
|
|
|
puts "They have been backed up to: #{backup_dir}"
|
2016-09-23 22:02:23 +02:00
|
|
|
@show_summary_heading = true
|
2011-08-23 23:30:52 +01:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2020-12-11 23:14:50 +01:00
|
|
|
def install_service
|
2024-05-07 12:01:26 +01:00
|
|
|
service = if formula.service? && formula.service.command?
|
2021-05-04 16:22:28 +02:00
|
|
|
service_path = formula.systemd_service_path
|
|
|
|
service_path.atomic_write(formula.service.to_systemd_unit)
|
|
|
|
service_path.chmod 0644
|
2021-11-20 15:14:50 +01:00
|
|
|
|
|
|
|
if formula.service.timed?
|
|
|
|
timer_path = formula.systemd_timer_path
|
|
|
|
timer_path.atomic_write(formula.service.to_systemd_timer)
|
|
|
|
timer_path.chmod 0644
|
|
|
|
end
|
2021-05-04 16:22:28 +02:00
|
|
|
|
2020-12-11 23:14:50 +01:00
|
|
|
formula.service.to_plist
|
|
|
|
end
|
2021-05-04 16:22:28 +02:00
|
|
|
return unless service
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2023-02-07 19:25:51 +01:00
|
|
|
launchd_service_path = formula.launchd_service_path
|
|
|
|
launchd_service_path.atomic_write(service)
|
|
|
|
launchd_service_path.chmod 0644
|
2015-03-23 01:14:21 +08:00
|
|
|
log = formula.var/"log"
|
2021-05-04 16:22:28 +02:00
|
|
|
log.mkpath if service.include? log.to_s
|
2025-01-12 16:56:48 +00:00
|
|
|
# Handle all possible exceptions when installing service files.
|
2017-10-07 00:31:28 +02:00
|
|
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
2023-01-13 14:40:03 +01:00
|
|
|
puts e
|
2021-05-04 16:22:28 +02:00
|
|
|
ofail "Failed to install service files"
|
2024-07-15 13:44:01 -04:00
|
|
|
|
|
|
|
require "utils/backtrace"
|
2023-09-11 21:54:27 -07:00
|
|
|
odebug e, Utils::Backtrace.clean(e)
|
2012-09-11 20:57:41 -04:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { params(keg: Keg).void }
|
2016-07-09 13:52:05 +01:00
|
|
|
def fix_dynamic_linkage(keg)
|
|
|
|
keg.fix_dynamic_linkage
|
2025-01-12 16:56:48 +00:00
|
|
|
# Rescue all possible exceptions when fixing linkage.
|
2017-10-07 00:31:28 +02:00
|
|
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
2020-07-29 19:41:39 -04:00
|
|
|
ofail "Failed to fix install linkage"
|
2011-08-23 23:30:52 +01:00
|
|
|
puts "The formula built, but you may encounter issues using it or linking other"
|
2019-04-08 12:47:15 -04:00
|
|
|
puts "formulae against it."
|
2024-07-15 13:44:01 -04:00
|
|
|
|
|
|
|
require "utils/backtrace"
|
2023-09-11 21:54:27 -07:00
|
|
|
odebug e, Utils::Backtrace.clean(e)
|
2024-07-15 13:44:01 -04:00
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2011-08-23 23:30:52 +01:00
|
|
|
def clean
|
2014-03-13 10:11:00 -05:00
|
|
|
ohai "Cleaning" if verbose?
|
2020-08-02 14:32:31 +02:00
|
|
|
Cleaner.new(formula).clean
|
2025-01-12 16:56:48 +00:00
|
|
|
# Handle all possible exceptions when cleaning does not complete.
|
2017-10-07 00:31:28 +02:00
|
|
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
2011-08-23 23:30:52 +01:00
|
|
|
opoo "The cleaning step did not complete successfully"
|
2021-01-26 15:21:24 -05:00
|
|
|
puts "Still, the installation was successful, so we will link it into your prefix."
|
2024-07-15 13:44:01 -04:00
|
|
|
|
|
|
|
require "utils/backtrace"
|
2023-09-11 21:54:27 -07:00
|
|
|
odebug e, Utils::Backtrace.clean(e)
|
2024-07-15 13:44:01 -04:00
|
|
|
|
2014-10-27 13:16:18 +00:00
|
|
|
Homebrew.failed = true
|
2011-08-23 23:30:52 +01:00
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
|
2023-07-28 11:26:09 +01:00
|
|
|
sig { returns(Pathname) }
|
|
|
|
def post_install_formula_path
|
|
|
|
# Use the formula from the keg when any of the following is true:
|
|
|
|
# * We're installing from the JSON API
|
|
|
|
# * We're installing a local bottle file
|
|
|
|
# * The formula doesn't exist in the tap (or the tap isn't installed)
|
|
|
|
# * The formula in the tap has a different `pkg_version``.
|
|
|
|
#
|
|
|
|
# In all other cases, including if the formula from the keg is unreadable
|
|
|
|
# (third-party taps may `require` some of their own libraries) or if there
|
|
|
|
# is no formula present in the keg (as is the case with very old bottles),
|
|
|
|
# use the formula from the tap.
|
|
|
|
keg_formula_path = formula.opt_prefix/".brew/#{formula.name}.rb"
|
|
|
|
return keg_formula_path if formula.loaded_from_api?
|
|
|
|
return keg_formula_path if formula.local_bottle_path.present?
|
|
|
|
|
2025-02-16 22:20:37 -08:00
|
|
|
tap_formula_path = T.must(formula.specified_path)
|
2023-07-28 11:26:09 +01:00
|
|
|
return keg_formula_path unless tap_formula_path.exist?
|
|
|
|
|
|
|
|
begin
|
|
|
|
keg_formula = Formulary.factory(keg_formula_path)
|
|
|
|
tap_formula = Formulary.factory(tap_formula_path)
|
|
|
|
return keg_formula_path if keg_formula.pkg_version != tap_formula.pkg_version
|
|
|
|
|
|
|
|
tap_formula_path
|
|
|
|
rescue FormulaUnavailableError, FormulaUnreadableError
|
|
|
|
tap_formula_path
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2013-12-05 10:09:14 -06:00
|
|
|
def post_install
|
2022-05-30 04:19:15 +01:00
|
|
|
args = [
|
|
|
|
"nice",
|
|
|
|
*HOMEBREW_RUBY_EXEC_ARGS,
|
|
|
|
"-I", $LOAD_PATH.join(File::PATH_SEPARATOR),
|
|
|
|
"--",
|
|
|
|
HOMEBREW_LIBRARY_PATH/"postinstall.rb"
|
2019-12-28 14:48:34 +05:30
|
|
|
]
|
2018-05-12 13:52:40 +02:00
|
|
|
|
2023-07-28 11:26:09 +01:00
|
|
|
args << post_install_formula_path
|
2021-06-15 17:30:38 +05:30
|
|
|
|
2024-08-28 03:45:26 +01:00
|
|
|
if Sandbox.available?
|
|
|
|
sandbox = Sandbox.new
|
|
|
|
formula.logs.mkpath
|
|
|
|
sandbox.record_log(formula.logs/"postinstall.sandbox.log")
|
|
|
|
sandbox.allow_write_temp_and_cache
|
|
|
|
sandbox.allow_write_log(formula)
|
|
|
|
sandbox.allow_write_xcode
|
|
|
|
sandbox.deny_write_homebrew_repository
|
|
|
|
sandbox.allow_write_cellar(formula)
|
|
|
|
sandbox.deny_all_network unless formula.network_access_allowed?(:postinstall)
|
2024-10-05 13:46:24 -07:00
|
|
|
Keg.keg_link_directories.each do |dir|
|
2024-08-28 03:45:26 +01:00
|
|
|
sandbox.allow_write_path "#{HOMEBREW_PREFIX}/#{dir}"
|
|
|
|
end
|
|
|
|
sandbox.run(*args)
|
|
|
|
else
|
|
|
|
Utils.safe_fork do
|
2018-05-12 13:52:40 +02:00
|
|
|
exec(*args)
|
|
|
|
end
|
|
|
|
end
|
2025-01-12 16:56:48 +00:00
|
|
|
# Handle all possible exceptions when postinstall does not complete.
|
2017-10-07 00:31:28 +02:00
|
|
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
2013-12-05 10:09:14 -06:00
|
|
|
opoo "The post-install step did not complete successfully"
|
2021-01-24 21:25:12 -05:00
|
|
|
puts "You can try again using:"
|
|
|
|
puts " brew postinstall #{formula.full_name}"
|
2024-07-15 13:44:01 -04:00
|
|
|
|
|
|
|
require "utils/backtrace"
|
2023-09-11 21:54:27 -07:00
|
|
|
odebug e, Utils::Backtrace.clean(e), always_display: Homebrew::EnvConfig.developer?
|
2024-07-15 13:44:01 -04:00
|
|
|
|
2014-10-27 13:16:18 +00:00
|
|
|
Homebrew.failed = true
|
2013-12-05 10:09:14 -06:00
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2020-03-04 17:48:17 -05:00
|
|
|
def fetch_dependencies
|
2020-05-23 13:40:13 +01:00
|
|
|
return if ignore_deps?
|
2020-03-04 17:48:17 -05:00
|
|
|
|
2023-01-04 14:19:22 +00:00
|
|
|
# Don't output dependencies if we're explicitly installing them.
|
2023-12-14 02:52:30 +00:00
|
|
|
deps = compute_dependencies.reject do |(dep, _options)|
|
2023-01-02 14:10:17 -08:00
|
|
|
self.class.fetched.include?(dep.to_formula)
|
|
|
|
end
|
|
|
|
|
2020-05-23 13:40:13 +01:00
|
|
|
return if deps.empty?
|
2020-03-04 17:48:17 -05:00
|
|
|
|
2022-12-06 14:09:49 +00:00
|
|
|
oh1 "Fetching dependencies for #{formula.full_name}: " \
|
2024-03-03 18:55:56 -08:00
|
|
|
"#{deps.map(&:first).map { Formatter.identifier(_1) }.to_sentence}",
|
2022-12-06 14:09:49 +00:00
|
|
|
truncate: false
|
|
|
|
|
2023-12-14 02:52:30 +00:00
|
|
|
deps.each { |(dep, _options)| fetch_dependency(dep) }
|
2020-03-04 17:48:17 -05:00
|
|
|
end
|
|
|
|
|
2023-07-28 02:12:09 +01:00
|
|
|
sig { returns(T.nilable(Formula)) }
|
|
|
|
def previously_fetched_formula
|
2023-08-15 14:44:35 +01:00
|
|
|
# We intentionally don't compare classes here:
|
|
|
|
# from-API-JSON and from-source formula classes are not equal but we
|
|
|
|
# want to equate them to be the same thing here given mixing bottle and
|
|
|
|
# from-source installs of the same formula within the same operation
|
|
|
|
# doesn't make sense.
|
2023-07-28 02:12:09 +01:00
|
|
|
self.class.fetched.find do |fetched_formula|
|
|
|
|
fetched_formula.full_name == formula.full_name && fetched_formula.active_spec_sym == formula.active_spec_sym
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-09-02 21:46:07 -04:00
|
|
|
sig { void }
|
|
|
|
def fetch_bottle_tab
|
2024-09-09 09:50:18 -07:00
|
|
|
return if @fetch_bottle_tab
|
|
|
|
|
|
|
|
begin
|
2023-09-02 21:46:07 -04:00
|
|
|
formula.fetch_bottle_tab
|
2024-01-22 10:41:48 -08:00
|
|
|
@bottle_tab_runtime_dependencies = formula.bottle_tab_attributes
|
2024-03-26 11:36:47 -04:00
|
|
|
.fetch("runtime_dependencies", []).then { |deps| deps || [] }
|
2024-01-22 10:41:48 -08:00
|
|
|
.each_with_object({}) { |dep, h| h[dep["full_name"]] = dep }
|
|
|
|
.freeze
|
2024-09-19 15:26:14 +01:00
|
|
|
rescue DownloadError, Resource::BottleManifest::Error
|
2024-09-09 09:50:18 -07:00
|
|
|
# do nothing
|
2023-09-02 21:46:07 -04:00
|
|
|
end
|
2024-09-09 09:50:18 -07:00
|
|
|
@fetch_bottle_tab = T.let(true, T.nilable(TrueClass))
|
2023-09-02 21:46:07 -04:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2020-03-04 17:48:17 -05:00
|
|
|
def fetch
|
2023-07-28 02:12:09 +01:00
|
|
|
return if previously_fetched_formula
|
2021-10-31 10:52:40 -07:00
|
|
|
|
2020-03-04 17:48:17 -05:00
|
|
|
fetch_dependencies
|
|
|
|
|
|
|
|
return if only_deps?
|
2025-01-23 14:06:39 +00:00
|
|
|
return if formula.local_bottle_path.present?
|
2020-03-04 17:48:17 -05:00
|
|
|
|
2022-12-06 14:09:49 +00:00
|
|
|
oh1 "Fetching #{Formatter.identifier(formula.full_name)}".strip
|
|
|
|
|
2024-10-14 13:55:28 +08:00
|
|
|
downloadable_object = downloadable
|
|
|
|
check_attestation = if pour_bottle?(output_warning: true)
|
2023-09-02 21:46:07 -04:00
|
|
|
fetch_bottle_tab
|
2024-10-14 13:55:28 +08:00
|
|
|
|
|
|
|
!downloadable_object.cached_download.exist?
|
2021-03-31 20:55:35 +01:00
|
|
|
else
|
2023-05-15 13:58:33 +02:00
|
|
|
@formula = Homebrew::API::Formula.source_download(formula) if formula.loaded_from_api?
|
2023-02-17 04:25:04 +00:00
|
|
|
|
2021-02-17 13:51:53 +00:00
|
|
|
formula.fetch_patches
|
|
|
|
formula.resources.each(&:fetch)
|
2024-11-21 16:55:19 -05:00
|
|
|
downloadable_object = downloadable
|
2020-03-04 17:48:17 -05:00
|
|
|
|
2024-10-14 13:55:28 +08:00
|
|
|
false
|
2013-06-08 16:41:23 +01:00
|
|
|
end
|
2024-10-14 13:55:28 +08:00
|
|
|
downloadable_object.fetch
|
2019-06-15 17:22:45 +01:00
|
|
|
|
2024-06-21 12:35:01 -04:00
|
|
|
# We skip `gh` to avoid a bootstrapping cycle, in the off-chance a user attempts
|
|
|
|
# to explicitly `brew install gh` without already having a version for bootstrapping.
|
2024-07-13 16:02:12 -04:00
|
|
|
# We also skip bottle installs from local bottle paths, as these are done in CI
|
|
|
|
# as part of the build lifecycle before attestations are produced.
|
2024-10-14 13:55:28 +08:00
|
|
|
if check_attestation &&
|
|
|
|
Homebrew::Attestation.enabled? &&
|
2024-07-13 16:05:35 -04:00
|
|
|
formula.tap&.core_tap? &&
|
2025-01-23 14:06:39 +00:00
|
|
|
formula.name != "gh"
|
2024-04-08 16:18:15 -04:00
|
|
|
ohai "Verifying attestation for #{formula.name}"
|
2024-04-09 10:45:44 -04:00
|
|
|
begin
|
2024-10-14 13:55:28 +08:00
|
|
|
Homebrew::Attestation.check_core_attestation T.cast(downloadable_object, Bottle)
|
2024-10-10 12:06:09 +01:00
|
|
|
rescue Homebrew::Attestation::GhIncompatible
|
|
|
|
# A small but significant number of users have developer mode enabled
|
|
|
|
# but *also* haven't upgraded in a long time, meaning that their `gh`
|
|
|
|
# version is too old to perform attestations.
|
|
|
|
raise CannotInstallFormulaError, <<~EOS
|
|
|
|
The bottle for #{formula.name} could not be verified.
|
|
|
|
|
|
|
|
This typically indicates an outdated or incompatible `gh` CLI.
|
|
|
|
|
|
|
|
Please confirm that you're running the latest version of `gh`
|
|
|
|
by performing an upgrade before retrying:
|
|
|
|
|
|
|
|
brew update
|
|
|
|
brew upgrade gh
|
|
|
|
EOS
|
2024-07-18 16:11:25 +01:00
|
|
|
rescue Homebrew::Attestation::GhAuthInvalid
|
|
|
|
# Only raise an error if we explicitly opted-in to verification.
|
|
|
|
raise CannotInstallFormulaError, <<~EOS if Homebrew::EnvConfig.verify_attestations?
|
|
|
|
The bottle for #{formula.name} could not be verified.
|
|
|
|
|
|
|
|
This typically indicates an invalid GitHub API token.
|
|
|
|
|
|
|
|
If you have `HOMEBREW_GITHUB_API_TOKEN` set, check it is correct
|
|
|
|
or unset it and instead run:
|
|
|
|
|
|
|
|
gh auth login
|
|
|
|
EOS
|
|
|
|
|
|
|
|
# If we didn't explicitly opt-in, then quietly opt-out in the case of invalid credentials.
|
|
|
|
# Based on user reports, a significant number of users are running with stale tokens.
|
|
|
|
ENV["HOMEBREW_NO_VERIFY_ATTESTATIONS"] = "1"
|
2024-05-03 12:37:01 -04:00
|
|
|
rescue Homebrew::Attestation::GhAuthNeeded
|
|
|
|
raise CannotInstallFormulaError, <<~EOS
|
|
|
|
The bottle for #{formula.name} could not be verified.
|
|
|
|
|
|
|
|
This typically indicates a missing GitHub API token, which you
|
|
|
|
can resolve either by setting `HOMEBREW_GITHUB_API_TOKEN` or
|
|
|
|
by running:
|
|
|
|
|
|
|
|
gh auth login
|
|
|
|
EOS
|
2024-07-23 16:59:52 +01:00
|
|
|
rescue Homebrew::Attestation::MissingAttestationError, Homebrew::Attestation::InvalidAttestationError => e
|
2024-04-09 10:45:44 -04:00
|
|
|
raise CannotInstallFormulaError, <<~EOS
|
|
|
|
The bottle for #{formula.name} has an invalid build provenance attestation.
|
|
|
|
|
|
|
|
This may indicate that the bottle was not produced by the expected
|
|
|
|
tap, or was maliciously inserted into the expected tap's bottle
|
|
|
|
storage.
|
|
|
|
|
|
|
|
Additional context:
|
|
|
|
|
|
|
|
#{e}
|
|
|
|
EOS
|
|
|
|
end
|
2024-04-08 16:18:15 -04:00
|
|
|
end
|
|
|
|
|
2024-10-14 13:55:28 +08:00
|
|
|
self.class.fetched << formula
|
|
|
|
rescue CannotInstallFormulaError
|
|
|
|
if (cached_download = downloadable_object&.cached_download)&.exist?
|
|
|
|
cached_download.unlink
|
|
|
|
end
|
|
|
|
|
|
|
|
raise
|
|
|
|
end
|
|
|
|
|
|
|
|
sig { returns(Downloadable) }
|
|
|
|
def downloadable
|
|
|
|
if (bottle_path = formula.local_bottle_path)
|
|
|
|
Resource::Local.new(bottle_path)
|
|
|
|
elsif pour_bottle?
|
|
|
|
T.must(formula.bottle)
|
|
|
|
else
|
|
|
|
T.must(formula.resource)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
sig { void }
|
|
|
|
def pour
|
2011-08-24 01:13:15 +01:00
|
|
|
HOMEBREW_CELLAR.cd do
|
2024-09-04 23:12:58 +02:00
|
|
|
downloadable.downloader.stage
|
2011-08-24 01:13:15 +01:00
|
|
|
end
|
2013-10-05 20:32:05 +01:00
|
|
|
|
2016-10-25 03:41:35 -04:00
|
|
|
Tab.clear_cache
|
2016-10-09 19:43:55 -04:00
|
|
|
|
2021-05-11 03:08:59 +01:00
|
|
|
tab = Utils::Bottles.load_tab(formula)
|
2015-07-30 19:58:25 +08:00
|
|
|
|
2021-03-30 17:35:13 +01:00
|
|
|
# fill in missing/outdated parts of the tab
|
2024-03-26 11:36:47 -04:00
|
|
|
# keep in sync with Tab#to_bottle_hash
|
2021-03-30 17:35:13 +01:00
|
|
|
tab.used_options = []
|
|
|
|
tab.unused_options = []
|
|
|
|
tab.built_as_bottle = true
|
2014-10-17 22:40:03 -05:00
|
|
|
tab.poured_from_bottle = true
|
2023-05-15 13:58:33 +02:00
|
|
|
tab.loaded_from_api = formula.loaded_from_api?
|
2020-11-18 05:37:12 +01:00
|
|
|
tab.installed_as_dependency = installed_as_dependency?
|
|
|
|
tab.installed_on_request = installed_on_request?
|
2021-03-30 17:35:13 +01:00
|
|
|
tab.time = Time.now.to_i
|
2017-07-21 17:20:54 +01:00
|
|
|
tab.aliases = formula.aliases
|
2021-03-30 17:35:13 +01:00
|
|
|
tab.arch = Hardware::CPU.arch
|
2024-09-08 16:33:11 -07:00
|
|
|
tab.source["versions"]["stable"] = T.must(formula.stable).version&.to_s
|
2021-04-12 15:47:46 +01:00
|
|
|
tab.source["versions"]["version_scheme"] = formula.version_scheme
|
2021-03-30 17:35:13 +01:00
|
|
|
tab.source["path"] = formula.specified_path.to_s
|
2021-05-31 20:36:36 +05:30
|
|
|
tab.source["tap_git_head"] = formula.tap&.installed? ? formula.tap&.git_head : nil
|
2021-03-30 17:35:13 +01:00
|
|
|
tab.tap = formula.tap
|
2014-10-17 22:40:03 -05:00
|
|
|
tab.write
|
2021-05-11 03:08:59 +01:00
|
|
|
|
|
|
|
keg = Keg.new(formula.prefix)
|
|
|
|
skip_linkage = formula.bottle_specification.skip_relocation?
|
2024-03-07 16:20:20 +00:00
|
|
|
keg.replace_placeholders_with_locations(tab.changed_files, skip_linkage:)
|
2021-12-07 10:59:08 -08:00
|
|
|
|
|
|
|
cellar = formula.bottle_specification.tag_to_cellar(Utils::Bottles.tag)
|
|
|
|
return if [:any, :any_skip_relocation].include?(cellar)
|
|
|
|
|
|
|
|
prefix = Pathname(cellar).parent.to_s
|
|
|
|
return if cellar == HOMEBREW_CELLAR.to_s && prefix == HOMEBREW_PREFIX.to_s
|
|
|
|
|
2022-04-28 12:45:26 -07:00
|
|
|
return unless ENV["HOMEBREW_RELOCATE_BUILD_PREFIX"]
|
|
|
|
|
2021-12-07 10:59:08 -08:00
|
|
|
keg.relocate_build_prefix(keg, prefix, HOMEBREW_PREFIX)
|
2011-08-24 01:13:15 +01:00
|
|
|
end
|
|
|
|
|
2023-03-25 08:36:56 -07:00
|
|
|
sig { override.params(output: T.nilable(String)).void }
|
2017-04-18 08:17:24 +01:00
|
|
|
def problem_if_output(output)
|
2016-09-23 22:02:23 +02:00
|
|
|
return unless output
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-09-23 22:02:23 +02:00
|
|
|
opoo output
|
|
|
|
@show_summary_heading = true
|
2012-02-26 16:33:44 -08:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { void }
|
2014-10-13 23:13:01 -05:00
|
|
|
def audit_installed
|
2018-02-14 03:38:38 -08:00
|
|
|
unless formula.keg_only?
|
|
|
|
problem_if_output(check_env_path(formula.bin))
|
|
|
|
problem_if_output(check_env_path(formula.sbin))
|
|
|
|
end
|
2014-10-13 23:13:01 -05:00
|
|
|
super
|
2014-10-01 23:32:53 -05:00
|
|
|
end
|
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { returns(T::Array[Formula]) }
|
2017-10-07 00:31:28 +02:00
|
|
|
def self.locked
|
2024-09-08 16:33:11 -07:00
|
|
|
@locked ||= T.let([], T.nilable(T::Array[Formula]))
|
2017-10-07 00:31:28 +02:00
|
|
|
end
|
|
|
|
|
2024-04-08 16:38:32 +01:00
|
|
|
sig { void }
|
|
|
|
def forbidden_license_check
|
|
|
|
forbidden_licenses = Homebrew::EnvConfig.forbidden_licenses.to_s.dup
|
|
|
|
SPDX::ALLOWED_LICENSE_SYMBOLS.each do |s|
|
|
|
|
pattern = /#{s.to_s.tr("_", " ")}/i
|
|
|
|
forbidden_licenses.sub!(pattern, s.to_s)
|
|
|
|
end
|
2024-10-17 08:34:03 +01:00
|
|
|
|
|
|
|
invalid_licenses = []
|
|
|
|
forbidden_licenses = forbidden_licenses.split.each_with_object({}) do |license, hash|
|
|
|
|
unless SPDX.valid_license?(license)
|
|
|
|
invalid_licenses << license
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
hash[license] = SPDX.license_version_info(license)
|
|
|
|
end
|
|
|
|
|
|
|
|
if invalid_licenses.present?
|
|
|
|
opoo <<~EOS
|
|
|
|
HOMEBREW_FORBIDDEN_LICENSES contains invalid license identifiers: #{invalid_licenses.to_sentence}
|
|
|
|
These licenses will not be forbidden. See the valid SPDX license identifiers at:
|
|
|
|
#{Formatter.url("https://spdx.org/licenses/")}
|
|
|
|
And the licenses for a formula with:
|
|
|
|
brew info <formula>
|
|
|
|
EOS
|
2024-04-08 16:38:32 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
return if forbidden_licenses.blank?
|
|
|
|
|
|
|
|
owner = Homebrew::EnvConfig.forbidden_owner
|
|
|
|
owner_contact = if (contact = Homebrew::EnvConfig.forbidden_owner_contact.presence)
|
|
|
|
"\n#{contact}"
|
|
|
|
end
|
|
|
|
|
|
|
|
unless ignore_deps?
|
|
|
|
compute_dependencies.each do |(dep, _options)|
|
|
|
|
dep_f = dep.to_formula
|
|
|
|
next unless SPDX.licenses_forbid_installation? dep_f.license, forbidden_licenses
|
|
|
|
|
|
|
|
raise CannotInstallFormulaError, <<~EOS
|
|
|
|
The installation of #{formula.name} has a dependency on #{dep.name} where all
|
|
|
|
its licenses were forbidden by #{owner} in `HOMEBREW_FORBIDDEN_LICENSES`:
|
2024-10-17 08:34:03 +01:00
|
|
|
#{SPDX.license_expression_to_string dep_f.license}#{owner_contact}
|
2024-04-08 16:38:32 +01:00
|
|
|
EOS
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return if only_deps?
|
|
|
|
|
|
|
|
return unless SPDX.licenses_forbid_installation? formula.license, forbidden_licenses
|
|
|
|
|
|
|
|
raise CannotInstallFormulaError, <<~EOS
|
|
|
|
#{formula.name}'s licenses are all forbidden by #{owner} in `HOMEBREW_FORBIDDEN_LICENSES`:
|
2024-10-17 08:34:03 +01:00
|
|
|
#{SPDX.license_expression_to_string formula.license}#{owner_contact}
|
2024-04-08 16:38:32 +01:00
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
|
|
|
sig { void }
|
|
|
|
def forbidden_tap_check
|
2024-05-03 16:08:42 +01:00
|
|
|
return if Tap.allowed_taps.blank? && Tap.forbidden_taps.blank?
|
2024-04-08 16:38:32 +01:00
|
|
|
|
|
|
|
owner = Homebrew::EnvConfig.forbidden_owner
|
|
|
|
owner_contact = if (contact = Homebrew::EnvConfig.forbidden_owner_contact.presence)
|
|
|
|
"\n#{contact}"
|
|
|
|
end
|
|
|
|
|
|
|
|
unless ignore_deps?
|
|
|
|
compute_dependencies.each do |(dep, _options)|
|
|
|
|
dep_tap = dep.tap
|
2024-05-03 16:08:42 +01:00
|
|
|
next if dep_tap.blank? || (dep_tap.allowed_by_env? && !dep_tap.forbidden_by_env?)
|
2024-04-08 16:38:32 +01:00
|
|
|
|
2024-09-04 10:05:50 -07:00
|
|
|
error_message = "The installation of #{formula.name} has a dependency #{dep.name}\n" \
|
|
|
|
"from the #{dep_tap} tap but #{owner} "
|
2024-05-03 16:08:42 +01:00
|
|
|
error_message << "has not allowed this tap in `HOMEBREW_ALLOWED_TAPS`" unless dep_tap.allowed_by_env?
|
|
|
|
error_message << " and\n" if !dep_tap.allowed_by_env? && dep_tap.forbidden_by_env?
|
|
|
|
error_message << "has forbidden this tap in `HOMEBREW_FORBIDDEN_TAPS`" if dep_tap.forbidden_by_env?
|
|
|
|
error_message << ".#{owner_contact}"
|
|
|
|
|
|
|
|
raise CannotInstallFormulaError, error_message
|
2024-04-08 16:38:32 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return if only_deps?
|
|
|
|
|
|
|
|
formula_tap = formula.tap
|
2024-05-03 16:08:42 +01:00
|
|
|
return if formula_tap.blank? || (formula_tap.allowed_by_env? && !formula_tap.forbidden_by_env?)
|
2024-04-08 16:38:32 +01:00
|
|
|
|
2024-09-04 10:05:50 -07:00
|
|
|
error_message = "The installation of #{formula.full_name} has the tap #{formula_tap}\n" \
|
|
|
|
"but #{owner} "
|
2024-05-03 16:08:42 +01:00
|
|
|
error_message << "has not allowed this tap in `HOMEBREW_ALLOWED_TAPS`" unless formula_tap.allowed_by_env?
|
|
|
|
error_message << " and\n" if !formula_tap.allowed_by_env? && formula_tap.forbidden_by_env?
|
|
|
|
error_message << "has forbidden this tap in `HOMEBREW_FORBIDDEN_TAPS`" if formula_tap.forbidden_by_env?
|
|
|
|
error_message << ".#{owner_contact}"
|
|
|
|
|
|
|
|
raise CannotInstallFormulaError, error_message
|
2024-04-08 16:38:32 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
sig { void }
|
|
|
|
def forbidden_formula_check
|
|
|
|
forbidden_formulae = Set.new(Homebrew::EnvConfig.forbidden_formulae.to_s.split)
|
|
|
|
return if forbidden_formulae.blank?
|
|
|
|
|
|
|
|
owner = Homebrew::EnvConfig.forbidden_owner
|
|
|
|
owner_contact = if (contact = Homebrew::EnvConfig.forbidden_owner_contact.presence)
|
|
|
|
"\n#{contact}"
|
|
|
|
end
|
|
|
|
|
|
|
|
unless ignore_deps?
|
|
|
|
compute_dependencies.each do |(dep, _options)|
|
|
|
|
dep_name = if forbidden_formulae.include?(dep.name)
|
|
|
|
dep.name
|
|
|
|
elsif dep.tap.present? &&
|
|
|
|
(dep_full_name = "#{dep.tap}/#{dep.name}") &&
|
|
|
|
forbidden_formulae.include?(dep_full_name)
|
|
|
|
dep_full_name
|
|
|
|
else
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
raise CannotInstallFormulaError, <<~EOS
|
|
|
|
The installation of #{formula.name} has a dependency #{dep_name}
|
|
|
|
but the #{dep_name} formula was forbidden by #{owner} in `HOMEBREW_FORBIDDEN_FORMULAE`.#{owner_contact}
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return if only_deps?
|
|
|
|
|
|
|
|
formula_name = if forbidden_formulae.include?(formula.name)
|
|
|
|
formula.name
|
|
|
|
elsif forbidden_formulae.include?(formula.full_name)
|
|
|
|
formula.full_name
|
|
|
|
else
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
raise CannotInstallFormulaError, <<~EOS
|
|
|
|
The installation of #{formula_name} was forbidden by #{owner}
|
|
|
|
in `HOMEBREW_FORBIDDEN_FORMULAE`.#{owner_contact}
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
2013-01-23 00:26:25 -06:00
|
|
|
private
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2013-01-23 00:26:25 -06:00
|
|
|
def lock
|
2017-10-07 00:31:28 +02:00
|
|
|
return unless self.class.locked.empty?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-11-13 23:37:40 +01:00
|
|
|
unless ignore_deps?
|
|
|
|
formula.recursive_dependencies.each do |dep|
|
2017-10-07 00:31:28 +02:00
|
|
|
self.class.locked << dep.to_formula
|
2016-11-13 23:37:40 +01:00
|
|
|
end
|
|
|
|
end
|
2017-10-07 00:31:28 +02:00
|
|
|
self.class.locked.unshift(formula)
|
|
|
|
self.class.locked.uniq!
|
|
|
|
self.class.locked.each(&:lock)
|
2016-09-23 22:02:23 +02:00
|
|
|
@hold_locks = true
|
2013-01-23 00:26:25 -06:00
|
|
|
end
|
|
|
|
|
2020-11-18 05:37:12 +01:00
|
|
|
sig { void }
|
2013-01-23 00:26:25 -06:00
|
|
|
def unlock
|
2024-09-08 16:33:11 -07:00
|
|
|
return unless @hold_locks
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2017-10-07 00:31:28 +02:00
|
|
|
self.class.locked.each(&:unlock)
|
|
|
|
self.class.locked.clear
|
2016-09-23 22:02:23 +02:00
|
|
|
@hold_locks = false
|
2013-01-23 00:26:25 -06:00
|
|
|
end
|
2016-11-20 19:45:33 +09:00
|
|
|
|
2024-09-08 16:33:11 -07:00
|
|
|
sig { void }
|
2016-11-20 19:45:33 +09:00
|
|
|
def puts_requirement_messages
|
|
|
|
return if @requirement_messages.empty?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2017-04-23 15:10:18 +01:00
|
|
|
$stderr.puts @requirement_messages
|
2016-11-20 19:45:33 +09:00
|
|
|
end
|
2011-08-23 23:30:52 +01:00
|
|
|
end
|
2024-09-06 13:58:32 -07:00
|
|
|
|
|
|
|
require "extend/os/formula_installer"
|