More modular cmd/install and FormulaInstaller
This commit is contained in:
parent
768910283a
commit
cf29569099
@ -3,26 +3,30 @@ require 'hardware'
|
|||||||
|
|
||||||
module Homebrew extend self
|
module Homebrew extend self
|
||||||
def install
|
def install
|
||||||
brew_install
|
blacklisted? ARGV.named do |msg, name|
|
||||||
end
|
abort msg
|
||||||
end
|
end unless ARGV.force?
|
||||||
|
|
||||||
def brew_install
|
install_formulae ARGV.formulae
|
||||||
############################################################ sanity checks
|
end
|
||||||
|
|
||||||
|
def check_ppc
|
||||||
case Hardware.cpu_type when :ppc, :dunno
|
case Hardware.cpu_type when :ppc, :dunno
|
||||||
abort "Sorry, Homebrew does not support your computer's CPU architecture.\n"+
|
abort "Sorry, Homebrew does not support your computer's CPU architecture.\n"+
|
||||||
"For PPC support, see: http://github.com/sceaga/homebrew/tree/powerpc"
|
"For PPC support, see: http://github.com/sceaga/homebrew/tree/powerpc"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_writable_install_location
|
||||||
raise "Cannot write to #{HOMEBREW_CELLAR}" if HOMEBREW_CELLAR.exist? and not HOMEBREW_CELLAR.writable?
|
raise "Cannot write to #{HOMEBREW_CELLAR}" if HOMEBREW_CELLAR.exist? and not HOMEBREW_CELLAR.writable?
|
||||||
raise "Cannot write to #{HOMEBREW_PREFIX}" unless HOMEBREW_PREFIX.writable?
|
raise "Cannot write to #{HOMEBREW_PREFIX}" unless HOMEBREW_PREFIX.writable?
|
||||||
|
end
|
||||||
|
|
||||||
################################################################# warnings
|
def check_cc
|
||||||
begin
|
|
||||||
if MACOS_VERSION >= 10.6
|
if MACOS_VERSION >= 10.6
|
||||||
opoo "You should upgrade to Xcode 3.2.3" if llvm_build < RECOMMENDED_LLVM
|
opoo "You should upgrade to Xcode 3.2.3" if MacOS.llvm_build_version < RECOMMENDED_LLVM
|
||||||
else
|
else
|
||||||
opoo "You should upgrade to Xcode 3.1.4" if (gcc_40_build < RECOMMENDED_GCC_40) or (gcc_42_build < RECOMMENDED_GCC_42)
|
opoo "You should upgrade to Xcode 3.1.4" if (MacOS.gcc_40_build_version < RECOMMENDED_GCC_40) or (MacOS.gcc_42_build_version < RECOMMENDED_GCC_42)
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
# the reason we don't abort is some formula don't require Xcode
|
# the reason we don't abort is some formula don't require Xcode
|
||||||
@ -30,58 +34,69 @@ def brew_install
|
|||||||
opoo "Xcode is not installed! Builds may fail!"
|
opoo "Xcode is not installed! Builds may fail!"
|
||||||
end
|
end
|
||||||
|
|
||||||
if macports_or_fink_installed?
|
def check_macports
|
||||||
opoo "It appears you have MacPorts or Fink installed."
|
if MacOS.macports_or_fink_installed?
|
||||||
puts "Software installed with MacPorts and Fink are known to cause problems."
|
opoo "It appears you have Macports or Fink installed"
|
||||||
puts "If you experience issues try uninstalling these tools."
|
puts "Software installed with other package managers causes known problems for"
|
||||||
end
|
puts "Homebrew. If formula fail to build uninstall Macports/Fink and reinstall any"
|
||||||
|
puts "affected formula."
|
||||||
################################################################# install!
|
|
||||||
installer = FormulaInstaller.new
|
|
||||||
installer.install_deps = !ARGV.include?('--ignore-dependencies')
|
|
||||||
|
|
||||||
ARGV.formulae.each do |f|
|
|
||||||
if not f.installed? or ARGV.force?
|
|
||||||
installer.install f
|
|
||||||
else
|
|
||||||
puts "Formula already installed: #{f.prefix}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def check_for_blacklisted_formula names
|
def install_formulae formulae
|
||||||
return if ARGV.force?
|
formulae = [formulae].flatten.compact
|
||||||
|
return if formulae.empty?
|
||||||
|
|
||||||
names.each do |name|
|
check_ppc
|
||||||
|
check_writable_install_location
|
||||||
|
check_cc
|
||||||
|
check_macports
|
||||||
|
|
||||||
|
formulae.each do |f|
|
||||||
|
begin
|
||||||
|
installer = FormulaInstaller.new f
|
||||||
|
installer.ignore_deps = ARGV.include? '--ignore-dependencies'
|
||||||
|
installer.go
|
||||||
|
rescue FormulaAlreadyInstalledError => e
|
||||||
|
opoo e.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def blacklisted? names
|
||||||
|
names.each do |name|
|
||||||
|
msg = blacklisted_reason name
|
||||||
|
yield msg.undent, name if msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def blacklisted_reason name
|
||||||
case name
|
case name
|
||||||
when 'tex', 'tex-live', 'texlive' then abort <<-EOS.undent
|
when 'tex', 'tex-live', 'texlive' then <<-EOS
|
||||||
Installing TeX from source is weird and gross, requires a lot of patches,
|
Installing TeX from source is weird and gross, requires a lot of patches,
|
||||||
and only builds 32-bit (and thus can't use Homebrew deps on Snow Leopard.)
|
and only builds 32-bit (and thus can't use Homebrew deps on Snow Leopard.)
|
||||||
|
|
||||||
We recommend using a MacTeX distribution:
|
We recommend using a MacTeX distribution:
|
||||||
http://www.tug.org/mactex/
|
http://www.tug.org/mactex/
|
||||||
EOS
|
EOS
|
||||||
|
when 'mercurial', 'hg' then <<-EOS
|
||||||
when 'mercurial', 'hg' then abort <<-EOS.undent
|
|
||||||
Mercurial can be install thusly:
|
Mercurial can be install thusly:
|
||||||
brew install pip && pip install mercurial
|
brew install pip && pip install mercurial
|
||||||
EOS
|
EOS
|
||||||
|
|
||||||
when 'npm' then abort <<-EOS.undent
|
when 'npm' then abort <<-EOS.undent
|
||||||
npm can be installed thusly by following the instructions at
|
npm can be installed thusly by following the instructions at
|
||||||
http://npmjs.org/
|
http://npmjs.org/
|
||||||
|
|
||||||
To do it in one line, use this command:
|
To do it in one line, use this command:
|
||||||
curl http://npmjs.org/install.sh | sudo sh
|
curl http://npmjs.org/install.sh | sudo sh
|
||||||
EOS
|
EOS
|
||||||
|
|
||||||
when 'setuptools' then abort <<-EOS.undent
|
when 'setuptools' then abort <<-EOS.undent
|
||||||
When working with a Homebrew-built Python, distribute is preferred
|
When working with a Homebrew-built Python, distribute is preferred
|
||||||
over setuptools, and can be used as the prerequisite for pip.
|
over setuptools, and can be used as the prerequisite for pip.
|
||||||
|
|
||||||
Install distribute using:
|
Install distribute using:
|
||||||
brew install distribute
|
brew install distribute
|
||||||
EOS
|
EOS
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,100 +1,57 @@
|
|||||||
|
require 'exceptions'
|
||||||
require 'formula'
|
require 'formula'
|
||||||
require 'set'
|
require 'set'
|
||||||
|
|
||||||
class FormulaInstaller
|
class FormulaInstaller
|
||||||
@@attempted = Set.new
|
attr :ignore_deps, true
|
||||||
|
|
||||||
def initialize
|
def initialize f
|
||||||
@install_deps = true
|
@f = f
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_writer :install_deps
|
# raises Homebrew::InstallationErrors in the event of install failures
|
||||||
|
def go
|
||||||
def self.expand_deps f
|
if @f.installed? and not ARGV.force?
|
||||||
deps = []
|
raise FormulaAlreadyInstalledError, @f
|
||||||
f.deps.collect do |dep|
|
else
|
||||||
dep = Formula.factory dep
|
unless ignore_deps
|
||||||
deps += expand_deps dep
|
@f.recursive_deps.each do |dep|
|
||||||
deps << dep
|
FormulaInstaller.install_formula dep unless dep.installed?
|
||||||
end
|
end
|
||||||
deps
|
FormulaInstaller.check_external_deps @f
|
||||||
end
|
|
||||||
|
|
||||||
def pyerr dep
|
|
||||||
brew_pip = ' brew install pip &&' unless Formula.factory('pip').installed?
|
|
||||||
<<-EOS.undent
|
|
||||||
Unsatisfied dependency, #{dep}
|
|
||||||
Homebrew does not provide Python dependencies, pip does:
|
|
||||||
|
|
||||||
#{brew_pip} pip install #{dep}
|
|
||||||
EOS
|
|
||||||
end
|
|
||||||
def plerr dep; <<-EOS.undent
|
|
||||||
Unsatisfied dependency, #{dep}
|
|
||||||
Homebrew does not provide Perl dependencies, cpan does:
|
|
||||||
|
|
||||||
cpan -i #{dep}
|
|
||||||
EOS
|
|
||||||
end
|
|
||||||
def rberr dep; <<-EOS.undent
|
|
||||||
Unsatisfied dependency "#{dep}"
|
|
||||||
Homebrew does not provide Ruby dependencies, rubygems does:
|
|
||||||
|
|
||||||
gem install #{dep}
|
|
||||||
EOS
|
|
||||||
end
|
|
||||||
def jrberr dep; <<-EOS.undent
|
|
||||||
Unsatisfied dependency "#{dep}"
|
|
||||||
Homebrew does not provide JRuby dependencies, rubygems does:
|
|
||||||
|
|
||||||
jruby -S gem install #{dep}
|
|
||||||
EOS
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_external_deps f
|
|
||||||
return unless f.external_deps
|
|
||||||
|
|
||||||
f.external_deps[:python].each do |dep|
|
|
||||||
raise pyerr(dep) unless quiet_system "/usr/bin/env", "python", "-c", "import #{dep}"
|
|
||||||
end
|
|
||||||
f.external_deps[:perl].each do |dep|
|
|
||||||
raise plerr(dep) unless quiet_system "/usr/bin/env", "perl", "-e", "use #{dep}"
|
|
||||||
end
|
|
||||||
f.external_deps[:ruby].each do |dep|
|
|
||||||
raise rberr(dep) unless quiet_system "/usr/bin/env", "ruby", "-rubygems", "-e", "require '#{dep}'"
|
|
||||||
end
|
|
||||||
f.external_deps[:jruby].each do |dep|
|
|
||||||
raise jrberr(dep) unless quiet_system "/usr/bin/env", "jruby", "-rubygems", "-e", "require '#{dep}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_formula_deps f
|
|
||||||
FormulaInstaller.expand_deps(f).each do |dep|
|
|
||||||
begin
|
|
||||||
install_private dep unless dep.installed?
|
|
||||||
rescue
|
|
||||||
#TODO continue if this is an optional dep
|
|
||||||
raise
|
|
||||||
end
|
end
|
||||||
|
FormulaInstaller.install_formula @f
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def install f
|
def self.check_external_deps f
|
||||||
if @install_deps
|
[:ruby, :python, :perl, :jruby].each do |type|
|
||||||
check_external_deps f
|
f.external_deps[type].each do |dep|
|
||||||
check_formula_deps f
|
unless quiet_system(*external_dep_check(dep, type))
|
||||||
|
raise UnsatisfiedExternalDependencyError.new(dep, type)
|
||||||
|
end
|
||||||
|
end if f.external_deps[type]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.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
|
||||||
install_private f
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def install_private f
|
def self.install_formula f
|
||||||
return if @@attempted.include? f.name
|
@attempted ||= Set.new
|
||||||
@@attempted << f.name
|
raise FormulaInstallationAlreadyAttemptedError, f if @attempted.include? f
|
||||||
|
@attempted << f
|
||||||
|
|
||||||
# 1. formulae can modify ENV, so we must ensure that each
|
# 1. formulae can modify ENV, so we must ensure that each
|
||||||
# installation has a pristine ENV when it starts, forking now is
|
# installation has a pristine ENV when it starts, forking now is
|
||||||
# the easiest way to do this
|
# the easiest way to do this
|
||||||
# 2. formulae have access to __END__ the only way to allow this is
|
# 2. formulae have access to __END__ the only way to allow this is
|
||||||
# to make the formula script the executed script
|
# to make the formula script the executed script
|
||||||
@ -102,24 +59,29 @@ class FormulaInstaller
|
|||||||
# I'm guessing this is not a good way to do this, but I'm no UNIX guru
|
# 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
|
ENV['HOMEBREW_ERROR_PIPE'] = write.to_i.to_s
|
||||||
|
|
||||||
begin #watch_out_for_spill do #disabled temporarily, see Issue #124
|
fork do
|
||||||
fork do
|
begin
|
||||||
begin
|
read.close
|
||||||
read.close
|
exec '/usr/bin/nice',
|
||||||
exec '/usr/bin/nice', '/usr/bin/ruby', '-I', File.dirname(__FILE__), '-rinstall', f.path, '--', *ARGV.options_only
|
'/usr/bin/ruby',
|
||||||
rescue => e
|
'-I', Pathname.new(__FILE__).dirname,
|
||||||
Marshal.dump(e, write)
|
'-rinstall',
|
||||||
write.close
|
f.path,
|
||||||
exit! 1
|
'--',
|
||||||
end
|
*ARGV.options_only
|
||||||
end
|
rescue Exception => e
|
||||||
ignore_interrupts do # because child proc will get it and marshall it back
|
Marshal.dump(e, write)
|
||||||
write.close
|
write.close
|
||||||
Process.wait
|
exit! 1
|
||||||
data = read.read
|
|
||||||
raise Marshal.load(data) unless data.nil? or data.empty?
|
|
||||||
raise "Suspicious installation failure" unless $?.success?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user