mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00

- the executed script is the formula file, build.rb is a library - ruby is invoked directly, so it wouldn't need to be executable anyway
226 lines
6.7 KiB
Ruby
226 lines
6.7 KiB
Ruby
# This script is loaded by formula_installer as a separate instance.
|
|
# Rationale: Formula can use __END__, Formula can change ENV
|
|
# Thrown exceptions are propogated back to the parent process over a pipe
|
|
|
|
STD_TRAP = trap("INT") { exit! 130 } # no backtrace thanks
|
|
|
|
at_exit do
|
|
# the whole of everything must be run in at_exit because the formula has to
|
|
# be the run script as __END__ must work for *that* formula.
|
|
main
|
|
end
|
|
|
|
require 'global'
|
|
require 'cxxstdlib'
|
|
require 'debrew' if ARGV.debug?
|
|
|
|
def main
|
|
# The main Homebrew process expects to eventually see EOF on the error
|
|
# pipe in FormulaInstaller#build. However, if any child process fails to
|
|
# terminate (i.e, fails to close the descriptor), this won't happen, and
|
|
# the installer will hang. Set close-on-exec to prevent this.
|
|
# Whether it is *wise* to launch daemons from formulae is a separate
|
|
# question altogether.
|
|
if ENV['HOMEBREW_ERROR_PIPE']
|
|
require 'fcntl'
|
|
error_pipe = IO.new(ENV['HOMEBREW_ERROR_PIPE'].to_i, 'w')
|
|
error_pipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
|
end
|
|
|
|
raise $! if $! # an exception was already thrown when parsing the formula
|
|
|
|
trap("INT", STD_TRAP) # restore default CTRL-C handler
|
|
|
|
require 'hardware'
|
|
require 'keg'
|
|
require 'extend/ENV'
|
|
|
|
# Force any future invocations of sudo to require the user's password to be
|
|
# re-entered. This is in-case any build script call sudo. Certainly this is
|
|
# can be inconvenient for the user. But we need to be safe.
|
|
system "/usr/bin/sudo", "-k"
|
|
|
|
Build.new(Formula.factory($0)).install
|
|
rescue Exception => e
|
|
unless error_pipe.nil?
|
|
e.continuation = nil if ARGV.debug?
|
|
Marshal.dump(e, error_pipe)
|
|
error_pipe.close
|
|
exit! 1
|
|
else
|
|
onoe e
|
|
puts e.backtrace
|
|
exit! 2
|
|
end
|
|
end
|
|
|
|
class Build
|
|
attr_reader :f, :deps, :reqs
|
|
|
|
def initialize(f)
|
|
@f = f
|
|
|
|
if ARGV.ignore_deps?
|
|
@deps = []
|
|
@reqs = []
|
|
else
|
|
@deps = expand_deps
|
|
@reqs = expand_reqs
|
|
end
|
|
end
|
|
|
|
def post_superenv_hacks
|
|
# Only allow Homebrew-approved directories into the PATH, unless
|
|
# a formula opts-in to allowing the user's path.
|
|
if f.env.userpaths? || reqs.any? { |rq| rq.env.userpaths? }
|
|
ENV.userpaths!
|
|
end
|
|
end
|
|
|
|
def pre_superenv_hacks
|
|
# Allow a formula to opt-in to the std environment.
|
|
if (f.env.std? || deps.any? { |d| d.name == "scons" }) && ARGV.env != "super"
|
|
ARGV.unshift "--env=std"
|
|
end
|
|
end
|
|
|
|
def expand_reqs
|
|
f.recursive_requirements do |dependent, req|
|
|
if (req.optional? || req.recommended?) && dependent.build.without?(req)
|
|
Requirement.prune
|
|
elsif req.build? && dependent != f
|
|
Requirement.prune
|
|
elsif req.satisfied? && req.default_formula? && (dep = req.to_dependency).installed?
|
|
deps << dep
|
|
Requirement.prune
|
|
end
|
|
end
|
|
end
|
|
|
|
def expand_deps
|
|
f.recursive_dependencies do |dependent, dep|
|
|
if (dep.optional? || dep.recommended?) && dependent.build.without?(dep)
|
|
Dependency.prune
|
|
elsif dep.build? && dependent != f
|
|
Dependency.prune
|
|
elsif dep.build?
|
|
Dependency.keep_but_prune_recursive_deps
|
|
end
|
|
end
|
|
end
|
|
|
|
def install
|
|
keg_only_deps = deps.map(&:to_formula).select(&:keg_only?)
|
|
|
|
pre_superenv_hacks
|
|
|
|
ENV.activate_extensions!
|
|
|
|
deps.map(&:to_formula).each do |dep|
|
|
opt = HOMEBREW_PREFIX/:opt/dep
|
|
fixopt(dep) unless opt.directory?
|
|
end
|
|
|
|
if superenv?
|
|
ENV.keg_only_deps = keg_only_deps.map(&:name)
|
|
ENV.deps = deps.map { |d| d.to_formula.name }
|
|
ENV.x11 = reqs.any? { |rq| rq.kind_of?(X11Dependency) }
|
|
ENV.setup_build_environment(f)
|
|
post_superenv_hacks
|
|
reqs.each(&:modify_build_environment)
|
|
deps.each(&:modify_build_environment)
|
|
else
|
|
ENV.setup_build_environment(f)
|
|
reqs.each(&:modify_build_environment)
|
|
deps.each(&:modify_build_environment)
|
|
|
|
keg_only_deps.each do |dep|
|
|
opt = dep.opt_prefix
|
|
ENV.prepend_path 'PATH', "#{opt}/bin"
|
|
ENV.prepend_path 'PKG_CONFIG_PATH', "#{opt}/lib/pkgconfig"
|
|
ENV.prepend_path 'PKG_CONFIG_PATH', "#{opt}/share/pkgconfig"
|
|
ENV.prepend_path 'ACLOCAL_PATH', "#{opt}/share/aclocal"
|
|
ENV.prepend_path 'CMAKE_PREFIX_PATH', opt
|
|
ENV.prepend 'LDFLAGS', "-L#{opt}/lib" if (opt/:lib).directory?
|
|
ENV.prepend 'CPPFLAGS', "-I#{opt}/include" if (opt/:include).directory?
|
|
end
|
|
end
|
|
|
|
f.brew do
|
|
if ARGV.flag? '--git'
|
|
system "git init"
|
|
system "git add -A"
|
|
end
|
|
if ARGV.interactive?
|
|
ohai "Entering interactive mode"
|
|
puts "Type `exit' to return and finalize the installation"
|
|
puts "Install to this prefix: #{f.prefix}"
|
|
|
|
if ARGV.flag? '--git'
|
|
puts "This directory is now a git repo. Make your changes and then use:"
|
|
puts " git diff | pbcopy"
|
|
puts "to copy the diff to the clipboard."
|
|
end
|
|
|
|
interactive_shell f
|
|
else
|
|
f.prefix.mkpath
|
|
|
|
f.resources.each { |r| r.extend(ResourceDebugger) } if ARGV.debug?
|
|
|
|
begin
|
|
f.install
|
|
|
|
# This first test includes executables because we still
|
|
# want to record the stdlib for something that installs no
|
|
# dylibs.
|
|
stdlibs = Keg.new(f.prefix).detect_cxx_stdlibs
|
|
# This currently only tracks a single C++ stdlib per dep,
|
|
# though it's possible for different libs/executables in
|
|
# a given formula to link to different ones.
|
|
stdlib_in_use = CxxStdlib.new(stdlibs.first, ENV.compiler)
|
|
begin
|
|
stdlib_in_use.check_dependencies(f, deps)
|
|
rescue IncompatibleCxxStdlibs => e
|
|
opoo e.message
|
|
end
|
|
|
|
# This second check is recorded for checking dependencies,
|
|
# so executable are irrelevant at this point. If a piece
|
|
# of software installs an executable that links against libstdc++
|
|
# and dylibs against libc++, libc++-only dependencies can safely
|
|
# link against it.
|
|
stdlibs = Keg.new(f.prefix).detect_cxx_stdlibs :skip_executables => true
|
|
|
|
Tab.create(f, ENV.compiler, stdlibs.first,
|
|
Options.coerce(ARGV.options_only)).write
|
|
rescue Exception => e
|
|
if ARGV.debug?
|
|
debrew e, f
|
|
else
|
|
raise e
|
|
end
|
|
end
|
|
|
|
# Find and link metafiles
|
|
f.prefix.install_metafiles Pathname.pwd
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def fixopt f
|
|
path = if f.linked_keg.directory? and f.linked_keg.symlink?
|
|
f.linked_keg.resolved_path
|
|
elsif f.prefix.directory?
|
|
f.prefix
|
|
elsif (kids = f.rack.children).size == 1 and kids.first.directory?
|
|
kids.first
|
|
else
|
|
raise
|
|
end
|
|
Keg.new(path).optlink
|
|
rescue StandardError
|
|
raise "#{f.opt_prefix} not present or broken\nPlease reinstall #{f}. Sorry :("
|
|
end
|