Merge pull request #9182 from reitermarkus/formula-installer

Refactor `FormulaInstaller`.
This commit is contained in:
Markus Reiter 2020-11-19 20:18:57 +01:00 committed by GitHub
commit 6d850a97a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 197 additions and 123 deletions

View File

@ -371,10 +371,15 @@ module Cask
force: false, force: false,
).install ).install
else else
FormulaInstaller.new(cask_or_formula, verbose: verbose?).yield_self do |fi| fi = FormulaInstaller.new(
fi.installed_as_dependency = true cask_or_formula,
fi.installed_on_request = false **{
fi.show_header = true show_header: true,
installed_as_dependency: true,
installed_on_request: false,
verbose: verbose?,
}.compact,
)
fi.prelude fi.prelude
fi.fetch fi.fetch
fi.install fi.install
@ -382,7 +387,6 @@ module Cask
end end
end end
end end
end
def caveats def caveats
self.class.caveats(@cask) self.class.caveats(@cask)

View File

@ -357,21 +357,28 @@ module Homebrew
f.print_tap_action f.print_tap_action
build_options = f.build build_options = f.build
fi = FormulaInstaller.new(f, force_bottle: args.force_bottle?, fi = FormulaInstaller.new(
f,
**{
options: build_options.used_options,
build_bottle: args.build_bottle?,
force_bottle: args.force_bottle?,
bottle_arch: args.bottle_arch,
ignore_deps: args.ignore_dependencies?,
only_deps: args.only_dependencies?,
include_test_formulae: args.include_test_formulae, include_test_formulae: args.include_test_formulae,
build_from_source_formulae: args.build_from_source_formulae, build_from_source_formulae: args.build_from_source_formulae,
debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?) env: args.env,
fi.options = build_options.used_options cc: args.cc,
fi.env = args.env git: args.git?,
fi.force = args.force? interactive: args.interactive?,
fi.keep_tmp = args.keep_tmp? keep_tmp: args.keep_tmp?,
fi.ignore_deps = args.ignore_dependencies? force: args.force?,
fi.only_deps = args.only_dependencies? debug: args.debug?,
fi.build_bottle = args.build_bottle? quiet: args.quiet?,
fi.bottle_arch = args.bottle_arch verbose: args.verbose?,
fi.interactive = args.interactive? }.compact,
fi.git = args.git? )
fi.cc = args.cc
fi.prelude fi.prelude
fi.fetch fi.fetch
fi.install fi.install

View File

@ -28,7 +28,7 @@ module Homebrew
args.named.to_resolved_formulae.each do |f| args.named.to_resolved_formulae.each do |f|
ohai "Postinstalling #{f}" ohai "Postinstalling #{f}"
fi = FormulaInstaller.new(f, debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?) fi = FormulaInstaller.new(f, **{ debug: args.debug?, quiet: args.quiet?, verbose: args.verbose? }.compact)
fi.post_install fi.post_install
end end
end end

View File

