2010-11-09 12:57:41 +00:00
|
|
|
require 'exceptions'
|
2009-10-26 18:13:38 +00:00
|
|
|
require 'formula'
|
2011-08-23 23:30:52 +01:00
|
|
|
require 'keg'
|
2009-10-26 18:13:38 +00:00
|
|
|
require 'set'
|
2011-09-22 20:07:39 -07:00
|
|
|
require 'tab'
|
2009-10-26 18:13:38 +00:00
|
|
|
|
|
|
|
class FormulaInstaller
|
2011-08-23 23:30:52 +01:00
|
|
|
attr :f
|
|
|
|
attr :show_summary_heading, true
|
2010-11-09 12:57:41 +00:00
|
|
|
attr :ignore_deps, true
|
2011-08-23 23:30:52 +01:00
|
|
|
attr :install_bottle, true
|
|
|
|
attr :show_header, true
|
2009-10-26 18:13:38 +00:00
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
def initialize ff
|
|
|
|
@f = ff
|
|
|
|
@show_header = true
|
|
|
|
@ignore_deps = ARGV.include? '--ignore-dependencies' || ARGV.interactive?
|
2012-02-09 21:05:17 +00:00
|
|
|
@install_bottle = !ARGV.build_from_source? && ff.bottle_up_to_date?
|
2009-10-26 18:13:38 +00:00
|
|
|
end
|
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
def install
|
|
|
|
raise FormulaAlreadyInstalledError, f if f.installed? and not ARGV.force?
|
2011-03-09 21:01:23 -08:00
|
|
|
|
|
|
|
unless ignore_deps
|
2011-08-23 23:30:52 +01:00
|
|
|
f.check_external_deps
|
|
|
|
|
|
|
|
needed_deps = f.recursive_deps.reject{ |d| d.installed? }
|
2011-04-06 12:22:28 -07:00
|
|
|
unless needed_deps.empty?
|
|
|
|
needed_deps.each do |dep|
|
2011-11-26 21:03:46 -08:00
|
|
|
if dep.explicitly_requested?
|
|
|
|
install_dependency dep
|
|
|
|
else
|
|
|
|
ARGV.filter_for_dependencies do
|
|
|
|
# Re-create the formula object so that args like `--HEAD` won't
|
2011-11-29 10:43:02 -06:00
|
|
|
# affect properties like the installation prefix. Also need to
|
|
|
|
# re-check installed status as the Formula may have changed.
|
2011-12-26 11:55:05 -08:00
|
|
|
dep = Formula.factory dep.path
|
2011-11-29 10:43:02 -06:00
|
|
|
install_dependency dep unless dep.installed?
|
2011-11-26 21:03:46 -08:00
|
|
|
end
|
|
|
|
end
|
2011-04-06 12:22:28 -07:00
|
|
|
end
|
2011-08-23 23:30:52 +01:00
|
|
|
# now show header as all the deps stuff has clouded the original issue
|
|
|
|
show_header = true
|
2011-02-20 12:20:07 -05:00
|
|
|
end
|
2010-04-08 14:50:06 -07:00
|
|
|
end
|
2010-01-13 09:00:24 +00:00
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
oh1 "Installing #{f}" if show_header
|
|
|
|
|
|
|
|
@@attempted ||= Set.new
|
|
|
|
raise FormulaInstallationAlreadyAttemptedError, f if @@attempted.include? f
|
|
|
|
@@attempted << f
|
|
|
|
|
|
|
|
if install_bottle
|
|
|
|
pour
|
|
|
|
else
|
|
|
|
build
|
|
|
|
clean
|
2010-02-09 11:30:16 -08:00
|
|
|
end
|
2011-08-23 23:30:52 +01:00
|
|
|
|
|
|
|
raise "Nothing was installed to #{f.prefix}" unless f.installed?
|
2010-02-09 11:30:16 -08:00
|
|
|
end
|
|
|
|
|
2011-11-26 21:03:46 -08:00
|
|
|
def install_dependency dep
|
|
|
|
fi = FormulaInstaller.new dep
|
|
|
|
fi.ignore_deps = true
|
|
|
|
fi.show_header = false
|
|
|
|
oh1 "Installing #{f} dependency: #{dep}"
|
|
|
|
fi.install
|
2012-02-10 17:21:48 -06:00
|
|
|
Keg.new(dep.linked_keg.realpath).unlink if dep.linked_keg.directory?
|
2011-11-26 21:03:46 -08:00
|
|
|
fi.caveats
|
|
|
|
fi.finish
|
|
|
|
end
|
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
def caveats
|
2011-12-20 20:11:54 -08:00
|
|
|
the_caveats = (f.caveats || "").strip
|
|
|
|
unless the_caveats.empty?
|
2011-08-23 23:30:52 +01:00
|
|
|
ohai "Caveats", f.caveats
|
|
|
|
@show_summary_heading = true
|
2010-02-09 11:30:16 -08:00
|
|
|
end
|
2011-12-20 20:11:54 -08:00
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
if f.keg_only?
|
|
|
|
ohai 'Caveats', f.keg_only_text
|
|
|
|
@show_summary_heading = true
|
|
|
|
else
|
2012-02-19 19:09:38 -08:00
|
|
|
audit_bin
|
|
|
|
audit_lib
|
2011-08-23 23:30:52 +01:00
|
|
|
check_manpages
|
|
|
|
check_infopages
|
|
|
|
check_m4
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def finish
|
|
|
|
ohai 'Finishing up' if ARGV.verbose?
|
|
|
|
|
2011-09-13 19:40:51 -07:00
|
|
|
unless f.keg_only?
|
|
|
|
link
|
|
|
|
check_PATH
|
|
|
|
end
|
2011-08-23 23:30:52 +01:00
|
|
|
fix_install_names
|
|
|
|
|
|
|
|
ohai "Summary" if ARGV.verbose? or show_summary_heading
|
|
|
|
print "#{f.prefix}: #{f.prefix.abv}"
|
|
|
|
print ", built in #{pretty_duration build_time}" if build_time
|
|
|
|
puts
|
2009-10-26 18:13:38 +00:00
|
|
|
end
|
2010-07-20 21:03:25 -07:00
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
def build_time
|
|
|
|
@build_time ||= Time.now - @start_time unless install_bottle or ARGV.interactive? or @start_time.nil?
|
|
|
|
end
|
2009-10-26 18:13:38 +00:00
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
def build
|
|
|
|
@start_time = Time.now
|
2009-10-26 18:13:38 +00:00
|
|
|
|
|
|
|
# 1. formulae can modify ENV, so we must ensure that each
|
2010-11-09 12:57:41 +00:00
|
|
|
# installation has a pristine ENV when it starts, forking now is
|
2009-10-26 18:13:38 +00:00
|
|
|
# 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
|
|
|
|
|
2011-11-26 21:03:46 -08:00
|
|
|
args = ARGV.clone
|
|
|
|
unless args.include? '--fresh'
|
|
|
|
previous_install = Tab.for_formula f
|
|
|
|
args.concat previous_install.used_options
|
|
|
|
args.uniq! # Just in case some dupes were added
|
|
|
|
end
|
|
|
|
|
2010-11-09 12:57:41 +00:00
|
|
|
fork do
|
|
|
|
begin
|
|
|
|
read.close
|
|
|
|
exec '/usr/bin/nice',
|
|
|
|
'/usr/bin/ruby',
|
|
|
|
'-I', Pathname.new(__FILE__).dirname,
|
2011-08-24 13:12:36 +01:00
|
|
|
'-rbuild',
|
2010-11-09 12:57:41 +00:00
|
|
|
'--',
|
2011-06-21 06:57:07 -07:00
|
|
|
f.path,
|
2011-09-22 16:09:44 -07:00
|
|
|
*args.options_only
|
2010-11-09 12:57:41 +00:00
|
|
|
rescue Exception => e
|
|
|
|
Marshal.dump(e, write)
|
2009-10-26 18:13:38 +00:00
|
|
|
write.close
|
2010-11-09 12:57:41 +00:00
|
|
|
exit! 1
|
2009-10-26 18:13:38 +00:00
|
|
|
end
|
|
|
|
end
|
2010-11-09 12:57:41 +00:00
|
|
|
|
|
|
|
ignore_interrupts do # the fork will receive the interrupt and marshall it back
|
|
|
|
write.close
|
|
|
|
Process.wait
|
|
|
|
data = read.read
|
|
|
|
raise Marshal.load(data) unless data.nil? or data.empty?
|
|
|
|
raise "Suspicious installation failure" unless $?.success?
|
2011-09-22 20:07:39 -07:00
|
|
|
|
|
|
|
# Write an installation receipt (a Tab) to the prefix
|
2011-11-23 12:25:00 -08:00
|
|
|
Tab.for_install(f, args).write if f.installed?
|
2010-11-09 12:57:41 +00:00
|
|
|
end
|
2009-10-26 18:13:38 +00:00
|
|
|
end
|
2011-08-23 23:30:52 +01:00
|
|
|
|
|
|
|
def link
|
2012-02-21 11:40:06 +00:00
|
|
|
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
|
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
Keg.new(f.prefix).link
|
|
|
|
rescue Exception => e
|
|
|
|
onoe "The linking 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}'"
|
|
|
|
ohai e, e.backtrace if ARGV.debug?
|
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
|
|
|
|
def fix_install_names
|
|
|
|
Keg.new(f.prefix).fix_install_names
|
|
|
|
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
|
|
|
|
require 'cleaner'
|
2011-08-24 01:13:15 +01:00
|
|
|
Cleaner.new f
|
2011-08-23 23:30:52 +01:00
|
|
|
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
|
|
|
|
|
2011-08-24 01:13:15 +01:00
|
|
|
def pour
|
|
|
|
HOMEBREW_CACHE.mkpath
|
2012-01-22 22:32:15 -06:00
|
|
|
downloader = CurlBottleDownloadStrategy.new f.bottle_url, f.name, f.version, nil
|
2011-08-24 01:13:15 +01:00
|
|
|
downloader.fetch
|
|
|
|
f.verify_download_integrity downloader.tarball_path, f.bottle_sha1, "SHA1"
|
|
|
|
HOMEBREW_CELLAR.cd do
|
|
|
|
downloader.stage
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
## checks
|
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
def paths
|
|
|
|
@paths ||= ENV['PATH'].split(':').map{ |p| File.expand_path p }
|
|
|
|
end
|
|
|
|
|
2011-11-16 22:44:38 +01:00
|
|
|
def in_aclocal_dirlist?
|
|
|
|
File.open("/usr/share/aclocal/dirlist") do |dirlist|
|
|
|
|
dirlist.grep(%r{^#{HOMEBREW_PREFIX}/share/aclocal$}).length > 0
|
|
|
|
end rescue false
|
|
|
|
end
|
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
def check_PATH
|
|
|
|
# warn the user if stuff was installed outside of their PATH
|
|
|
|
[f.bin, f.sbin].each do |bin|
|
2011-08-24 17:33:28 -07:00
|
|
|
if bin.directory? and bin.children.length > 0
|
2011-08-23 23:30:52 +01:00
|
|
|
bin = (HOMEBREW_PREFIX/bin.basename).realpath.to_s
|
|
|
|
unless paths.include? bin
|
|
|
|
opoo "#{bin} is not in your PATH"
|
|
|
|
puts "You can amend this by altering your ~/.bashrc file"
|
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_manpages
|
|
|
|
# Check for man pages that aren't in share/man
|
|
|
|
if (f.prefix+'man').exist?
|
2011-12-10 17:14:38 -06:00
|
|
|
opoo 'A top-level "man" directory was found.'
|
2011-08-23 23:30:52 +01:00
|
|
|
puts "Homebrew requires that man pages live under share."
|
|
|
|
puts 'This can often be fixed by passing "--mandir=#{man}" to configure.'
|
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_infopages
|
|
|
|
# Check for info pages that aren't in share/info
|
|
|
|
if (f.prefix+'info').exist?
|
2011-12-10 17:14:38 -06:00
|
|
|
opoo 'A top-level "info" directory was found.'
|
2011-08-23 23:30:52 +01:00
|
|
|
puts "Homebrew suggests that info pages live under share."
|
|
|
|
puts 'This can often be fixed by passing "--infodir=#{info}" to configure.'
|
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_jars
|
|
|
|
# Check for Jars in lib
|
2012-02-19 19:09:38 -08:00
|
|
|
return unless File.exist? f.lib
|
|
|
|
|
|
|
|
unless f.lib.children.select{|g| g.to_s =~ /\.jar$/}.empty?
|
|
|
|
opoo 'JARs were installed to "lib".'
|
|
|
|
puts "Installing JARs to \"lib\" can cause conflicts between packages."
|
|
|
|
puts "For Java software, it is typically better for the formula to"
|
|
|
|
puts "install to \"libexec\" and then symlink or wrap binaries into \"bin\"."
|
|
|
|
puts "See \"activemq\", \"jruby\", etc. for examples."
|
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_non_libraries
|
|
|
|
return unless File.exist? f.lib
|
|
|
|
|
|
|
|
valid_libraries = %w(.a .dylib .framework .la .so)
|
|
|
|
non_libraries = f.lib.children.select do |g|
|
|
|
|
next if g.directory?
|
|
|
|
extname = g.extname
|
|
|
|
(extname != ".jar") and (not valid_libraries.include? extname)
|
|
|
|
end
|
|
|
|
|
|
|
|
unless non_libraries.empty?
|
|
|
|
opoo 'Non-libraries were installed to "lib".'
|
|
|
|
puts "Installing non-libraries to \"lib\" is bad practice."
|
|
|
|
puts "The offending files are:"
|
|
|
|
puts non_libraries
|
|
|
|
@show_summary_heading = true
|
2011-08-23 23:30:52 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-02-19 19:09:38 -08:00
|
|
|
def audit_bin
|
|
|
|
return unless File.exist? f.bin
|
|
|
|
|
|
|
|
non_exes = f.bin.children.select {|g| not File.executable? g}
|
|
|
|
|
|
|
|
unless non_exes.empty?
|
|
|
|
opoo 'Non-executables were installed to "bin".'
|
|
|
|
puts "Installing non-executables to \"bin\" is bad practice."
|
|
|
|
puts "The offending files are:"
|
|
|
|
puts non_exes
|
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def audit_lib
|
|
|
|
check_jars
|
|
|
|
check_non_libraries
|
|
|
|
end
|
|
|
|
|
2011-08-23 23:30:52 +01:00
|
|
|
def check_m4
|
|
|
|
# Check for m4 files
|
2011-11-16 22:44:38 +01:00
|
|
|
if Dir[f.share+"aclocal/*.m4"].length > 0 and not in_aclocal_dirlist?
|
2011-08-23 23:30:52 +01:00
|
|
|
opoo 'm4 macros were installed to "share/aclocal".'
|
|
|
|
puts "Homebrew does not append \"#{HOMEBREW_PREFIX}/share/aclocal\""
|
|
|
|
puts "to \"/usr/share/aclocal/dirlist\". If an autoconf script you use"
|
|
|
|
puts "requires these m4 macros, you'll need to add this path manually."
|
|
|
|
@show_summary_heading = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def external_dep_check dep, type
|
|
|
|
case type
|
|
|
|
when :python then %W{/usr/bin/env python -c import\ #{dep}}
|
|
|
|
when :jruby then %W{/usr/bin/env jruby -rubygems -e require\ '#{dep}'}
|
|
|
|
when :ruby then %W{/usr/bin/env ruby -rubygems -e require\ '#{dep}'}
|
|
|
|
when :perl then %W{/usr/bin/env perl -e use\ #{dep}}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
class Formula
|
2011-09-01 10:06:28 -07:00
|
|
|
def keg_only_text
|
|
|
|
# Add indent into reason so undent won't truncate the beginnings of lines
|
|
|
|
reason = self.keg_only?.to_s.gsub(/[\n]/, "\n ")
|
|
|
|
return <<-EOS.undent
|
2011-08-23 23:30:52 +01:00
|
|
|
This formula is keg-only, so it was not symlinked into #{HOMEBREW_PREFIX}.
|
|
|
|
|
2011-09-01 10:06:28 -07:00
|
|
|
#{reason}
|
2011-08-23 23:30:52 +01: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 its lib & include paths to your build variables:
|
|
|
|
|
|
|
|
LDFLAGS -L#{lib}
|
|
|
|
CPPFLAGS -I#{include}
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_external_deps
|
|
|
|
[:ruby, :python, :perl, :jruby].each do |type|
|
|
|
|
self.external_deps[type].each do |dep|
|
|
|
|
unless quiet_system(*external_dep_check(dep, type))
|
|
|
|
raise UnsatisfiedExternalDependencyError.new(dep, type)
|
|
|
|
end
|
|
|
|
end if self.external_deps[type]
|
|
|
|
end
|
|
|
|
end
|
2009-10-26 18:13:38 +00:00
|
|
|
end
|