brew/bin/brew
Adam Vandenberg 0d73cbbc53 Add brew --cellar command.
The Cellar may not actually be "`brew --prefix`/Cellar".
We support the Cellar existing only in the repo but
not linked into the prefix, for installs that aren't
directly in /usr/local (or other chosen prefix.)
2010-02-18 13:29:28 -08:00

374 lines
11 KiB
Ruby
Executable File

#!/usr/bin/ruby
# -*- coding: utf-8 -*-
# Many Pathname operations use getwd when they shouldn't, and then throw
# odd exceptions. Reduce our support burden by showing a user-friendly error.
Dir.getwd rescue abort "The current working directory doesn't exist, cannot proceed."
HOMEBREW_BREW_FILE = ENV['HOMEBREW_BREW_FILE'] = File.expand_path(__FILE__)
require 'pathname'
HOMEBREW_LIBRARY_PATH = (Pathname.new(__FILE__).realpath.dirname.parent+"Library"+"Homebrew").to_s
$:.unshift(HOMEBREW_LIBRARY_PATH)
require 'global'
case ARGV.first
when '--cache'
puts HOMEBREW_CACHE
exit 0
when '-h', '--help', '--usage', '-?', 'help', nil
puts ARGV.usage
exit 0
when '--version'
puts HOMEBREW_VERSION
exit 0
when '-v'
if ARGV.length > 1
puts "Homebrew #{HOMEBREW_VERSION}"
# continue in verbose mode
ARGV << ARGV.shift
else
puts HOMEBREW_VERSION
exit 0
end
end
case HOMEBREW_PREFIX.to_s when '/', '/usr'
# it may work, but I only see pain this route and don't want to support it
abort "Cowardly refusing to continue at this prefix: #{HOMEBREW_PREFIX}"
end
if MACOS_VERSION < 10.5
abort "Homebrew requires Leopard or higher, but you could fork it and fix that..."
end
def dump_config
require 'hardware'
bits = Hardware.bits
cores = Hardware.cores_as_words
llvm = llvm_build
gcc = gcc_build
sha = `cd #{HOMEBREW_REPOSITORY} && git rev-parse --verify HEAD 2> /dev/null`.chomp
kernel_arch = `uname -m`.chomp
puts <<-EOS
HOMEBREW_VERSION: #{HOMEBREW_VERSION}
HEAD: #{sha}
HOMEBREW_PREFIX: #{HOMEBREW_PREFIX}
HOMEBREW_CELLAR: #{HOMEBREW_CELLAR}
HOMEBREW_CACHE: #{HOMEBREW_CACHE}
HOMEBREW_REPOSITORY: #{HOMEBREW_REPOSITORY}
HOMEBREW_LIBRARY_PATH: #{HOMEBREW_LIBRARY_PATH}
Hardware: #{cores}-core #{bits}-bit #{Hardware.intel_family}
OS X: #{MACOS_FULL_VERSION}
Kernel Architecture: #{kernel_arch}
Ruby: #{RUBY_VERSION}-#{RUBY_PATCHLEVEL}
GCC-4.2: #{gcc ? "build #{gcc}" : "N/A"} (5577 or newer recommended)
LLVM: #{llvm ? "build #{llvm}" : "N/A" } #{llvm ? "(2206 or newer recommended)" : "" }
MacPorts or Fink? #{macports_or_fink_installed?}
X11 installed? #{x11_installed?}
EOS
end
begin
require 'brew.h'
case arg = ARGV.shift
when '--prefix'
puts HOMEBREW_PREFIX
when '--repository'
puts HOMEBREW_REPOSITORY
when '--cellar'
puts HOMEBREW_CELLAR
when '--config'
dump_config
when 'home', 'homepage'
if ARGV.named.empty?
exec "open", HOMEBREW_WWW
else
exec "open", *ARGV.formulae.collect {|f| f.homepage}
end
when 'ls', 'list'
if ARGV.flag? '--unbrewed'
dirs = HOMEBREW_PREFIX.children.select { |pn| pn.directory? }.collect { |pn| pn.basename.to_s }
dirs -= ['Library', 'Cellar', '.git']
Dir.chdir HOMEBREW_PREFIX
exec 'find', *dirs + %w[-type f ( ! -iname .ds_store ! -iname brew )]
elsif ARGV.named.empty?
ENV['CLICOLOR']=nil
exec 'ls', *ARGV.options<<HOMEBREW_CELLAR if HOMEBREW_CELLAR.exist?
elsif ARGV.verbose? or not $stdout.tty?
exec "find", *ARGV.kegs+%w[-not -type d -print]
else
ARGV.kegs.each { |keg| PrettyListing.new keg }
end
when 'search', '-S'
check_for_blacklisted_formula(ARGV.named)
require "formula"
formulae = Formulary.names with_aliases=true
if ARGV.first =~ /^\/(.*)\/$/
puts_columns formulae.grep(Regexp.new($1))
else
puts_columns formulae.grep(/.*#{ARGV.first}.*/)
end
when 'edit'
if ARGV.named.empty?
# EDITOR isn't a good fit here, we need a GUI client that actually has
# a UI for projects, so apologies if this wasn't what you expected,
# please improve it! :)
exec 'mate', *Dir["#{HOMEBREW_REPOSITORY}/Library/*"]<<
"#{HOMEBREW_REPOSITORY}/bin/brew"<<
"#{HOMEBREW_REPOSITORY}/README.md"
else
# we don't use ARGV.formulae as that will throw if the file doesn't parse
paths = ARGV.named.collect do |name|
unless File.exist? path = "#{HOMEBREW_REPOSITORY}/Library/Formula/#{name}.rb"
require 'formula'
raise FormulaUnavailableError, name
else
path
end
end
exec_editor *paths
end
when 'up', 'update'
if system "/usr/bin/which -s git"
require 'update'
updater = RefreshBrew.new
old_revision = updater.current_revision
unless updater.update_from_masterbrew!
puts "Already up-to-date."
else
puts "Updated Homebrew from #{old_revision[0,8]} to #{updater.current_revision[0,8]}."
if updater.pending_formulae_changes?
ohai "The following formulae were updated:"
puts_columns updater.updated_formulae
else
puts "No formulae were updated." unless updater.pending_formulae_changes?
end
end
else
abort "Please `brew install git' first."
end
when 'ln', 'link'
ARGV.kegs.each {|keg| puts "#{keg.link} links created for #{keg}"}
when 'unlink'
ARGV.kegs.each {|keg| puts "#{keg.unlink} links removed for #{keg}"}
when 'rm', 'uninstall', 'remove'
ARGV.kegs.each do |keg|
puts "Uninstalling #{keg}..."
keg.unlink
keg.uninstall
end
when 'prune'
prune
# 'make' supported until 0.7 for historic reasons
when 'mk', 'make'
opoo "`brew make' has changed to `brew create'"
puts "This is because make can be confused with the `make' tool."
puts "brew make will continue working until Homebrew 0.7"
exec __FILE__, "create", *ARGV
when 'create'
if ARGV.include? '--macports'
exec "open", "http://www.macports.org/ports.php?by=name&substr=#{ARGV.next}"
elsif ARGV.named.empty?
raise UsageError
else
exec_editor *ARGV.named.collect {|name| make name}
end
when 'diy', 'configure'
puts diy
when 'info', 'abv'
if ARGV.named.empty?
if ARGV.include? "--all"
require 'formula'
Formulary.names.each do |name|
info name
puts '---'
end
else
puts `ls #{HOMEBREW_CELLAR} | wc -l`.strip+" kegs, "+HOMEBREW_CELLAR.abv
end
elsif ARGV[0][0..6] == 'http://'
puts Pathname.new(ARGV.shift).version
else
ARGV.named.each {|name| info name}
end
when 'cleanup'
if ARGV.named.empty?
require 'formula'
HOMEBREW_CELLAR.children.each do |rack|
begin
cleanup(rack.basename.to_s) if rack.directory?
rescue FormulaUnavailableError => e
opoo "Formula not found for #{e.name}"
end
end
prune # seems like a good time to do some additional cleanup
else
ARGV.named.each { |name| cleanup name}
end
when 'install'
check_for_blacklisted_formula(ARGV.named)
require 'formula_installer'
require 'hardware'
############################################################ sanity checks
case Hardware.cpu_type when :ppc, :dunno
abort "Sorry, Homebrew does not support your computer's CPU architecture."
end
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?
################################################################# warnings
begin
if MACOS_VERSION >= 10.6
opoo "You should upgrade to Xcode 3.2.1" if llvm_build < 2206
else
opoo "You should upgrade to Xcode 3.1.4" if gcc_build < 5577
end
rescue
# the reason we don't abort is some formula don't require Xcode
# TODO allow formula to declare themselves as "not needing Xcode"
opoo "Xcode is not installed! Builds may fail!"
end
if macports_or_fink_installed?
opoo "It appears you have Macports or Fink installed"
puts "Although, unlikely, this can break builds or cause obscure runtime issues."
puts "If you experience problems try uninstalling these tools."
end
################################################################# 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
when 'log'
Dir.chdir HOMEBREW_REPOSITORY
exec "git", "log", ARGV.formulae.first.path, *ARGV.options
when 'uses'
# For each formula given, show which other formulas depend on it.
# We only go one level up, direct dependencies.
require 'formula'
require 'utils'
deps = Formula.get_used_by
ARGV.formulae.each do |f|
name = f.name
our_deps = deps[name]
if our_deps == nil
puts "#{name} is not a dependency."
else
puts "#{name} is a dependency for #{our_deps.join(', ')}."
end
end
when 'deps'
require 'formula'
ARGV.formulae.each do |f|
name = f.name
our_deps = []
checked = {}
to_check = [name]
until to_check.empty?
item = to_check.pop
checked[item] = true
formula = Formulary.read item
next if formula == nil || formula.deps == nil || formula.deps.empty?
our_deps.push(*formula.deps)
to_check.push(*formula.deps.select{|g| !checked[g]})
end
our_deps.uniq!
if our_deps.empty?
puts "#{name} has no dependencies."
else
our_deps.sort!
puts "#{name} depends on #{our_deps.join(", ")}"
end
end
when 'branch', 'checkout', 'pull', 'push', 'reset'
onoe "Unknown command: #{arg} (did you mean 'git #{arg}'?)"
when 'cat'
Dir.chdir HOMEBREW_REPOSITORY
exec "cat", ARGV.formulae.first.path, *ARGV.options
else
onoe "Unknown command: #{arg}"
end
rescue FormulaUnspecifiedError
abort "This command requires a formula argument"
rescue KegUnspecifiedError
abort "This command requires a keg argument"
rescue UsageError
onoe "Invalid usage"
abort ARGV.usage
rescue SystemExit
puts "Kernel.exit" if ARGV.verbose?
raise
rescue Interrupt => e
puts # seemingly a newline is typical
exit 130
rescue BuildError => e
e.backtrace[1] =~ %r{Library/Formula/(.+)\.rb:(\d+)}
formula_name = $1
puts "http://github.com/mxcl/homebrew/blob/master/Library/Formula/#{formula_name}.rb#L#{$2}"
ohai "Environment"
dump_config
puts "Exit status: #{e.exit_status}"
onoe e
puts PLEASE_REPORT_BUG
# this feature can be slow (depends on network conditions and if github is up)
# so ideally we'd show feedback, eg. "checking for existin issues..." and
# then replace that string with the following when the github api returns
issues = issues_for_formula(formula_name)
puts "These existing issues may help you:", *issues unless issues.empty?
exit 1
rescue RuntimeError, SystemCallError => e
onoe e
puts e.backtrace if ARGV.debug?
exit 1
rescue Exception => e
onoe e
puts PLEASE_REPORT_BUG
puts e.backtrace
exit 1
end