brew/Library/Homebrew/build.rb

215 lines
6.4 KiB
Ruby
Raw Normal View History

# This script is loaded by formula_installer as a separate instance.
2015-04-13 17:53:02 +08:00
# Thrown exceptions are propagated back to the parent process over a pipe
2011-03-15 22:02:14 -07:00
old_trap = trap("INT") { exit! 130 }
require "global"
require "build_options"
require "cxxstdlib"
require "keg"
require "extend/ENV"
require "debrew"
require "fcntl"
require "socket"
2013-05-25 15:26:55 -05:00
class Build
attr_reader :formula, :deps, :reqs
def initialize(formula, options)
@formula = formula
@formula.build = BuildOptions.new(options, formula.options)
if ARGV.ignore_deps?
@deps = []
@reqs = []
else
@deps = expand_deps
@reqs = expand_reqs
end
2013-05-25 15:26:55 -05:00
end
2013-05-25 15:26:55 -05:00
def post_superenv_hacks
# Only allow Homebrew-approved directories into the PATH, unless
# a formula opts-in to allowing the user's path.
2016-09-22 20:12:28 +02:00
return unless formula.env.userpaths? || reqs.any? { |rq| rq.env.userpaths? }
2018-09-17 02:45:00 +02:00
2016-09-22 20:12:28 +02:00
ENV.userpaths!
2013-05-25 15:26:55 -05:00
end
def effective_build_options_for(dependent)
args = dependent.build.used_options
args |= Tab.for_formula(dependent).used_options
BuildOptions.new(args, dependent.options)
end
def expand_reqs
formula.recursive_requirements do |dependent, req|
build = effective_build_options_for(dependent)
if req.prune_from_option?(build)
Requirement.prune
elsif req.prune_if_build_and_not_dependent?(dependent, formula)
Requirement.prune
end
end
end
2013-05-25 15:26:55 -05:00
def expand_deps
formula.recursive_dependencies do |dependent, dep|
build = effective_build_options_for(dependent)
if dep.prune_from_option?(build)
Dependency.prune
elsif dep.prune_if_build_and_not_dependent?(dependent, formula)
Dependency.prune
elsif dep.build?
Dependency.keep_but_prune_recursive_deps
2013-05-25 15:26:55 -05:00
end
end
end
2013-05-25 15:26:55 -05:00
def install
formula_deps = deps.map(&:to_formula)
keg_only_deps = formula_deps.select(&:keg_only?)
2018-05-16 11:34:12 -07:00
run_time_deps = deps.reject(&:build?).map(&:to_formula)
2013-05-25 15:26:55 -05:00
formula_deps.each do |dep|
fixopt(dep) unless dep.opt_prefix.directory?
end
ENV.activate_extensions!
2013-05-25 15:26:55 -05:00
if superenv?
ENV.keg_only_deps = keg_only_deps
ENV.deps = formula_deps
2018-05-16 11:34:12 -07:00
ENV.run_time_deps = run_time_deps
ENV.x11 = reqs.any? { |rq| rq.is_a?(X11Requirement) }
ENV.setup_build_environment(formula)
2013-05-25 15:26:55 -05:00
post_superenv_hacks
2013-05-25 15:26:55 -05:00
reqs.each(&:modify_build_environment)
deps.each(&:modify_build_environment)
2013-05-25 15:26:55 -05:00
else
ENV.setup_build_environment(formula)
2013-05-25 15:26:55 -05:00
reqs.each(&:modify_build_environment)
deps.each(&:modify_build_environment)
2013-05-25 15:26:55 -05:00
keg_only_deps.each do |dep|
2014-06-09 14:55:01 -05:00
ENV.prepend_path "PATH", dep.opt_bin.to_s
ENV.prepend_path "PKG_CONFIG_PATH", "#{dep.opt_lib}/pkgconfig"
ENV.prepend_path "PKG_CONFIG_PATH", "#{dep.opt_share}/pkgconfig"
ENV.prepend_path "ACLOCAL_PATH", "#{dep.opt_share}/aclocal"
ENV.prepend_path "CMAKE_PREFIX_PATH", dep.opt_prefix.to_s
ENV.prepend "LDFLAGS", "-L#{dep.opt_lib}" if dep.opt_lib.directory?
ENV.prepend "CPPFLAGS", "-I#{dep.opt_include}" if dep.opt_include.directory?
2013-05-25 15:26:55 -05:00
end
end
2017-07-15 17:26:42 -07:00
new_env = {
"TMPDIR" => HOMEBREW_TEMP,
2018-11-02 17:18:07 +00:00
"TEMP" => HOMEBREW_TEMP,
"TMP" => HOMEBREW_TEMP,
2017-07-15 17:26:42 -07:00
}
2017-07-15 17:26:42 -07:00
with_env(new_env) do
formula.extend(Debrew::Formula) if ARGV.debug?
2017-07-15 17:26:42 -07:00
formula.brew do |_formula, staging|
# For head builds, HOMEBREW_FORMULA_PREFIX should include the commit,
# which is not known until after the formula has been staged.
ENV["HOMEBREW_FORMULA_PREFIX"] = formula.prefix
2017-07-15 17:26:42 -07:00
staging.retain! if ARGV.keep_tmp?
formula.patch
2013-05-25 15:26:55 -05:00
2014-11-03 21:33:20 -06:00
if ARGV.git?
2017-07-15 17:26:42 -07:00
system "git", "init"
system "git", "add", "-A"
end
if ARGV.interactive?
ohai "Entering interactive mode"
2019-04-05 12:24:10 -04:00
puts "Type `exit` to return and finalize the installation"
2017-07-15 17:26:42 -07:00
puts "Install to this prefix: #{formula.prefix}"
if ARGV.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(formula)
else
formula.prefix.mkpath
(formula.logs/"00.options.out").write \
"#{formula.full_name} #{formula.build.used_options.sort.join(" ")}".strip
formula.install
stdlibs = detect_stdlibs(ENV.compiler)
tab = Tab.create(formula, ENV.compiler, stdlibs.first)
tab.write
# Find and link metafiles
formula.prefix.install_metafiles formula.buildpath
formula.prefix.install_metafiles formula.libexec if formula.libexec.exist?
2013-05-25 15:26:55 -05:00
end
end
end
end
def detect_stdlibs(compiler)
keg = Keg.new(formula.prefix)
CxxStdlib.check_compatibility(formula, deps, keg, compiler)
# The stdlib recorded in the install receipt is used during dependency
# compatibility checks, so we only care about the stdlib that libraries
# link against.
keg.detect_cxx_stdlibs(skip_executables: true)
end
def fixopt(f)
path = if f.linked_keg.directory? && f.linked_keg.symlink?
f.linked_keg.resolved_path
elsif f.prefix.directory?
f.prefix
elsif (kids = f.rack.children).size == 1 && kids.first.directory?
kids.first
else
raise
end
Keg.new(path).optlink
rescue
2015-05-27 20:35:18 +08:00
raise "#{f.opt_prefix} not present or broken\nPlease reinstall #{f.full_name}. Sorry :("
end
end
begin
error_pipe = UNIXSocket.open(ENV["HOMEBREW_ERROR_PIPE"], &:recv_io)
error_pipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
trap("INT", old_trap)
formula = ARGV.formulae.first
options = Options.create(ARGV.flags_only)
build = Build.new(formula, options)
build.install
rescue Exception => e # rubocop:disable Lint/RescueException
error_hash = JSON.parse e.to_json
# Special case: need to recreate BuildErrors in full
# for proper analytics reporting and error messages.
# BuildErrors are specific to build processses and not other
# children, which is why we create the necessary state here
# and not in Utils.safe_fork.
if error_hash["json_class"] == "BuildError"
error_hash["cmd"] = e.cmd
error_hash["args"] = e.args
error_hash["env"] = e.env
elsif error_hash["json_class"] == "ErrorDuringExecution"
error_hash["cmd"] = e.cmd
error_hash["status"] = e.status.exitstatus
error_hash["output"] = e.output
end
error_pipe.puts error_hash.to_json
error_pipe.close
exit! 1
end