Clean up and improve build-error output and logs

All logs are now stored from each command executed in Formula.install.

Error output is truncated to five lines in an attempt to not overwhelm the user and to encourage users to read the error output and report the bug properly. Maybe we can get that figure up from 70% to 90%.
This commit is contained in:
Max Howell 2012-09-11 20:59:59 -04:00
parent 1100818100
commit a217b03952
7 changed files with 55 additions and 83 deletions

View File

@ -168,9 +168,10 @@ class Cmd
dels = @args - args dels = @args - args
adds = args - @args adds = args - @args
dups = dels & args dups = dels & args
puts "brew: Superenv removed: #{dels*' '}" unless dels.empty?
puts "brew: Superenv deduped: #{dels}" unless dups.empty? STDERR.puts "brew: superenv removed: #{dels*' '}" unless dels.empty?
puts "brew: Superenv added: #{adds*' '}" unless adds.empty? STDERR.puts "brew: superenv deduped: #{dels}" unless dups.empty?
STDERR.puts "brew: superenv added: #{adds*' '}" unless adds.empty?
end end
end end

View File

@ -128,7 +128,6 @@ def install f
end end
interactive_shell f interactive_shell f
nil
else else
f.prefix.mkpath f.prefix.mkpath
f.install f.install

View File

@ -95,54 +95,13 @@ class BuildError < Homebrew::InstallationError
end end
def dump def dump
e = self logs = "#{ENV['HOME']}/Library/Logs/Homebrew/#{formula}/"
require 'cmd/--config'
require 'cmd/--env'
e.backtrace[1] =~ %r{Library/Formula/(.+)\.rb:(\d+)}
formula_name = $1
error_line = $2
path = HOMEBREW_REPOSITORY/"Library/Formula/#{formula_name}.rb"
if path.symlink? and path.realpath.to_s =~ %r{^#{HOMEBREW_REPOSITORY}/Library/Taps/(\w+)-(\w+)/}
repo = "#$1/homebrew-#$2"
repo_path = path.realpath.relative_path_from(HOMEBREW_REPOSITORY/"Library/Taps/#$1-#$2").parent.to_s
issues_url = "https://github.com/#$1/homebrew-#$2/issues/new"
else
repo = "mxcl/master"
repo_path = "Library/Formula"
issues_url = ISSUES_URL
end
if ARGV.verbose?
ohai "Exit Status: #{e.exit_status}"
puts "https://github.com/#{repo}/blob/master/#{repo_path}/#{formula_name}.rb#L#{error_line}"
end
ohai "Build Environment"
Homebrew.dump_build_config
puts %["--use-clang" was specified] if ARGV.include? '--use-clang'
puts %["--use-llvm" was specified] if ARGV.include? '--use-llvm'
puts %["--use-gcc" was specified] if ARGV.include? '--use-gcc'
Homebrew.dump_build_env e.env
puts puts
onoe "#{e.to_s.strip} (#{formula_name}.rb:#{error_line})" onoe "#{formula.name} did not build"
issues = GitHub.issues_for_formula formula_name puts "Logs: #{logs}" unless Dir["#{logs}/*"].empty?
puts puts "Help: #{Tty.em}https://github.com/mxcl/homebrew/wiki/troubleshooting#{Tty.reset}"
if issues.empty? issues = GitHub.issues_for_formula(formula.name)
puts "This link will help resolve the above errors:" puts *issues.map{ |s| " #{Tty.em}#{s}#{Tty.reset}" } unless issues.empty?
puts " #{Tty.em}#{issues_url}#{Tty.reset}"
else
puts "These existing issues may help you:", *issues.map{ |s| " #{Tty.em}#{s}#{Tty.reset}" }
puts "Otherwise, this may help you fix or report the issue:"
puts " #{Tty.em}#{issues_url}#{Tty.reset}"
end
if e.was_running_configure?
puts "We saved the configure log:"
puts " ~/Library/Logs/Homebrew/config.log"
puts "When you report the issue please paste the build output above and the config.log here:"
puts " #{Tty.em}http://gist.github.com/#{Tty.reset}"
end
end end
end end

View File

@ -14,7 +14,7 @@ module FileUtils extend self
# /tmp volume to the other volume. So we let the user override the tmp # /tmp volume to the other volume. So we let the user override the tmp
# prefix if they need to. # prefix if they need to.
tmp = ENV['HOMEBREW_TEMP'].chuzzle || '/tmp' tmp = ENV['HOMEBREW_TEMP'].chuzzle || '/tmp'
tempd = `/usr/bin/mktemp -d #{tmp}/brew-#{name}-#{version}-XXXX`.chuzzle tempd = `/usr/bin/mktemp -d #{tmp}/#{name}-XXXX`.chuzzle
raise "Failed to create sandbox" if tempd.nil? raise "Failed to create sandbox" if tempd.nil?
prevd = pwd prevd = pwd
cd tempd cd tempd

View File

@ -223,25 +223,17 @@ class Formula
# we allow formulas to do anything they want to the Ruby process # we allow formulas to do anything they want to the Ruby process
# so load any deps before this point! And exit asap afterwards # so load any deps before this point! And exit asap afterwards
yield self yield self
rescue Interrupt, RuntimeError, SystemCallError => e rescue RuntimeError, SystemCallError => e
puts if Interrupt === e # don't print next to the ^C if not ARGV.debug?
unless ARGV.debug? %w(config.log CMakeCache.txt).each do |fn|
%w(config.log CMakeCache.txt).select{|f| File.exist? f}.each do |f| (HOMEBREW_LOGS/name).install(fn) if File.file?(fn)
HOMEBREW_LOGS.install f
puts "#{f} was copied to #{HOMEBREW_LOGS}"
end end
raise raise
end end
onoe e.inspect onoe e.inspect
puts e.backtrace puts e.backtrace unless e.kind_of? BuildError
ohai "Rescuing build..." ohai "Rescuing build..."
if (e.was_running_configure? rescue false) and File.exist? 'config.log'
puts "It looks like an autotools configure failed."
puts "Gist 'config.log' and any error output when reporting an issue."
puts
end
puts "When you exit this shell Homebrew will attempt to finalise the installation." puts "When you exit this shell Homebrew will attempt to finalise the installation."
puts "If nothing is installed or the shell exits with a non-zero error code," puts "If nothing is installed or the shell exits with a non-zero error code,"
puts "Homebrew will abort. The installation prefix is:" puts "Homebrew will abort. The installation prefix is:"
@ -529,23 +521,30 @@ protected
if ARGV.verbose? if ARGV.verbose?
safe_system cmd, *args safe_system cmd, *args
else else
@exec_count ||= 0
@exec_count += 1
logd = HOMEBREW_LOGS/name
logfn = "#{logd}/%02d.%s" % [@exec_count, File.basename(cmd).split(' ').first]
mkdir_p(logd)
rd, wr = IO.pipe rd, wr = IO.pipe
pid = fork do pid = fork do
ENV['VERBOSE'] = '1' # helps with many tool's logging outputs
rd.close rd.close
$stdout.reopen wr $stdout.reopen wr
$stderr.reopen wr $stderr.reopen wr
args.collect!{|arg| arg.to_s} args.collect!{|arg| arg.to_s}
exec(cmd, *args) rescue nil exec(cmd, *args) rescue nil
puts "Failed to execute: #{cmd}"
exit! 1 # never gets here unless exec threw or failed exit! 1 # never gets here unless exec threw or failed
end end
wr.close wr.close
out = ''
out << rd.read until rd.eof? f = File.open(logfn, 'w')
f.write(rd.read) until rd.eof?
Process.wait Process.wait
unless $?.success? raise unless $?.success?
puts out
raise
end
end end
removed_ENV_variables.each do |key, value| removed_ENV_variables.each do |key, value|
@ -553,7 +552,21 @@ protected
end if removed_ENV_variables end if removed_ENV_variables
rescue rescue
if f
f.flush
Kernel.system "/usr/bin/tail -n 5 #{logfn}"
require 'cmd/--config'
$f = f
def Homebrew.puts(*foo); $f.puts *foo end
f.puts
Homebrew.dump_build_config
class << Homebrew; undef :puts end
else
puts "No logs recorded :(" unless ARGV.verbose?
end
raise BuildError.new(self, cmd, args, $?) raise BuildError.new(self, cmd, args, $?)
ensure
f.close if f
end end
public public

View File

@ -200,6 +200,8 @@ class FormulaInstaller
end end
def build def build
FileUtils.rm Dir["#{HOMEBREW_LOGS}/#{f}/*"]
@start_time = Time.now @start_time = Time.now
# 1. formulae can modify ENV, so we must ensure that each # 1. formulae can modify ENV, so we must ensure that each
@ -236,7 +238,7 @@ class FormulaInstaller
end end
end end
ignore_interrupts do # the fork will receive the interrupt and marshall it back ignore_interrupts(:quietly) do # the fork will receive the interrupt and marshall it back
write.close write.close
Process.wait Process.wait
data = read.read data = read.read
@ -245,18 +247,14 @@ class FormulaInstaller
raise "Suspicious installation failure" unless $?.success? raise "Suspicious installation failure" unless $?.success?
end end
# This is the installation receipt. The reason this comment is necessary raise "Empty installation" if Dir["#{f.prefix}/*"].empty?
# is because some numpty decided to call the class Tab rather than
# the far more appropriate InstallationReceipt :P Tab.for_install(f, args).write # INSTALL_RECEIPT.json
Tab.for_install(f, args).write
rescue Exception => e rescue Exception => e
ignore_interrupts do ignore_interrupts do
# any exceptions must leave us with nothing installed # any exceptions must leave us with nothing installed
if f.prefix.directory? f.prefix.rmtree if f.prefix.directory?
puts "One sec, just cleaning up..." if e.kind_of? Interrupt
f.prefix.rmtree
end
f.rack.rmdir_if_possible f.rack.rmdir_if_possible
end end
raise raise

View File

@ -216,8 +216,10 @@ def inreplace path, before=nil, after=nil
end end
end end
def ignore_interrupts def ignore_interrupts(opt = nil)
std_trap = trap("INT") { puts "One sec, just cleaning up" } std_trap = trap("INT") do
puts "One sec, just cleaning up" unless opt = :quietly
end
yield yield
ensure ensure
trap("INT", std_trap) trap("INT", std_trap)