aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Howell2011-08-23 23:30:52 +0100
committerMax Howell2011-08-24 22:30:43 +0100
commit19e387d92e2741fa23cc6fde4743b1b8b28e0fa3 (patch)
tree152d301d9ffe3d9cb8477645d928b6037d9ea6a9
parent3a0cf31ed0ffb33e5ae2c0d996902a983752589d (diff)
downloadbrew-19e387d92e2741fa23cc6fde4743b1b8b28e0fa3.tar.bz2
`brew upgrade`
Consequence: you can no longer install when something is already installed, you must upgrade it. This doesn't apply if the formula in question was unlinked. You can still --force installs though. Rationale: the old way of installing over the top would leave symlinks to multiple versions in /usr/local if the old version had a file the newer version didn't. The new upgrade command handles everything properly.
-rwxr-xr-xLibrary/Contributions/examples/brew-upgrade.rb7
-rw-r--r--Library/Homebrew/cmd/help.rb2
-rw-r--r--Library/Homebrew/cmd/install.rb26
-rw-r--r--Library/Homebrew/cmd/upgrade.rb35
-rw-r--r--Library/Homebrew/formula_installer.rb235
-rwxr-xr-xLibrary/Homebrew/install.rb207
-rw-r--r--Library/Homebrew/test/test_external_deps.rb4
7 files changed, 295 insertions, 221 deletions
diff --git a/Library/Contributions/examples/brew-upgrade.rb b/Library/Contributions/examples/brew-upgrade.rb
deleted file mode 100755
index 3ffb570ac..000000000
--- a/Library/Contributions/examples/brew-upgrade.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# Updates all outdated brews
-# See: http://github.com/mxcl/homebrew/issues/issue/1324
-
-require 'cmd/outdated'
-require 'cmd/install'
-
-Homebrew.install_formulae Homebrew.outdated_brews.map{ |_keg, name, _version| Formula.factory name }
diff --git a/Library/Homebrew/cmd/help.rb b/Library/Homebrew/cmd/help.rb
index 3e7cc75b9..2d55d0427 100644
--- a/Library/Homebrew/cmd/help.rb
+++ b/Library/Homebrew/cmd/help.rb
@@ -5,7 +5,7 @@ Example usage:
brew search [foo]
brew list [FORMULA...]
brew update
- brew outdated
+ brew upgrade [FORMULA...]
brew [info | home] [FORMULA...]
Troubleshooting:
diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb
index b7f9e7cae..4b2711994 100644
--- a/Library/Homebrew/cmd/install.rb
+++ b/Library/Homebrew/cmd/install.rb
@@ -57,22 +57,26 @@ module Homebrew extend self
end
end
- def install_formulae formulae
- formulae = [formulae].flatten.compact
- return if formulae.empty?
-
+ def perform_preinstall_checks
check_ppc
check_writable_install_location
check_cc
check_macports
+ end
- formulae.each do |f|
- begin
- installer = FormulaInstaller.new f
- installer.ignore_deps = ARGV.include? '--ignore-dependencies'
- installer.go
- rescue FormulaAlreadyInstalledError => e
- opoo e.message
+ def install_formulae formulae
+ formulae = [formulae].flatten.compact
+ unless formulae.empty?
+ perform_preinstall_checks
+ formulae.each do |f|
+ begin
+ fi = FormulaInstaller.new(f)
+ fi.install
+ fi.caveats
+ fi.finish
+ rescue FormulaAlreadyInstalledError => e
+ opoo e.message
+ end
end
end
end
diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb
new file mode 100644
index 000000000..f86e8a871
--- /dev/null
+++ b/Library/Homebrew/cmd/upgrade.rb
@@ -0,0 +1,35 @@
+require 'cmd/outdated'
+require 'cmd/install'
+
+class Fixnum
+ def plural_s
+ if self > 1 then "s" else "" end
+ end
+end
+
+module Homebrew extend self
+ def upgrade
+ Homebrew.perform_preinstall_checks
+
+ outdated = if ARGV.named.empty?
+ Homebrew.outdated_brews
+ else
+ ARGV.formulae.map{ |f| [f.prefix.parent, f.name, f.version] }
+ end
+
+ if outdated.count > 1
+ oh1 "Upgrading #{outdated.count} outdated package#{outdated.count.plural_s}, with result:"
+ puts outdated.map{ |_, name, version| "#{name} #{version}" } * ", "
+ end
+
+ outdated.each do |rack, name, version|
+ installer = FormulaInstaller.new(Formula.factory(name))
+ installer.show_header = false
+ oh1 "Upgrading #{name}"
+ installer.install
+ Keg.new("#{rack}/#{version}").unlink
+ installer.caveats
+ installer.finish # includes link step
+ end
+ end
+end
diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb
index 57f93fdee..5c2e61590 100644
--- a/Library/Homebrew/formula_installer.rb
+++ b/Library/Homebrew/formula_installer.rb
@@ -1,63 +1,96 @@
require 'exceptions'
require 'formula'
+require 'keg'
require 'set'
class FormulaInstaller
+ attr :f
+ attr :show_summary_heading, true
attr :ignore_deps, true
+ attr :install_bottle, true
+ attr :show_header, true
- def initialize f
- @f = f
+ def initialize ff
+ @f = ff
+ @show_header = true
+ @ignore_deps = ARGV.include? '--ignore-dependencies' || ARGV.interactive?
+ @install_bottle = ff.pourable? #TODO better
end
- # raises Homebrew::InstallationErrors in the event of install failures
- def go
- if @f.installed? and not ARGV.force?
- raise FormulaAlreadyInstalledError, @f
- end
+ def install
+ raise FormulaAlreadyInstalledError, f if f.installed? and not ARGV.force?
unless ignore_deps
- needed_deps = @f.recursive_deps.reject {|d| d.installed?}
+ f.check_external_deps
+
+ needed_deps = f.recursive_deps.reject{ |d| d.installed? }
unless needed_deps.empty?
- puts "Also installing dependencies: "+needed_deps*", "
needed_deps.each do |dep|
- FormulaInstaller.install_formula dep
+ fi = FormulaInstaller.new(dep)
+ fi.ignore_deps = true
+ fi.show_header = false
+ oh1 "Installing #{f} dependency: #{dep}"
+ fi.install
+ fi.caveats
+ fi.finish
end
- end
- begin
- FormulaInstaller.check_external_deps @f
- rescue UnsatisfiedExternalDependencyError => e
- onoe e.message
- exit! 1
+
+ # now show header as all the deps stuff has clouded the original issue
+ show_header = true
end
end
- FormulaInstaller.install_formula @f
- end
- def self.check_external_deps f
- [:ruby, :python, :perl, :jruby].each do |type|
- f.external_deps[type].each do |dep|
- unless quiet_system(*external_dep_check(dep, type))
- raise UnsatisfiedExternalDependencyError.new(dep, type)
- end
- end if f.external_deps[type]
+ 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
end
+
+ raise "Nothing was installed to #{f.prefix}" unless f.installed?
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}}
+ def caveats
+ if f.caveats
+ ohai "Caveats", f.caveats
+ @show_summary_heading = true
end
+ if f.keg_only?
+ ohai 'Caveats', f.keg_only_text
+ @show_summary_heading = true
+ else
+ check_PATH
+ check_manpages
+ check_infopages
+ check_jars
+ check_m4
+ end
+ end
+
+ def finish
+ ohai 'Finishing up' if ARGV.verbose?
+
+ link unless f.keg_only?
+ 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
end
- private
+ def build_time
+ @build_time ||= Time.now - @start_time unless install_bottle or ARGV.interactive? or @start_time.nil?
+ end
- def self.install_formula f
- @attempted ||= Set.new
- raise FormulaInstallationAlreadyAttemptedError, f if @attempted.include? f
- @attempted << f
+ def build
+ @start_time = Time.now
# 1. formulae can modify ENV, so we must ensure that each
# installation has a pristine ENV when it starts, forking now is
@@ -93,4 +126,134 @@ class FormulaInstaller
raise "Suspicious installation failure" unless $?.success?
end
end
+
+ def link
+ 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'
+ Cleaner.new f if not f.pourable?
+ 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
+
+ def paths
+ @paths ||= ENV['PATH'].split(':').map{ |p| File.expand_path p }
+ end
+
+ def check_PATH
+ # warn the user if stuff was installed outside of their PATH
+ [f.bin, f.sbin].each do |bin|
+ if bin.directory? and bin.children.count > 0
+ 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?
+ opoo 'A top-level "man" folder was found.'
+ 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?
+ opoo 'A top-level "info" folder was found.'
+ 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
+ if 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
+ end
+
+ def check_m4
+ # Check for m4 files
+ if Dir[f.share+"aclocal/*.m4"].length > 0
+ 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
+ def keg_only_text; <<-EOS.undent
+ This formula is keg-only, so it was not symlinked into #{HOMEBREW_PREFIX}.
+
+ #{self.keg_only?}
+
+ 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
end
diff --git a/Library/Homebrew/install.rb b/Library/Homebrew/install.rb
index e92e80e08..9844ef401 100755
--- a/Library/Homebrew/install.rb
+++ b/Library/Homebrew/install.rb
@@ -1,35 +1,28 @@
#!/usr/bin/ruby
-require 'global'
-
-def text_for_keg_only_formula f
- <<-EOS
-This formula is keg-only, so it was not symlinked into #{HOMEBREW_PREFIX}.
-#{f.keg_only?}
+# This script is called 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
-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:
+ORIGINAL_PATHS = ENV['PATH'].split(':').map{ |p| File.expand_path p }
- LDFLAGS: -L#{f.lib}
- CPPFLAGS: -I#{f.include}
- EOS
-end
+require 'global'
-# I like this little at all, but see no alternative seeing as the formula
-# rb file has to be the running script to allow it to use __END__ and DATA
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.
+
begin
raise $! if $! # an exception was already thrown when parsing the formula
require 'extend/ENV'
- require 'fileutils'
require 'hardware'
require 'keg'
- require 'compatibility'
ENV.extend(HomebrewEnvExtension)
ENV.setup_build_environment
+ # we must do this or tools like pkg-config won't get found by configure scripts etc.
+ ENV.prepend 'PATH', "#{HOMEBREW_PREFIX}/bin", ':' unless ORIGINAL_PATHS.include? "#{HOMEBREW_PREFIX}/bin"
install(Formula.factory($0))
rescue Exception => e
@@ -46,15 +39,7 @@ at_exit do
end
end
-ORIGINAL_PATHS = ENV['PATH'].split(':').map{ |p| File.expand_path p }
-HOMEBREW_BIN = (HOMEBREW_PREFIX+'bin').to_s
-
def install f
- show_summary_heading = false
-
- # we must do this or tools like pkg-config won't get found by configure scripts etc.
- ENV.prepend 'PATH', HOMEBREW_BIN, ':' unless ORIGINAL_PATHS.include? HOMEBREW_BIN
-
f.deps.uniq.each do |dep|
dep = Formula.factory dep
if dep.keg_only?
@@ -65,149 +50,43 @@ def install f
end
end
- build_time = nil
- begin
- f.brew do
- if ARGV.flag? '--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'
- system "git init"
- system "git add -A"
- puts "This folder 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
- nil
- elsif ARGV.include? '--help'
- system './configure --help'
- exit $?
- else
- f.prefix.mkpath
- beginning=Time.now
- f.install if not f.pourable?
- FORMULA_META_FILES.each do |filename|
- next if File.directory? filename
- target_file = filename
- target_file = "#{filename}.txt" if File.exists? "#{filename}.txt"
- # Some software symlinks these files (see help2man.rb)
- target_file = Pathname.new(target_file).resolved_path
- f.prefix.install target_file => filename rescue nil
- (f.prefix+file).chmod 0644 rescue nil
- end
- build_time = Time.now-beginning if not f.pourable?
+ f.brew do
+ if ARGV.flag? '--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'
+ system "git init"
+ system "git add -A"
+ puts "This folder is now a git repo. Make your changes and then use:"
+ puts " git diff | pbcopy"
+ puts "to copy the diff to the clipboard."
end
- end
- rescue Exception
- if f.prefix.directory?
- f.prefix.rmtree
- f.prefix.parent.rmdir_if_possible
- end
- raise
- end
- if f.caveats
- ohai "Caveats", f.caveats
- show_summary_heading = true
- end
-
- ohai 'Finishing up' if ARGV.verbose?
-
- keg = Keg.new f.prefix
-
- begin
- require 'cleaner'
- Cleaner.new f if not f.pourable?
- 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
-
- raise "Nothing was installed to #{f.prefix}" unless f.installed?
-
- if f.keg_only?
- ohai 'Caveats', text_for_keg_only_formula(f)
- show_summary_heading = true
- else
- # warn the user if stuff was installed outside of their PATH
- [f.bin, f.sbin].each do |bin|
- if bin.directory?
- bin = File.expand_path bin
- unless ORIGINAL_PATHS.include? HOMEBREW_BIN
- opoo "#{HOMEBREW_BIN} is not in your PATH"
- puts "You can amend this by altering your ~/.bashrc file"
- show_summary_heading = true
- end
- end
- end
-
- # Check for man pages that aren't in share/man
- if (f.prefix+'man').exist?
- opoo 'A top-level "man" folder was found.'
- 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
-
- # Check for info pages that aren't in share/info
- if (f.prefix+'info').exist?
- opoo 'A top-level "info" folder was found.'
- 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
-
- # Check for Jars in lib
- if 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
+ interactive_shell f
+ nil
+ elsif ARGV.include? '--help'
+ system './configure --help'
+ exit $?
+ else
+ f.prefix.mkpath
+ f.install
+ FORMULA_META_FILES.each do |filename|
+ next if File.directory? filename
+ target_file = filename
+ target_file = "#{filename}.txt" if File.exists? "#{filename}.txt"
+ # Some software symlinks these files (see help2man.rb)
+ target_file = Pathname.new(target_file).resolved_path
+ f.prefix.install target_file => filename rescue nil
+ (f.prefix+file).chmod 0644 rescue nil
end
end
-
- # Check for m4 files
- if Dir[f.share+"aclocal/*.m4"].length > 0
- 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
-
- # link from Cellar to Prefix
- begin
- keg.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
end
-
- begin
- keg.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
+rescue Exception
+ if f.prefix.directory?
+ f.prefix.rmtree
+ f.prefix.parent.rmdir_if_possible
end
-
- 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
+ raise
end
diff --git a/Library/Homebrew/test/test_external_deps.rb b/Library/Homebrew/test/test_external_deps.rb
index 30b3540d9..052617712 100644
--- a/Library/Homebrew/test/test_external_deps.rb
+++ b/Library/Homebrew/test/test_external_deps.rb
@@ -77,13 +77,13 @@ end
class ExternalDepsTests < Test::Unit::TestCase
def check_deps_fail f
assert_raises(UnsatisfiedExternalDependencyError) do
- FormulaInstaller.check_external_deps f.new
+ f.new.check_external_deps
end
end
def check_deps_pass f
assert_nothing_raised do
- FormulaInstaller.check_external_deps f.new
+ f.new.check_external_deps
end
end