@ -34,47 +34,64 @@ class FormulaInstaller
extend Predicable extend Predicable
attr_reader :formula attr_reader :formula
attr_accessor :cc, :env, :options, :build_bottle, :bottle_arch,
:build_from_source_formulae, :include_test_formulae,
:installed_as_dependency, :installed_on_request, :link_keg, :other_installers
attr_accessor :options, :link_keg
attr_predicate :installed_as_dependency?, :installed_on_request?
attr_predicate :show_summary_heading?, :show_header? attr_predicate :show_summary_heading?, :show_header?
attr_writer :show_header
attr_predicate :force_bottle?, :ignore_deps?, :only_deps?, :interactive?, :git?, :force?, :keep_tmp? attr_predicate :force_bottle?, :ignore_deps?, :only_deps?, :interactive?, :git?, :force?, :keep_tmp?
attr_writer :force_bottle, :ignore_deps, :only_deps, :interactive, :git, :force, :keep_tmp
attr_predicate :verbose?, :debug?, :quiet? attr_predicate :verbose?, :debug?, :quiet?
def initialize(formula, # TODO: Remove when removed from `test-bot`.
attr_writer :build_bottle
def initialize(
formula,
link_keg: false,
installed_as_dependency: false,
installed_on_request: true,
show_header: false,
build_bottle: false,
force_bottle: false, force_bottle: false,
bottle_arch: nil,
ignore_deps: false,
only_deps: false,
include_test_formulae: [], include_test_formulae: [],
build_from_source_formulae: [], build_from_source_formulae: [],
env: nil,
git: false,
interactive: false,
keep_tmp: false,
cc: nil, cc: nil,
debug: false, quiet: false, verbose: false) options: Options.new,
force: false,
debug: false,
quiet: false,
verbose: false
)
@formula = formula @formula = formula
@env = nil @env = env
@force = false @force = force
@keep_tmp = false @keep_tmp = keep_tmp
@link_keg = !formula.keg_only? @link_keg = !formula.keg_only? || link_keg
@show_header = false @show_header = show_header
@ignore_deps = false @ignore_deps = ignore_deps
@only_deps = false @only_deps = only_deps
@build_from_source_formulae = build_from_source_formulae @build_from_source_formulae = build_from_source_formulae
@build_bottle = false @build_bottle = build_bottle
@bottle_arch = nil @bottle_arch = bottle_arch
@formula.force_bottle ||= force_bottle @formula.force_bottle ||= force_bottle
@force_bottle = @formula.force_bottle @force_bottle = @formula.force_bottle
@include_test_formulae = include_test_formulae @include_test_formulae = include_test_formulae
@interactive = false @interactive = interactive
@git = false @git = git
@cc = cc @cc = cc
@verbose = verbose @verbose = verbose
@quiet = quiet @quiet = quiet
@debug = debug @debug = debug
@installed_as_dependency = false @installed_as_dependency = installed_as_dependency
@installed_on_request = true @installed_on_request = installed_on_request
@options = Options.new @options = options
@requirement_messages = [] @requirement_messages = []
@poured_bottle = false @poured_bottle = false
@pour_failed = false @pour_failed = false
@ -85,6 +102,7 @@ class FormulaInstaller
@attempted ||= Set.new @attempted ||= Set.new
end end
sig { void }
def self.clear_attempted def self.clear_attempted
@attempted = Set.new @attempted = Set.new
end end
@ -93,6 +111,7 @@ class FormulaInstaller
@installed ||= Set.new @installed ||= Set.new
end end
sig { void }
def self.clear_installed def self.clear_installed
@installed = Set.new @installed = Set.new
end end
@ -116,14 +135,17 @@ class FormulaInstaller
raise BuildFlagsError.new(build_flags, bottled: all_bottled) raise BuildFlagsError.new(build_flags, bottled: all_bottled)
end end
sig { returns(T::Boolean) }
def build_from_source? def build_from_source?
build_from_source_formulae.include?(formula.full_name) @build_from_source_formulae.include?(formula.full_name)
end end
sig { returns(T::Boolean) }
def include_test? def include_test?
include_test_formulae.include?(formula.full_name) @include_test_formulae.include?(formula.full_name)
end end
sig { returns(T::Boolean) }
def build_bottle? def build_bottle?
return false unless @build_bottle return false unless @build_bottle
@ -137,7 +159,7 @@ class FormulaInstaller
return false if !formula.bottled? && !formula.local_bottle_path return false if !formula.bottled? && !formula.local_bottle_path
return true if force_bottle? return true if force_bottle?
return false if build_from_source? || build_bottle? || interactive? return false if build_from_source? || build_bottle? || interactive?
return false if cc return false if @cc
return false unless options.empty? return false unless options.empty?
return false if formula.bottle_disabled? return false if formula.bottle_disabled?
@ -168,7 +190,7 @@ class FormulaInstaller
sig { params(dep: Formula, build: BuildOptions).returns(T::Boolean) } sig { params(dep: Formula, build: BuildOptions).returns(T::Boolean) }
def install_bottle_for?(dep, build) def install_bottle_for?(dep, build)
return pour_bottle? if dep == formula return pour_bottle? if dep == formula
return false if build_from_source_formulae.include?(dep.full_name) return false if @build_from_source_formulae.include?(dep.full_name)
return false unless dep.bottle && dep.pour_bottle? return false unless dep.bottle && dep.pour_bottle?
return false unless build.used_options.empty? return false unless build.used_options.empty?
return false unless dep.bottle&.compatible_cellar? return false unless dep.bottle&.compatible_cellar?
@ -290,6 +312,7 @@ class FormulaInstaller
end end
end end
sig { void }
def install def install
lock lock
@ -341,7 +364,7 @@ class FormulaInstaller
return if only_deps? return if only_deps?
if build_bottle? && (arch = bottle_arch) && !Hardware::CPU.optimization_flags.include?(arch.to_sym) if build_bottle? && (arch = @bottle_arch) && !Hardware::CPU.optimization_flags.include?(arch.to_sym)
raise CannotInstallFormulaError, "Unrecognized architecture for --bottle-arch: #{arch}" raise CannotInstallFormulaError, "Unrecognized architecture for --bottle-arch: #{arch}"
end end
@ -358,7 +381,7 @@ class FormulaInstaller
action = "#{formula.full_name} #{options}".strip action = "#{formula.full_name} #{options}".strip
Utils::Analytics.report_event("install", action) Utils::Analytics.report_event("install", action)
Utils::Analytics.report_event("install_on_request", action) if installed_on_request Utils::Analytics.report_event("install_on_request", action) if installed_on_request?
end end
self.class.attempted << formula self.class.attempted << formula
@ -416,8 +439,8 @@ class FormulaInstaller
keg = Keg.new(formula.prefix) keg = Keg.new(formula.prefix)
tab = Tab.for_keg(keg) tab = Tab.for_keg(keg)
tab.installed_as_dependency = installed_as_dependency tab.installed_as_dependency = installed_as_dependency?
tab.installed_on_request = installed_on_request tab.installed_on_request = installed_on_request?
tab.write tab.write
end end
@ -530,7 +553,7 @@ class FormulaInstaller
keep_build_test ||= req.build? && !install_bottle_for_dependent && !dependent.latest_version_installed? keep_build_test ||= req.build? && !install_bottle_for_dependent && !dependent.latest_version_installed?
if req.prune_from_option?(build) || if req.prune_from_option?(build) ||
req.satisfied?(env: env, cc: cc, build_bottle: @build_bottle, bottle_arch: bottle_arch) || req.satisfied?(env: @env, cc: @cc, build_bottle: @build_bottle, bottle_arch: @bottle_arch) ||
((req.build? || req.test?) && !keep_build_test) || ((req.build? || req.test?) && !keep_build_test) ||
formula_deps_map[dependent.name]&.build? formula_deps_map[dependent.name]&.build?
Requirement.prune Requirement.prune
@ -558,7 +581,7 @@ class FormulaInstaller
) )
keep_build_test = false keep_build_test = false
keep_build_test ||= dep.test? && include_test? && include_test_formulae.include?(dependent.full_name) keep_build_test ||= dep.test? && include_test? && @include_test_formulae.include?(dependent.full_name)
keep_build_test ||= dep.build? && !install_bottle_for?(dependent, build) && !dependent.latest_version_installed? keep_build_test ||= dep.build? && !install_bottle_for?(dependent, build) && !dependent.latest_version_installed?
if dep.prune_from_option?(build) || ((dep.build? || dep.test?) && !keep_build_test) if dep.prune_from_option?(build) || ((dep.build? || dep.test?) && !keep_build_test)
@ -607,6 +630,7 @@ class FormulaInstaller
options options
end end
sig { params(dep: Dependency).returns(Options) }
def inherited_options_for(dep) def inherited_options_for(dep)
inherited_options = Options.new inherited_options = Options.new
u = Option.new("universal") u = Option.new("universal")
@ -616,6 +640,7 @@ class FormulaInstaller
inherited_options inherited_options
end end
sig { params(deps: T::Array[[Formula, Options]]).void }
def install_dependencies(deps) def install_dependencies(deps)
if deps.empty? && only_deps? if deps.empty? && only_deps?
puts "All dependencies for #{formula.full_name} are satisfied." puts "All dependencies for #{formula.full_name} are satisfied."
@ -629,22 +654,28 @@ class FormulaInstaller
@show_header = true unless deps.empty? @show_header = true unless deps.empty?
end end
sig { params(dep: Formula).void }
def fetch_dependency(dep) def fetch_dependency(dep)
df = dep.to_formula df = dep.to_formula
fi = FormulaInstaller.new(df, force_bottle: false, fi = FormulaInstaller.new(
include_test_formulae: include_test_formulae, df,
build_from_source_formulae: build_from_source_formulae, force_bottle: false,
debug: debug?, quiet: quiet?, verbose: verbose?)
fi.force = force?
fi.keep_tmp = keep_tmp?
# When fetching we don't need to recurse the dependency tree as it's already # 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 # been done for us in `compute_dependencies` and there's no requirement to
# fetch in a particular order. # fetch in a particular order.
fi.ignore_deps = true ignore_deps: true,
include_test_formulae: @include_test_formulae,
build_from_source_formulae: @build_from_source_formulae,
keep_tmp: keep_tmp?,
force: force?,
debug: debug?,
quiet: quiet?,
verbose: verbose?,
)
fi.fetch fi.fetch
end end
sig { params(dep: Formula, inherited_options: Options).void }
def install_dependency(dep, inherited_options) def install_dependency(dep, inherited_options)
df = dep.to_formula df = dep.to_formula
tab = Tab.for_formula(df) tab = Tab.for_formula(df)
@ -670,20 +701,29 @@ class FormulaInstaller
EOS EOS
end end
fi = FormulaInstaller.new(df, force_bottle: false, options = Options.new
include_test_formulae: include_test_formulae, options |= tab.used_options
build_from_source_formulae: build_from_source_formulae, options |= Tab.remap_deprecated_options(df.deprecated_options, dep.options)
debug: debug?, quiet: quiet?, verbose: verbose?) options |= inherited_options
options &= df.options
fi.options |= tab.used_options fi = FormulaInstaller.new(
fi.options |= Tab.remap_deprecated_options(df.deprecated_options, dep.options) df,
fi.options |= inherited_options **{
fi.options &= df.options options: options,
fi.force = force? link_keg: keg_had_linked_keg ? keg_was_linked : nil,
fi.keep_tmp = keep_tmp? installed_as_dependency: true,
fi.link_keg ||= keg_was_linked if keg_had_linked_keg installed_on_request: df.any_version_installed? && tab.installed_on_request,
fi.installed_as_dependency = true force_bottle: false,
fi.installed_on_request = df.any_version_installed? && tab.installed_on_request include_test_formulae: @include_test_formulae,
build_from_source_formulae: @build_from_source_formulae,
keep_tmp: keep_tmp?,
force: force?,
debug: debug?,
quiet: quiet?,
verbose: verbose?,
},
)
fi.prelude fi.prelude
oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}" oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}"
fi.install fi.install
@ -702,6 +742,7 @@ class FormulaInstaller
ignore_interrupts { tmp_keg.rmtree if tmp_keg&.directory? } ignore_interrupts { tmp_keg.rmtree if tmp_keg&.directory? }
end end
sig { void }
def caveats def caveats
return if only_deps? return if only_deps?
@ -716,6 +757,7 @@ class FormulaInstaller
Homebrew.messages.record_caveats(formula, caveats) Homebrew.messages.record_caveats(formula, caveats)
end end
sig { void }
def finish def finish
return if only_deps? return if only_deps?
@ -781,24 +823,25 @@ class FormulaInstaller
@build_time ||= Time.now - @start_time if @start_time && !interactive? @build_time ||= Time.now - @start_time if @start_time && !interactive?
end end
sig { returns(T::Array[String]) }
def sanitized_argv_options def sanitized_argv_options
args = [] args = []
args << "--ignore-dependencies" if ignore_deps? args << "--ignore-dependencies" if ignore_deps?
if build_bottle? if build_bottle?
args << "--build-bottle" args << "--build-bottle"
args << "--bottle-arch=#{bottle_arch}" if bottle_arch args << "--bottle-arch=#{@bottle_arch}" if @bottle_arch
end end
args << "--git" if git? args << "--git" if git?
args << "--interactive" if interactive? args << "--interactive" if interactive?
args << "--verbose" if verbose? args << "--verbose" if verbose?
args << "--debug" if debug? args << "--debug" if debug?
args << "--cc=#{cc}" if cc args << "--cc=#{@cc}" if @cc
args << "--keep-tmp" if keep_tmp? args << "--keep-tmp" if keep_tmp?
if env.present? if @env.present?
args << "--env=#{env}" args << "--env=#{@env}"
elsif formula.env.std? || formula.deps.select(&:build?).any? { |d| d.name == "scons" } elsif formula.env.std? || formula.deps.select(&:build?).any? { |d| d.name == "scons" }
args << "--env=std" args << "--env=std"
end end
@ -808,10 +851,12 @@ class FormulaInstaller
args args
end end
sig { returns(T::Array[String]) }
def build_argv def build_argv
sanitized_argv_options + options.as_flags sanitized_argv_options + options.as_flags
end end
sig { void }
def build def build
FileUtils.rm_rf(formula.logs) FileUtils.rm_rf(formula.logs)
@ -865,6 +910,7 @@ class FormulaInstaller
raise e raise e
end end
sig { params(keg: Keg).void }
def link(keg) def link(keg)
Formula.clear_cache Formula.clear_cache
@ -955,6 +1001,7 @@ class FormulaInstaller
@show_summary_heading = true @show_summary_heading = true
end end
sig { void }
def install_plist def install_plist
return unless formula.plist return unless formula.plist
@ -968,6 +1015,7 @@ class FormulaInstaller
Homebrew.failed = true Homebrew.failed = true
end end
sig { params(keg: Keg).void }
def fix_dynamic_linkage(keg) def fix_dynamic_linkage(keg)
keg.fix_dynamic_linkage keg.fix_dynamic_linkage
rescue Exception => e # rubocop:disable Lint/RescueException rescue Exception => e # rubocop:disable Lint/RescueException
@ -979,6 +1027,7 @@ class FormulaInstaller
@show_summary_heading = true @show_summary_heading = true
end end
sig { void }
def clean def clean
ohai "Cleaning" if verbose? ohai "Cleaning" if verbose?
Cleaner.new(formula).clean Cleaner.new(formula).clean
@ -990,6 +1039,7 @@ class FormulaInstaller
@show_summary_heading = true @show_summary_heading = true
end end
sig { void }
def post_install def post_install
args = %W[ args = %W[
nice #{RUBY_PATH} nice #{RUBY_PATH}
@ -1026,6 +1076,7 @@ class FormulaInstaller
@show_summary_heading = true @show_summary_heading = true
end end
sig { void }
def fetch_dependencies def fetch_dependencies
return if ignore_deps? return if ignore_deps?
@ -1035,6 +1086,7 @@ class FormulaInstaller
deps.each { |dep, _options| fetch_dependency(dep) } deps.each { |dep, _options| fetch_dependency(dep) }
end end
sig { void }
def fetch def fetch
fetch_dependencies fetch_dependencies
@ -1072,6 +1124,7 @@ class FormulaInstaller
end end
end end
sig { void }
def pour def pour
HOMEBREW_CELLAR.cd do HOMEBREW_CELLAR.cd do
downloader.stage downloader.stage
@ -1096,12 +1149,13 @@ class FormulaInstaller
tab.time = Time.now.to_i tab.time = Time.now.to_i
tab.head = HOMEBREW_REPOSITORY.git_head tab.head = HOMEBREW_REPOSITORY.git_head
tab.source["path"] = formula.specified_path.to_s tab.source["path"] = formula.specified_path.to_s
tab.installed_as_dependency = installed_as_dependency tab.installed_as_dependency = installed_as_dependency?
tab.installed_on_request = installed_on_request tab.installed_on_request = installed_on_request?
tab.aliases = formula.aliases tab.aliases = formula.aliases
tab.write tab.write
end end
sig { params(output: T.nilable(String)).void }
def problem_if_output(output) def problem_if_output(output)
return unless output return unless output
@ -1125,6 +1179,7 @@ class FormulaInstaller
attr_predicate :hold_locks? attr_predicate :hold_locks?
sig { void }
def lock def lock
return unless self.class.locked.empty? return unless self.class.locked.empty?
@ -1139,6 +1194,7 @@ class FormulaInstaller
@hold_locks = true @hold_locks = true
end end
sig { void }
def unlock def unlock
return unless hold_locks? return unless hold_locks?
@ -1154,6 +1210,7 @@ class FormulaInstaller
$stderr.puts @requirement_messages $stderr.puts @requirement_messages
end end
sig { void }
def forbidden_license_check def forbidden_license_check
forbidden_licenses = Homebrew::EnvConfig.forbidden_licenses.to_s.dup forbidden_licenses = Homebrew::EnvConfig.forbidden_licenses.to_s.dup
SPDX::ALLOWED_LICENSE_SYMBOLS.each do |s| SPDX::ALLOWED_LICENSE_SYMBOLS.each do |s|

View File

@ -27,21 +27,25 @@ module Homebrew
build_from_source_formulae = args.build_from_source_formulae build_from_source_formulae = args.build_from_source_formulae
build_from_source_formulae << f.full_name if build_from_source build_from_source_formulae << f.full_name if build_from_source
fi = FormulaInstaller.new(f, force_bottle: args.force_bottle?, fi = FormulaInstaller.new(
f,
**{
options: options,
link_keg: keg_had_linked_opt ? keg_was_linked : nil,
installed_as_dependency: tab&.installed_as_dependency,
installed_on_request: tab&.installed_on_request,
build_bottle: args.build_bottle? || tab&.built_bottle?,
force_bottle: args.force_bottle?,
build_from_source_formulae: build_from_source_formulae, build_from_source_formulae: build_from_source_formulae,
debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?) git: args.git?,
fi.options = options interactive: args.interactive?,
fi.force = args.force? keep_tmp: args.keep_tmp?,
fi.keep_tmp = args.keep_tmp? force: args.force?,
fi.build_bottle = args.build_bottle? debug: args.debug?,
fi.interactive = args.interactive? quiet: args.quiet?,
fi.git = args.git? verbose: args.verbose?,
fi.link_keg ||= keg_was_linked if keg_had_linked_opt }.compact,
if tab )
fi.build_bottle ||= tab.built_bottle?
fi.installed_as_dependency = tab.installed_as_dependency
fi.installed_on_request = tab.installed_on_request
end
fi.prelude fi.prelude
fi.fetch fi.fetch

View File

@ -178,8 +178,7 @@ RSpec.shared_context "integration test" do
def install_test_formula(name, content = nil, build_bottle: false) def install_test_formula(name, content = nil, build_bottle: false)
setup_test_formula(name, content) setup_test_formula(name, content)
fi = FormulaInstaller.new(Formula[name]) fi = FormulaInstaller.new(Formula[name], build_bottle: build_bottle)
fi.build_bottle = build_bottle
fi.prelude fi.prelude
fi.fetch fi.fetch
fi.install fi.install

View File

@ -67,20 +67,23 @@ module Homebrew
options |= f.build.used_options options |= f.build.used_options
options &= f.options options &= f.options
fi = FormulaInstaller.new(f, force_bottle: args.force_bottle?, fi = FormulaInstaller.new(
f,
**{
options: options,
link_keg: keg_had_linked_opt ? keg_was_linked : nil,
installed_as_dependency: tab&.installed_as_dependency,
installed_on_request: args.named.present? || tab&.installed_on_request,
build_bottle: args.build_bottle? || tab&.built_bottle?,
force_bottle: args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae, build_from_source_formulae: args.build_from_source_formulae,
debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?) keep_tmp: args.keep_tmp?,
fi.options = options force: args.force?,
fi.force = args.force? debug: args.debug?,
fi.keep_tmp = args.keep_tmp? quiet: args.quiet?,
fi.build_bottle = args.build_bottle? verbose: args.verbose?,
fi.installed_on_request = args.named.present? }.compact,
fi.link_keg ||= keg_was_linked if keg_had_linked_opt )
if tab
fi.build_bottle ||= tab.built_bottle?
fi.installed_as_dependency = tab.installed_as_dependency
fi.installed_on_request ||= tab.installed_on_request
end
upgrade_version = if f.optlinked? upgrade_version = if f.optlinked?
"#{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}" "#{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"