brew/Library/Homebrew/formula_installer.rb

632 lines
17 KiB
Ruby
Raw Normal View History

2013-03-02 17:42:21 -06:00
# encoding: UTF-8
2013-02-19 14:27:17 -06:00
require 'cxxstdlib'
require 'exceptions'
require 'formula'
require 'keg'
require 'tab'
require 'bottles'
require 'caveats'
require 'cleaner'
require 'formula_cellar_checks'
require 'install_renamed'
require 'cmd/tap'
class FormulaInstaller
include FormulaCellarChecks
attr_reader :f
attr_accessor :options, :ignore_deps, :only_deps
attr_accessor :show_summary_heading, :show_header
def initialize ff
@f = ff
@show_header = false
@ignore_deps = false
@only_deps = false
@options = Options.new
@@attempted ||= Set.new
2013-12-12 15:42:31 -06:00
@poured_bottle = false
@pour_failed = false
end
def pour_bottle? install_bottle_options={:warn=>false}
return false if @pour_failed
options.empty? && install_bottle?(f, install_bottle_options)
end
def prelude
verify_deps_exist unless ignore_deps
lock
check_install_sanity
end
def verify_deps_exist
f.recursive_dependencies.map(&:to_formula)
rescue TapFormulaUnavailableError => e
Homebrew.install_tap(e.user, e.repo)
retry
rescue FormulaUnavailableError => e
e.dependent = f.name
raise
end
def check_install_sanity
raise FormulaInstallationAlreadyAttemptedError, f if @@attempted.include? f
if f.installed?
msg = "#{f}-#{f.installed_version} already installed"
2013-09-07 11:08:51 -05:00
msg << ", it's just not linked" unless f.linked_keg.symlink? or f.keg_only?
raise FormulaAlreadyInstalledError, msg
end
# Building head-only without --HEAD is an error
2012-04-05 21:09:24 -05:00
if not ARGV.build_head? and f.stable.nil?
raise CannotInstallFormulaError, <<-EOS.undent
#{f} is a head-only formula
Install with `brew install --HEAD #{f.name}
EOS
end
# Building stable-only with --HEAD is an error
2012-04-05 21:09:24 -05:00
if ARGV.build_head? and f.head.nil?
raise CannotInstallFormulaError, "No head is defined for #{f.name}"
end
unless ignore_deps
unlinked_deps = f.recursive_dependencies.map(&:to_formula).select do |dep|
dep.installed? and not dep.keg_only? and not dep.linked_keg.directory?
end
raise CannotInstallFormulaError,
"You must `brew link #{unlinked_deps*' '}' before #{f} can be installed" unless unlinked_deps.empty?
end
end
def build_bottle_preinstall
@etc_var_glob ||= "#{HOMEBREW_PREFIX}/{etc,var}/**/*"
@etc_var_preinstall = Dir[@etc_var_glob]
end
def build_bottle_postinstall
@etc_var_postinstall = Dir[@etc_var_glob]
(@etc_var_postinstall - @etc_var_preinstall).each do |file|
Pathname.new(file).cp_path_sub(HOMEBREW_PREFIX, f.bottle_prefix)
end
end
def install
# not in initialize so upgrade can unlink the active keg before calling this
# function but after instantiating this class so that it can avoid having to
# relink the active keg if possible (because it is slow).
if f.linked_keg.directory?
# some other version is already installed *and* linked
raise CannotInstallFormulaError, <<-EOS.undent
#{f}-#{f.linked_keg.realpath.basename} already installed
To install this version, first `brew unlink #{f}'
EOS
end
check_conflicts
compute_and_install_dependencies unless ignore_deps
return if only_deps
if ARGV.build_bottle? && (arch = ARGV.bottle_arch) && !Hardware::CPU.optimization_flags.include?(arch)
raise "Unrecognized architecture for --bottle-arch: #{arch}"
end
oh1 "Installing #{Tty.green}#{f}#{Tty.reset}" if show_header
@@attempted << f
begin
if pour_bottle? :warn => true
pour
@poured_bottle = true
stdlibs = Keg.new(f.prefix).detect_cxx_stdlibs
stdlib_in_use = CxxStdlib.new(stdlibs.first, MacOS.default_compiler)
begin
stdlib_in_use.check_dependencies(f, f.recursive_dependencies)
rescue IncompatibleCxxStdlibs => e
opoo e.message
end
stdlibs = Keg.new(f.prefix).detect_cxx_stdlibs :skip_executables => true
tab = Tab.for_keg f.prefix
tab.poured_from_bottle = true
tab.tabfile.delete if tab.tabfile
tab.write
end
rescue => e
raise e if ARGV.homebrew_developer?
@pour_failed = true
onoe e.message
opoo "Bottle installation failed: building from source."
end
build_bottle_preinstall if ARGV.build_bottle?
unless @poured_bottle
compute_and_install_dependencies if @pour_failed and not ignore_deps
build
clean
end
build_bottle_postinstall if ARGV.build_bottle?
opoo "Nothing was installed to #{f.prefix}" unless f.installed?
end
# HACK: If readline is present in the dependency tree, it will clash
# with the stdlib's Readline module when the debugger is loaded
def perform_readline_hack
if f.recursive_dependencies.any? { |d| d.name == "readline" } && ARGV.debug?
ENV['HOMEBREW_NO_READLINE'] = '1'
end
end
def check_conflicts
return if ARGV.force?
conflicts = f.conflicts.reject do |c|
keg = Formula.factory(c.name).prefix
not keg.directory? && Keg.new(keg).linked?
end
raise FormulaConflictError.new(f, conflicts) unless conflicts.empty?
end
def compute_and_install_dependencies
perform_readline_hack
req_map, req_deps = expand_requirements
check_requirements(req_map)
deps = [].concat(req_deps).concat(f.deps)
deps = expand_dependencies(deps)
if deps.empty? and only_deps
puts "All dependencies for #{f} are satisfied."
else
install_dependencies(deps)
end
end
def check_requirements(req_map)
fatals = []
req_map.each_pair do |dependent, reqs|
reqs.each do |req|
puts "#{dependent}: #{req.message}"
fatals << req if req.fatal?
end
end
raise UnsatisfiedRequirements.new(f, fatals) unless fatals.empty?
end
def expand_requirements
unsatisfied_reqs = Hash.new { |h, k| h[k] = [] }
deps = []
formulae = [f]
while f = formulae.pop
ARGV.filter_for_dependencies do
f.recursive_requirements do |dependent, req|
2014-03-02 14:02:18 -06:00
build = effective_build_options_for(dependent)
if (req.optional? || req.recommended?) && build.without?(req)
Requirement.prune
elsif req.build? && install_bottle?(dependent)
Requirement.prune
elsif req.satisfied?
Requirement.prune
elsif req.default_formula?
dep = req.to_dependency
deps.unshift(dep)
formulae.unshift(dep.to_formula)
Requirement.prune
else
unsatisfied_reqs[dependent] << req
end
end
end
end
return unsatisfied_reqs, deps
end
def expand_dependencies(deps)
# FIXME: can't check this inside the block for the top-level dependent
# because it depends on the contents of ARGV.
pour_bottle = pour_bottle?
inherited_options = {}
expanded_deps = ARGV.filter_for_dependencies do
Dependency.expand(f, deps) do |dependent, dep|
options = inherited_options[dep] = inherited_options_for(dep)
2014-03-02 14:02:18 -06:00
build = effective_build_options_for(dependent)
2014-03-02 14:02:18 -06:00
if (dep.optional? || dep.recommended?) && build.without?(dep)
Dependency.prune
elsif dep.build? && dependent == f && pour_bottle
Dependency.prune
elsif dep.build? && dependent != f && install_bottle?(dependent)
Dependency.prune
elsif dep.satisfied?(options)
2013-07-22 21:36:11 -05:00
Dependency.skip
2013-06-03 22:50:11 -05:00
elsif dep.installed?
raise UnsatisfiedDependencyError.new(f, dep, options)
2013-06-03 22:50:11 -05:00
end
end
end
expanded_deps.map { |dep| [dep, inherited_options[dep]] }
end
2014-03-02 14:02:18 -06:00
def effective_build_options_for(dependent)
if dependent == f
build = dependent.build.dup
build.args |= options
build
else
dependent.build
end
end
def inherited_options_for(dep)
options = Options.new
if f.build.universal? && !dep.build? && dep.to_formula.build.has_option?("universal")
options << Option.new("universal")
end
options
end
def install_dependencies(deps)
if deps.length > 1
oh1 "Installing dependencies for #{f}: #{Tty.green}#{deps.map(&:first)*", "}#{Tty.reset}"
end
2014-02-27 14:22:42 -06:00
ARGV.filter_for_dependencies do
deps.each { |dep, options| install_dependency(dep, options) }
end
2014-02-27 14:22:42 -06:00
@show_header = true unless deps.empty?
end
def install_dependency(dep, inherited_options)
df = dep.to_formula
outdated_keg = Keg.new(df.linked_keg.realpath) rescue nil
fi = FormulaInstaller.new(df)
fi.options |= Tab.for_formula(df).used_options
fi.options |= dep.options
fi.options |= inherited_options
fi.ignore_deps = true
fi.show_header = false
fi.prelude
oh1 "Installing #{f} dependency: #{Tty.green}#{dep.name}#{Tty.reset}"
outdated_keg.unlink if outdated_keg
fi.install
fi.caveats
fi.finish
ensure
# restore previous installation state if build failed
outdated_keg.link if outdated_keg and not df.installed? rescue nil
end
def caveats
return if only_deps
2013-09-07 11:08:51 -05:00
if ARGV.homebrew_developer? and not f.keg_only?
2012-12-31 17:51:57 +00:00
audit_bin
audit_sbin
audit_lib
audit_man
audit_info
end
c = Caveats.new(f)
unless c.empty?
@show_summary_heading = true
ohai 'Caveats', c.caveats
end
end
def finish
return if only_deps
ohai 'Finishing up' if ARGV.verbose?
install_plist
if f.keg_only?
begin
Keg.new(f.prefix).optlink
2013-02-17 22:54:27 -06:00
rescue Exception
onoe "Failed to create: #{f.opt_prefix}"
puts "Things that depend on #{f} will probably not build."
end
else
link
end
2013-10-18 12:56:51 -05:00
fix_install_names if OS.mac?
post_install
ohai "Summary" if ARGV.verbose? or show_summary_heading
puts summary
ensure
unlock if hold_locks?
end
def emoji
ENV['HOMEBREW_INSTALL_BADGE'] || "\xf0\x9f\x8d\xba"
end
def summary
s = ""
s << "#{emoji} " if MacOS.version >= :lion and not ENV['HOMEBREW_NO_EMOJI']
s << "#{f.prefix}: #{f.prefix.abv}"
s << ", built in #{pretty_duration build_time}" if build_time
s
end
def build_time
@build_time ||= Time.now - @start_time unless pour_bottle? or ARGV.interactive? or @start_time.nil?
end
def sanitized_ARGV_options
args = ARGV.options_only
args.delete "--ignore-dependencies" unless ignore_deps
args
end
def build_argv
opts = Options.coerce(sanitized_ARGV_options)
opts.concat(options) unless opts.include? "--fresh"
opts << Option.new("--build-from-source") # don't download bottle
end
def build
FileUtils.rm Dir["#{HOMEBREW_LOGS}/#{f}/*"]
@start_time = Time.now
# 1. formulae can modify ENV, so we must ensure that each
# installation has a pristine ENV when it starts, forking now is
# the easiest way to do this
# 2. formulae have access to __END__ the only way to allow this is
# to make the formula script the executed script
read, write = IO.pipe
# I'm guessing this is not a good way to do this, but I'm no UNIX guru
ENV['HOMEBREW_ERROR_PIPE'] = write.to_i.to_s
args = %W[
nice #{RUBY_PATH}
-W0
-I #{File.dirname(__FILE__)}
-rbuild
--
#{f.path}
].concat(build_argv)
# Ruby 2.0+ sets close-on-exec on all file descriptors except for
# 0, 1, and 2 by default, so we have to specify that we want the pipe
# to remain open in the child process.
args << { write => write } if RUBY_VERSION >= "2.0"
fork do
begin
read.close
exec(*args)
rescue Exception => e
Marshal.dump(e, write)
write.close
exit! 1
end
end
ignore_interrupts(:quietly) do # the fork will receive the interrupt and marshall it back
write.close
Process.wait
data = read.read
read.close
raise Marshal.load(data) unless data.nil? or data.empty?
raise Interrupt if $?.exitstatus == 130
raise "Suspicious installation failure" unless $?.success?
end
raise "Empty installation" if Dir["#{f.prefix}/*"].empty?
2013-02-17 22:54:27 -06:00
rescue Exception
ignore_interrupts do
# any exceptions must leave us with nothing installed
f.prefix.rmtree if f.prefix.directory?
f.rack.rmdir_if_possible
end
raise
end
def link
if f.linked_keg.directory? and f.linked_keg.realpath == f.prefix
opoo "This keg was marked linked already, continuing anyway"
# otherwise Keg.link will bail
f.linked_keg.unlink
end
keg = Keg.new(f.prefix)
begin
keg.link
rescue Exception => e
onoe "The `brew link` step did not complete successfully"
puts "The formula built, but is not symlinked into #{HOMEBREW_PREFIX}"
puts "You can try again using `brew link #{f.name}'"
puts
puts "Possible conflicting files are:"
mode = OpenStruct.new(:dry_run => true, :overwrite => true)
keg.link(mode)
ohai e, e.backtrace if ARGV.debug?
@show_summary_heading = true
ignore_interrupts{ keg.unlink }
raise unless e.kind_of? RuntimeError
end
end
def install_plist
return unless f.plist
# A plist may already exist if we are installing from a bottle
f.plist_path.unlink if f.plist_path.exist?
f.plist_path.write f.plist
f.plist_path.chmod 0644
end
def fix_install_names
keg = Keg.new(f.prefix)
keg.fix_install_names(:keg_only => f.keg_only?)
if @poured_bottle
keg.relocate_install_names Keg::PREFIX_PLACEHOLDER, HOMEBREW_PREFIX.to_s,
Keg::CELLAR_PLACEHOLDER, HOMEBREW_CELLAR.to_s, :keg_only => f.keg_only?
end
rescue Exception => e
onoe "Failed to fix install names"
puts "The formula built, but you may encounter issues using it or linking other"
puts "formula against it."
ohai e, e.backtrace if ARGV.debug?
@show_summary_heading = true
end
def clean
ohai "Cleaning" if ARGV.verbose?
Cleaner.new(f).clean
rescue Exception => e
opoo "The cleaning step did not complete successfully"
puts "Still, the installation was successful, so we will link it into your prefix"
ohai e, e.backtrace if ARGV.debug?
@show_summary_heading = true
end
def post_install
f.post_install
rescue Exception => e
opoo "The post-install step did not complete successfully"
puts "You can try again using `brew postinstall #{f.name}`"
ohai e, e.backtrace if ARGV.debug?
@show_summary_heading = true
end
def pour
if f.local_bottle_path
downloader = LocalBottleDownloadStrategy.new(f)
else
bottle = f.bottle
downloader = bottle.downloader
bottle.verify_download_integrity(bottle.fetch)
end
HOMEBREW_CELLAR.cd do
downloader.stage
end
Dir["#{f.bottle_prefix}/{etc,var}/**/*"].each do |file|
path = Pathname.new(file)
path.extend(InstallRenamed)
path.cp_path_sub(f.bottle_prefix, HOMEBREW_PREFIX)
end
FileUtils.rm_rf f.bottle_prefix
end
## checks
def print_check_output warning_and_description
return unless warning_and_description
warning, description = *warning_and_description
opoo warning
puts description
@show_summary_heading = true
end
def audit_bin
print_check_output(check_PATH(f.bin)) unless f.keg_only?
print_check_output(check_non_executables(f.bin))
print_check_output(check_generic_executables(f.bin))
end
def audit_sbin
print_check_output(check_PATH(f.sbin)) unless f.keg_only?
print_check_output(check_non_executables(f.sbin))
print_check_output(check_generic_executables(f.sbin))
end
def audit_lib
print_check_output(check_jars)
print_check_output(check_non_libraries)
end
def audit_man
print_check_output(check_manpages)
end
def audit_info
print_check_output(check_infopages)
end
private
def hold_locks?
@hold_locks || false
end
def lock
if (@@locked ||= []).empty?
f.recursive_dependencies.each do |dep|
@@locked << dep.to_formula
end unless ignore_deps
@@locked.unshift(f)
@@locked.each(&:lock)
@hold_locks = true
end
end
def unlock
if hold_locks?
@@locked.each(&:unlock)
@@locked.clear
@hold_locks = false
end
end
end
class Formula
def keg_only_text
2013-11-13 23:22:18 -08:00
s = "This formula is keg-only, so it was not symlinked into #{HOMEBREW_PREFIX}."
2012-08-10 16:34:20 -04:00
s << "\n\n#{keg_only_reason.to_s}"
if lib.directory? or include.directory?
s <<
<<-EOS.undent_________________________________________________________72
2012-08-10 16:34:20 -04:00
Generally there are no consequences of this for you. If you build your
own software and it requires this formula, you'll need to add to your
build variables:
2012-08-10 16:34:20 -04:00
EOS
s << " LDFLAGS: -L#{HOMEBREW_PREFIX}/opt/#{name}/lib\n" if lib.directory?
s << " CPPFLAGS: -I#{HOMEBREW_PREFIX}/opt/#{name}/include\n" if include.directory?
end
s << "\n"
end
end