Refactor the brew command into one file per command

The code was sucking. To the extent that maintenance was hard. It's a lot
easier to work with code that is sensibly split at sensible boundaries. So
now it is more like that.

But the refactor is minimal. Because we don't want you to have more merge
hell than absolutely necessary.

If you merge you will need to pay attention to brew.h.rb (as it is deleted)
and bin/brew (as command logic is gone). It will be painful, but you will just
have to help git out by moving any changes around manually.

Note compatibility.rb. It ensures that any function renames or removals don't
break anything. We're pretty serious about backwards compatibility. And that's
because we encourage you to hack around with the innards. And we couldn't do
that if we would then just make stuff disappear behind your back.
This commit is contained in:
Max Howell 2010-09-11 20:22:54 +01:00 committed by Adam Vandenberg
parent 97f654701d
commit 768910283a
48 changed files with 1284 additions and 1238 deletions

View File

@ -1,21 +1,18 @@
require "formula"
require 'formula_installer'
require "cmd/outdated"
def main
# Names of outdated brews; they count as installed.
outdated = outdated_brews.collect {|b| b[1]}
outdated = Homebrew.outdated_brews.collect{ |b| b[1] }
HOMEBREW_CELLAR.subdirs.each do |keg|
next unless keg.subdirs
if ((f = Formula.factory(keg.basename.to_s)).installed? rescue false)
f_deps = FormulaInstaller.expand_deps(f).collect{|g| g.name}.uniq
next if f_deps.empty?
missing_deps = f_deps.reject do |dep_name|
HOMEBREW_CELLAR.subdirs.each do |rack|
f = Formula.factory rack.basename.to_s rescue nil
if f and f.installed?
missing_deps = f.recursive_deps.map{ |g| g.name }.uniq.reject do |dep_name|
Formula.factory(dep_name).installed? or outdated.include?(dep_name)
end
puts "#{f.name}: #{missing_deps.join(', ')}" unless missing_deps.empty?
puts "#{f.name}: #{missing_deps * ', '}" unless missing_deps.empty?
end
end
end

View File

@ -82,7 +82,6 @@ get '/' do
end
get '/search' do
require 'brew.h'
q = params['q']
results = search_brews(q)

View File

@ -1,11 +1,7 @@
# Updates all outdated brews
# See: http://github.com/mxcl/homebrew/issues/issue/1324
# patch ARGV to use all of the outdated packages as the names passed in
module HomebrewArgvExtension
def formulae
@formulae = outdated_brews.map {|_keg, name, _version| Formula.factory name}
end
end
require 'cmd/outdated'
require 'cmd/install'
brew_install
Homebrew.install_formulae Homebrew.outdated_brews.map{ |_keg, name, _version| Formula.factory name }

View File

@ -1,199 +0,0 @@
# Vendored from Rucola: http://github.com/alloy/rucola/tree/master
#
# Copyright (c) 2007, 2008, 2009 Eloy Duran <eloy.de.enige@gmail.com>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
begin
require 'osx/cocoa'
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
module Rucola
class FSEvents
class FSEvent
attr_reader :fsevents_object
attr_reader :id
attr_reader :path
def initialize(fsevents_object, id, path)
@fsevents_object, @id, @path = fsevents_object, id, path
end
# Returns an array of the files/dirs in the path that the event occurred in.
# The files are sorted by the modification time, the first entry is the last modified file.
def files
Dir.glob("#{File.expand_path(path)}/*").sort_by {|f| File.mtime(f) }.reverse
end
# Returns the last modified file in the path that the event occurred in.
def last_modified_file
files.first
end
end
class StreamError < StandardError; end
attr_reader :paths
attr_reader :stream
attr_accessor :allocator
attr_accessor :context
attr_accessor :since
attr_accessor :latency
attr_accessor :flags
# Initializes a new FSEvents `watchdog` object and starts watching the directories you specify for events. The
# block is used as a handler for events, which are passed as the block's argument. This method is the easiest
# way to start watching some directories if you don't care about the details of setting up the event stream.
#
# Rucola::FSEvents.start_watching('/tmp') do |events|
# events.each { |event| log.debug("#{event.files.inspect} were changed.") }
# end
#
# Rucola::FSEvents.start_watching('/var/log/system.log', '/var/log/secure.log', :since => last_id, :latency => 5) do
# Growl.notify("Something was added to your log files!")
# end
#
# Note that the method also returns the FSEvents object. This enables you to control the event stream if you want to.
#
# fsevents = Rucola::FSEvents.start_watching('/Volumes') do |events|
# events.each { |event| Growl.notify("Volume changes: #{event.files.to_sentence}") }
# end
# fsevents.stop
def self.start_watching(*params, &block)
fsevents = new(*params, &block)
fsevents.create_stream
fsevents.start
fsevents
end
# Creates a new FSEvents `watchdog` object. You can specify a list of paths to watch and options to control the
# behaviour of the watchdog. The block you pass serves as a callback when an event is generated on one of the
# specified paths.
#
# fsevents = FSEvents.new('/etc/passwd') { Mailer.send_mail("Someone touched the password file!") }
# fsevents.create_stream
# fsevents.start
#
# fsevents = FSEvents.new('/home/upload', :since => UploadWatcher.last_event_id) do |events|
# events.each do |event|
# UploadWatcher.last_event_id = event.id
# event.files.each do |file|
# UploadWatcher.logfile.append("#{file} was changed")
# end
# end
# end
#
# *:since: The service will report events that have happened after the supplied event ID. Never use 0 because that
# will cause every fsevent since the "beginning of time" to be reported. Use OSX::KFSEventStreamEventIdSinceNow
# if you want to receive events that have happened after this call. (Default: OSX::KFSEventStreamEventIdSinceNow).
# You can find the ID's passed with :since in the events passed to your block.
# *:latency: Number of seconds to wait until an FSEvent is reported, this allows the service to bundle events. (Default: 0.0)
#
# Please refer to the Cocoa documentation for the rest of the options.
def initialize(*params, &block)
raise ArgumentError, 'No callback block was specified.' unless block_given?
options = params.last.kind_of?(Hash) ? params.pop : {}
@paths = params.flatten
paths.each { |path| raise ArgumentError, "The specified path (#{path}) does not exist." unless File.exist?(path) }
@allocator = options[:allocator] || OSX::KCFAllocatorDefault
@context = options[:context] || nil
@since = options[:since] || OSX::KFSEventStreamEventIdSinceNow
@latency = options[:latency] || 0.0
@flags = options[:flags] || 0
@stream = options[:stream] || nil
@user_callback = block
@callback = Proc.new do |stream, client_callback_info, number_of_events, paths_pointer, event_flags, event_ids|
paths_pointer.regard_as('*')
events = []
number_of_events.times {|i| events << Rucola::FSEvents::FSEvent.new(self, event_ids[i], paths_pointer[i]) }
@user_callback.call(events)
end
end
# Create the stream.
# Raises a Rucola::FSEvents::StreamError if the stream could not be created.
def create_stream
@stream = OSX.FSEventStreamCreate(@allocator, @callback, @context, @paths, @since, @latency, @flags)
raise(StreamError, 'Unable to create FSEvents stream.') unless @stream
OSX.FSEventStreamScheduleWithRunLoop(@stream, OSX.CFRunLoopGetCurrent, OSX::KCFRunLoopDefaultMode)
end
# Start the stream.
# Raises a Rucola::FSEvents::StreamError if the stream could not be started.
def start
raise(StreamError, 'Unable to start FSEvents stream.') unless OSX.FSEventStreamStart(@stream)
end
# Stop the stream.
# You can resume it by calling `start` again.
def stop
OSX.FSEventStreamStop(@stream)
end
end
end
# The complete BeerEvents API :)
HOMEBREW_KEEP_DRY = %w{ /System /usr /etc /sbin /bin /Applications /Library }
def watch_out_for_spill
# Disable the RubyCocoa thread hook as apparently Laurent did not apply the
# thread patches to the OS X system Ruby
ENV['RUBYCOCOA_THREAD_HOOK_DISABLE'] = 'kampai'
Thread.new { OSX.CFRunLoopRun() }
start = Time.now
dog = Rucola::FSEvents.start_watching(*HOMEBREW_KEEP_DRY) do |events|
spill = events.map { |e| e.files }.flatten
spill.reject! { |f| File.mtime(f) < start }
spill.reject! { |path| path =~ /^#{HOMEBREW_PREFIX}/ }
# irrelevent files that change a lot
spill.reject! { |path| path == "/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist-lock" }
spill.reject! { |path| path =~ %r{^(/System)?/Library/Caches/} }
unless spill.empty?
opoo "Detected installation of files outside the Homebrew prefix:"
puts *spill
end
end
yield
ensure
dog.stop
end
rescue LoadError => e
onoe "RubyCocoa could not be loaded, therefore checking for spill is disabled."
puts "When using a custom Ruby installation, you'll need to install RubyCocoa."
puts "If this is not the case, see if the following ticket applies, or create one."
puts " http://github.com/mxcl/homebrew/issues#issue/37"
if ARGV.verbose?
onoe e.message
puts e.backtrace
end
def watch_out_for_spill
yield
end
end

View File

@ -1,600 +0,0 @@
FORMULA_META_FILES = %w[README README.md ChangeLog COPYING LICENSE LICENCE COPYRIGHT AUTHORS]
PLEASE_REPORT_BUG = "#{Tty.white}Please follow the instructions to report this bug at: #{Tty.em}\nhttps://github.com/mxcl/homebrew/wiki/new-issue#{Tty.reset}"
def check_for_blacklisted_formula names
return if ARGV.force?
names.each do |name|
case name
when 'tex', 'tex-live', 'texlive' then abort <<-EOS.undent
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.)
We recommend using a MacTeX distribution:
http://www.tug.org/mactex/
EOS
when 'mercurial', 'hg' then abort <<-EOS.undent
Mercurial can be install thusly:
brew install pip && pip install mercurial
EOS
when 'npm' then abort <<-EOS.undent
npm can be installed thusly by following the instructions at
http://npmjs.org/
To do it in one line, use this command:
curl http://npmjs.org/install.sh | sh
EOS
when 'setuptools' then abort <<-EOS.undent
When working with a Homebrew-built Python, distribute is preferred
over setuptools, and can be used as the prerequisite for pip.
Install distribute using:
brew install distribute
EOS
end
end
end
def __make url, name
require 'formula'
require 'digest'
require 'erb'
path = Formula.path(name)
raise "#{path} already exists" if path.exist?
if Formula.aliases.include? name and not ARGV.force?
realname = Formula.resolve_alias(name)
raise <<-EOS.undent
"#{name}" is an alias for formula "#{realname}".
Please check that you are not creating a duplicate.
To force creation use --force.
EOS
end
if ARGV.include? '--cmake'
mode = :cmake
elsif ARGV.include? '--autotools'
mode = :autotools
else
mode = nil
end
version = Pathname.new(url).version
if version.nil?
opoo "Version cannot be determined from URL."
puts "You'll need to add an explicit 'version' to the formula."
else
puts "Version detected as #{version}."
end
md5 = ''
if ARGV.include? "--cache" and version != nil
strategy = detect_download_strategy url
if strategy == CurlDownloadStrategy
d = strategy.new url, name, version, nil
the_tarball = d.fetch
md5 = the_tarball.md5
puts "MD5 is #{md5}"
else
puts "--cache requested, but we can only cache formulas that use Curl."
end
end
formula_template = <<-EOS
require 'formula'
class #{Formula.class_s name} <Formula
url '#{url}'
homepage ''
md5 '#{md5}'
<% if mode == :cmake %>
depends_on 'cmake'
<% elsif mode == nil %>
# depends_on 'cmake'
<% end %>
def install
<% if mode == :cmake %>
system "cmake . \#{std_cmake_parameters}"
<% elsif mode == :autotools %>
system "./configure", "--disable-debug", "--disable-dependency-tracking",
"--prefix=\#{prefix}"
<% else %>
system "./configure", "--disable-debug", "--disable-dependency-tracking",
"--prefix=\#{prefix}"
# system "cmake . \#{std_cmake_parameters}"
<% end %>
system "make install"
end
end
EOS
path.write(ERB.new(formula_template, nil, '>').result(binding))
return path
end
def make url
path = Pathname.new url
/(.*?)[-_.]?#{path.version}/.match path.basename
unless $1.to_s.empty?
name = $1
else
print "Formula name [#{path.stem}]: "
gots = $stdin.gets.chomp
if gots.empty?
name = path.stem
else
name = gots
end
end
force_text = "If you really want to make this formula use --force."
case name.downcase
when 'vim', 'screen'
raise <<-EOS
#{name} is blacklisted for creation
Apple distributes this program with OS X.
#{force_text}
EOS
when 'libarchive', 'libpcap'
raise <<-EOS
#{name} is blacklisted for creation
Apple distributes this library with OS X, you can find it in /usr/lib.
#{force_text}
EOS
when 'libxml', 'libxlst', 'freetype', 'libpng'
raise <<-EOS
#{name} is blacklisted for creation
Apple distributes this library with OS X, you can find it in /usr/X11/lib.
However not all build scripts look here, so you may need to call ENV.x11 or
ENV.libxml2 in your formula's install function.
#{force_text}
EOS
when 'rubygem'
raise "Sorry RubyGems comes with OS X so we don't package it.\n\n#{force_text}"
when 'wxwidgets'
raise <<-EOS
#{name} is blacklisted for creation
An older version of wxWidgets is provided by Apple with OS X, but
a formula for wxWidgets 2.8.10 is provided:
brew install wxmac
#{force_text}
EOS
end unless ARGV.force?
__make url, name
end
def github_info name
formula_name = Formula.path(name).basename
user = 'mxcl'
branch = 'master'
if system "/usr/bin/which -s git"
gh_user=`git config --global github.user 2>/dev/null`.chomp
/^\*\s*(.*)/.match(`git --work-tree=#{HOMEBREW_REPOSITORY} branch 2>/dev/null`)
unless $1.nil? || $1.empty? || gh_user.empty?
branch = $1.chomp
user = gh_user
end
end
return "http://github.com/#{user}/homebrew/commits/#{branch}/Library/Formula/#{formula_name}"
end
def info f
exec 'open', github_info(f.name) if ARGV.flag? '--github'
puts "#{f.name} #{f.version}"
puts f.homepage
puts "Depends on: #{f.deps.join(', ')}" unless f.deps.empty?
if f.prefix.parent.directory?
kids=f.prefix.parent.children
kids.each do |keg|
next if keg.basename.to_s == '.DS_Store'
print "#{keg} (#{keg.abv})"
print " *" if f.installed_prefix == keg and kids.length > 1
puts
end
else
puts "Not installed"
end
if f.caveats
puts
puts f.caveats
puts
end
history = github_info(f.name)
puts history if history
rescue FormulaUnavailableError
# check for DIY installation
d=HOMEBREW_PREFIX+name
if d.directory?
ohai "DIY Installation"
d.children.each {|keg| puts "#{keg} (#{keg.abv})"}
else
raise "No such formula or keg"
end
end
def issues_for_formula name
# bit basic as depends on the issue at github having the exact name of the
# formula in it. Which for stuff like objective-caml is unlikely. So we
# really should search for aliases too.
name = f.name if Formula === name
require 'open-uri'
require 'yaml'
issues = []
open("http://github.com/api/v2/yaml/issues/search/mxcl/homebrew/open/"+name) do |f|
YAML::load(f.read)['issues'].each do |issue|
issues << 'http://github.com/mxcl/homebrew/issues/#issue/%s' % issue['number']
end
end
issues
rescue
[]
end
def cleanup name
require 'formula'
f = Formula.factory name
formula_cellar = f.prefix.parent
if f.installed? and formula_cellar.directory?
kids = f.prefix.parent.children
kids.each do |keg|
next if f.installed_prefix == keg
print "Uninstalling #{keg}..."
FileUtils.rm_rf keg
puts
end
else
# If the cellar only has one version installed, don't complain
# that we can't tell which one to keep.
if formula_cellar.children.length > 1
opoo "Skipping #{name}: most recent version #{f.version} not installed"
end
end
end
def clean f
require 'cleaner'
Cleaner.new f
# Hunt for empty folders and nuke them unless they are
# protected by f.skip_clean?
# We want post-order traversal, so put the dirs in a stack
# and then pop them off later.
paths = []
f.prefix.find do |path|
paths << path if path.directory?
end
until paths.empty? do
d = paths.pop
if d.children.empty? and not f.skip_clean? d
puts "rmdir: #{d} (empty)" if ARGV.verbose?
d.rmdir
end
end
end
def prune
$n=0
$d=0
dirs=Array.new
paths=%w[bin sbin etc lib include share].collect {|d| HOMEBREW_PREFIX+d}
paths.each do |path|
path.find do |path|
path.extend ObserverPathnameExtension
if path.symlink?
path.unlink unless path.resolved_path_exists?
elsif path.directory?
dirs<<path
end
end
end
dirs.sort.reverse_each {|d| d.rmdir_if_possible}
if $n == 0 and $d == 0
puts "Nothing pruned" if ARGV.verbose?
else
# always showing symlinks text is deliberate
print "Pruned #{$n} symbolic links "
print "and #{$d} directories " if $d > 0
puts "from #{HOMEBREW_PREFIX}"
end
end
def diy
path=Pathname.getwd
if ARGV.include? '--set-version'
version=ARGV.next
else
version=path.version
raise "Couldn't determine version, try --set-version" if version.to_s.empty?
end
if ARGV.include? '--set-name'
name=ARGV.next
else
path.basename.to_s =~ /(.*?)-?#{version}/
if $1.nil? or $1.empty?
name=path.basename
else
name=$1
end
end
prefix=HOMEBREW_CELLAR+name+version
if File.file? 'CMakeLists.txt'
"-DCMAKE_INSTALL_PREFIX=#{prefix}"
elsif File.file? 'Makefile.am'
"--prefix=#{prefix}"
else
raise "Couldn't determine build system"
end
end
def macports_or_fink_installed?
# See these issues for some history:
# http://github.com/mxcl/homebrew/issues/#issue/13
# http://github.com/mxcl/homebrew/issues/#issue/41
# http://github.com/mxcl/homebrew/issues/#issue/48
%w[port fink].each do |ponk|
path = `/usr/bin/which -s #{ponk}`
return ponk unless path.empty?
end
# we do the above check because macports can be relocated and fink may be
# able to be relocated in the future. This following check is because if
# fink and macports are not in the PATH but are still installed it can
# *still* break the build -- because some build scripts hardcode these paths:
%w[/sw/bin/fink /opt/local/bin/port].each do |ponk|
return ponk if File.exist? ponk
end
# finally, sometimes people make their MacPorts or Fink read-only so they
# can quickly test Homebrew out, but still in theory obey the README's
# advise to rename the root directory. This doesn't work, many build scripts
# error out when they try to read from these now unreadable directories.
%w[/sw /opt/local].each do |path|
path = Pathname.new(path)
return path if path.exist? and not path.readable?
end
false
end
def versions_of(keg_name)
`/bin/ls #{HOMEBREW_CELLAR}/#{keg_name}`.collect { |version| version.strip }.reverse
end
def outdated_brews
require 'formula'
results = []
HOMEBREW_CELLAR.subdirs.each do |keg|
# Skip kegs with no versions installed
next unless keg.subdirs
# Skip HEAD formulae, consider them "evergreen"
next if keg.subdirs.collect{|p|p.basename.to_s}.include? "HEAD"
name = keg.basename.to_s
if (not (f = Formula.factory(name)).installed? rescue nil)
results << [keg, name, f.version]
end
end
return results
end
def search_brews text
require "formula"
return Formula.names if text.to_s.empty?
rx = if text =~ %r{^/(.*)/$}
Regexp.new($1)
else
/.*#{Regexp.escape text}.*/i
end
aliases = Formula.aliases
results = (Formula.names+aliases).grep rx
# Filter out aliases when the full name was also found
results.reject do |alias_name|
if aliases.include? alias_name
results.include? Formula.resolve_alias(alias_name)
end
end
end
def brew_install
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.\n"+
"For PPC support, see: http://github.com/sceaga/homebrew/tree/powerpc"
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.3" if llvm_build < RECOMMENDED_LLVM
else
opoo "You should upgrade to Xcode 3.1.4" if (gcc_40_build < RECOMMENDED_GCC_40) or (gcc_42_build < RECOMMENDED_GCC_42)
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 "Software installed with MacPorts and Fink are known to cause problems."
puts "If you experience issues 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
end
########################################################## class PrettyListing
class PrettyListing
def initialize path
Pathname.new(path).children.sort{ |a,b| a.to_s.downcase <=> b.to_s.downcase }.each do |pn|
case pn.basename.to_s
when 'bin', 'sbin'
pn.find { |pnn| puts pnn unless pnn.directory? }
when 'lib'
print_dir pn do |pnn|
# dylibs have multiple symlinks and we don't care about them
(pnn.extname == '.dylib' or pnn.extname == '.pc') and not pnn.symlink?
end
else
if pn.directory?
if pn.symlink?
puts "#{pn} -> #{pn.readlink}"
else
print_dir pn
end
elsif not (FORMULA_META_FILES.include? pn.basename.to_s or pn.basename.to_s == '.DS_Store')
puts pn
end
end
end
end
private
def print_dir root
dirs = []
remaining_root_files = []
other = ''
root.children.sort.each do |pn|
if pn.directory?
dirs << pn
elsif block_given? and yield pn
puts pn
other = 'other '
else
remaining_root_files << pn unless pn.basename.to_s == '.DS_Store'
end
end
dirs.each do |d|
files = []
d.find { |pn| files << pn unless pn.directory? }
print_remaining_files files, d
end
print_remaining_files remaining_root_files, root, other
end
def print_remaining_files files, root, other = ''
case files.length
when 0
# noop
when 1
puts files
else
puts "#{root}/ (#{files.length} #{other}files)"
end
end
end
def gcc_42_build
`/usr/bin/gcc-4.2 -v 2>&1` =~ /build (\d{4,})/
if $1
$1.to_i
elsif system "/usr/bin/which gcc"
# Xcode 3.0 didn't come with gcc-4.2
# We can't change the above regex to use gcc because the version numbers
# are different and thus, not useful.
# FIXME I bet you 20 quid this causes a side effect — magic values tend to
401
else
nil
end
end
alias :gcc_build :gcc_42_build # For compatibility
def gcc_40_build
`/usr/bin/gcc-4.0 -v 2>&1` =~ /build (\d{4,})/
if $1
$1.to_i
else
nil
end
end
def llvm_build
if MACOS_VERSION >= 10.6
xcode_path = `/usr/bin/xcode-select -print-path`.chomp
return nil if xcode_path.empty?
`#{xcode_path}/usr/bin/llvm-gcc -v 2>&1` =~ /LLVM build (\d{4,})/
$1.to_i
end
end
def xcode_version
`xcodebuild -version 2>&1` =~ /Xcode (\d(\.\d)*)/
return $1 ? $1 : nil
end
def _compiler_recommendation build, recommended
message = (!build.nil? && build < recommended) ? "(#{recommended} or newer recommended)" : ""
return build, message
end

View File

@ -6,6 +6,21 @@ class Cleaner
unless ENV['HOMEBREW_KEEP_INFO'].nil?
f.info.rmtree if f.info.directory? and not f.skip_clean? f.info
end
# Hunt for empty folders and nuke them unless they are protected by
# f.skip_clean? We want post-order traversal, so put the dirs in a stack
# and then pop them off later.
paths = []
f.prefix.find do |path|
paths << path if path.directory?
end
paths.each do |d|
if d.children.empty? and not f.skip_clean? d
puts "rmdir: #{d} (empty)" if ARGV.verbose?
d.rmdir
end
end
end
private

View File

@ -0,0 +1,9 @@
module Homebrew extend self
def __cache
if ARGV.named.empty?
puts HOMEBREW_CACHE
else
puts ARGV.formulae.map{ |f| f.cached_download }
end
end
end

View File

@ -0,0 +1,9 @@
module Homebrew extend self
def __cellar
if ARGV.named.empty?
puts HOMEBREW_CELLAR
else
puts ARGV.formulae.map{ |f| HOMEBREW_CELLAR/f }
end
end
end

View File

@ -0,0 +1,66 @@
require 'hardware'
module Homebrew extend self
def __config
puts config_s
end
def llvm
@llvm ||= MacOS.llvm_build_version
end
def gcc_42
@gcc_42 ||= MacOS.gcc_42_build_version
end
def gcc_40
@gcc_40 ||= MacOS.gcc_40_build_version
end
def xcode_version
`xcodebuild -version 2>&1` =~ /Xcode (\d(\.\d)*)/
$1
end
def llvm_recommendation
"(#{RECOMMENDED_LLVM} or newer recommended)" if llvm and llvm < RECOMMENDED_LLVM
end
def gcc_42_recommendation
"(#{RECOMMENDED_GCC_42} or newer recommended)" if gcc_42 and gcc_42 < RECOMMENDED_GCC_42
end
def gcc_40_recommendation
"(#{RECOMMENDED_GCC_40} or newer recommended)" if gcc_40.nil? and gcc_40 < RECOMMENDED_GCC_40
end
def sha
sha = `cd #{HOMEBREW_REPOSITORY} && git rev-parse --verify HEAD 2> /dev/null`.chomp
if sha.empty? then "(none)" else sha end
end
def system_ruby
Pathname.new('/usr/bin/ruby').realpath.to_s
end
def config_s; <<-EOS.undent
HOMEBREW_VERSION: #{HOMEBREW_VERSION}
HEAD: #{sha}
HOMEBREW_PREFIX: #{HOMEBREW_PREFIX}
HOMEBREW_CELLAR: #{HOMEBREW_CELLAR}
HOMEBREW_REPOSITORY: #{HOMEBREW_REPOSITORY}
HOMEBREW_LIBRARY_PATH: #{HOMEBREW_LIBRARY_PATH}
Hardware: #{Hardware.cores_as_words}-core #{Hardware.bits}-bit #{Hardware.intel_family}
OS X: #{MACOS_FULL_VERSION}
Kernel Architecture: #{`uname -m`.chomp}
Ruby: #{RUBY_VERSION}-#{RUBY_PATCHLEVEL}
/usr/bin/ruby => #{system_ruby}
Xcode: #{xcode_version}
GCC-4.0: #{gcc_40 ? "build #{gcc_40}" : "N/A"} #{gcc_40_recommendation}
GCC-4.2: #{gcc_42 ? "build #{gcc_42}" : "N/A"} #{gcc_42_recommendation}
LLVM: #{llvm ? "build #{llvm}" : "N/A" } #{llvm_recommendation}
MacPorts or Fink? #{macports_or_fink_installed?}
X11 installed? #{x11_installed?}
EOS
end
end

View File

@ -0,0 +1,32 @@
require 'extend/ENV'
require 'hardware'
module Homebrew extend self
def __env
ENV.extend(HomebrewEnvExtension)
ENV.setup_build_environment
dump_build_env ENV
end
def dump_build_env env
puts %["--use-llvm" was specified] if ARGV.include? '--use-llvm'
%w[ CC CXX LD ].each do |k|
value = env[k]
if value
results = value
if File.exists? value and File.symlink? value
target = Pathname.new(value)
results += " => #{target.realpath}"
end
puts "#{k}: #{results}"
end
end
%w[ CFLAGS CXXFLAGS CPPFLAGS LDFLAGS MACOSX_DEPLOYMENT_TARGET MAKEFLAGS PKG_CONFIG_PATH
HOMEBREW_DEBUG HOMEBREW_VERBOSE HOMEBREW_USE_LLVM HOMEBREW_SVN ].each do |k|
value = env[k]
puts "#{k}: #{value}" if value
end
end
end

View File

@ -0,0 +1,9 @@
module Homebrew extend self
def __prefix
if ARGV.named.empty?
puts HOMEBREW_PREFIX
else
puts ARGV.formulae.map{ |f| f.prefix }
end
end
end

View File

@ -0,0 +1,5 @@
module Homebrew extend self
def __repository
puts HOMEBREW_REPOSITORY
end
end

View File

@ -0,0 +1,10 @@
module Homebrew extend self
def cat
# do not "fix" this to support multiple arguments, the output would be
# unparsable, if the user wants to cat multiple formula they can call
# brew cat multiple times.
cd HOMEBREW_REPOSITORY
exec "cat", ARGV.formulae.first.path, *ARGV.options_only
end
end

View File

@ -0,0 +1,42 @@
require 'formula'
require 'cmd/prune'
module Homebrew extend self
def cleanup
if ARGV.named.empty?
HOMEBREW_CELLAR.children.each do |rack|
begin
cleanup_formula rack.basename.to_s if rack.directory?
rescue FormulaUnavailableError => e
opoo "Formula not found for #{e.name}"
end
end
# seems like a good time to do some additional cleanup
Homebrew.prune
else
ARGV.formulae.each do |f|
cleanup_formula f
end
end
end
def cleanup_formula f
f = Formula.factory f
rack = f.prefix.parent
if f.installed? and rack.directory?
rack.children.each do |keg|
if f.installed_prefix != keg
print "Uninstalling #{keg}..."
rm_rf keg
puts
end
end
elsif rack.children.length > 1
# If the cellar only has one version installed, don't complain
# that we can't tell which one to keep.
opoo "Skipping #{name}: most recent version #{f.version} not installed"
end
end
end

View File

@ -0,0 +1,153 @@
require 'formula'
module Homebrew extend self
def create
if ARGV.include? '--macports'
exec "open", "http://www.macports.org/ports.php?by=name&substr=#{ARGV.next}"
elsif ARGV.include? '--fink'
exec "open", "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}"
elsif ARGV.named.empty?
raise UsageError
else
paths = ARGV.named.map do |url|
fc = FormulaCreator.new
fc.url = url
fc.mode = if ARGV.include? '--cmake'
:cmake
elsif ARGV.include? '--autotools'
:autotools
end
if fc.name.to_s.strip.empty?
path = Pathname.new url
print "Formula name [#{path.stem}]: "
fc.name = __gets || path.stem
end
unless ARGV.force?
if msg = blacklisted?(fc.name)
raise "#{msg}\n\nIf you really want to make this formula use --force."
end
if Formula.aliases.include? fc.name
realname = Formula.caniconical_name fc.name
raise <<-EOS.undent
The formula #{realname} is already aliased to #{fc.name}
Please check that you are not creating a duplicate.
To force creation use --force.
EOS
end
end
fc.generate
fc.path
end
exec_editor *paths
end
end
def __gets
gots = $stdin.gets.chomp
if gots.empty? then nil else gots end
end
def blacklisted? name
case name.downcase
when 'vim', 'screen' then <<-EOS.undent
#{name} is blacklisted for creation
Apple distributes this program with OS X.
EOS
when 'libarchive', 'libpcap' then <<-EOS.undent
#{name} is blacklisted for creation
Apple distributes this library with OS X, you can find it in /usr/lib.
EOS
when 'libxml', 'libxlst', 'freetype', 'libpng' then <<-EOS.undent
#{name} is blacklisted for creation
Apple distributes this library with OS X, you can find it in /usr/X11/lib.
However not all build scripts look here, so you may need to call ENV.x11 or
ENV.libxml2 in your formula's install function.
EOS
when /^rubygems?$/
"Sorry RubyGems comes with OS X so we don't package it."
when 'wxwidgets' then <<-EOS.undent
#{name} is blacklisted for creation
An older version of wxWidgets is provided by Apple with OS X, but
a formula for wxWidgets 2.8.10 is provided:
brew install wxmac
EOS
end
end
end
class FormulaCreator
attr :url
attr :md5
attr :name, true
attr :path
attr :mode, true
def url= url
@url = url
path = Pathname.new url
/(.*?)[-_.]?#{path.version}/.match path.basename
@name = $1
@path = Formula.path $1
end
def version
Pathname.new(url).version
end
def generate
raise "#{path} already exists" if path.exist?
raise VersionUndetermined if version.nil?
require 'digest'
require 'erb'
if version.nil?
opoo "Version cannot be determined from URL."
puts "You'll need to add an explicit 'version' to the formula."
else
puts "Version detected as #{version}."
end
unless ARGV.include? "--no-md5" and version
strategy = detect_download_strategy url
@md5 = strategy.new(url, name, version, nil).fetch.md5 if strategy == CurlDownloadStrategy
end
path.write ERB.new(template, nil, '>').result(binding)
end
def template; <<-EOS.undent
require 'formula'
class #{Formula.class_s name} <Formula
url '#{url}'
homepage ''
md5 '#{md5}'
<% if mode == :cmake %>
depends_on 'cmake'
<% elsif mode == nil %>
# depends_on 'cmake'
<% end %>
def install
<% if mode == :cmake %>
system "cmake . \#{std_cmake_parameters}"
<% elsif mode == :autotools %>
system "./configure", "--disable-debug", "--disable-dependency-tracking",
"--prefix=\#{prefix}"
<% else %>
system "./configure", "--disable-debug", "--disable-dependency-tracking",
"--prefix=\#{prefix}"
# system "cmake . \#{std_cmake_parameters}"
<% end %>
system "make install"
end
end
EOS
end
end

View File

@ -0,0 +1,14 @@
module Homebrew extend self
def deps
puts if ARGV.include?('--all')
require 'formula'
Formula.all.each do |f|
"#{f.name}:#{f.deps.join(' ')}"
end
elsif ARGV.include?("-1") or ARGV.include?("--1")
*ARGV.formulae.map{ |f| f.deps or [] }.flatten.uniq.sort
else
*ARGV.formulae.map{ |f| f.recursive_deps.map{ |f| f.name } }.flatten.uniq.sort
end
end
end

View File

@ -0,0 +1,34 @@
module Homebrew extend self
def diy
path = Pathname.getwd
version = if ARGV.include? '--set-version'
ARGV.next
elsif path.version.to_s.empty?
raise "Couldn't determine version, try --set-version"
else
path.version
end
name = if ARGV.include? '--set-name'
ARGV.next
else
path.basename.to_s =~ /(.*?)-?#{version}/
if $1.to_s.empty?
path.basename
else
$1
end
end
prefix = HOMEBREW_CELLAR/name/version
if File.file? 'CMakeLists.txt'
puts "-DCMAKE_INSTALL_PREFIX=#{prefix}"
elsif File.file? 'Makefile.am'
puts "--prefix=#{prefix}"
else
raise "Couldn't determine build system"
end
end
end

View File

@ -583,7 +583,8 @@ def check_for_other_vars
end
end
def brew_doctor
module Homebrew extend self
def doctor
read, write = IO.pipe
if fork == nil
@ -629,3 +630,4 @@ def brew_doctor
end
end
end
end

View File

@ -0,0 +1,26 @@
require 'formula'
module Homebrew extend self
def 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', HOMEBREW_REPOSITORY/"bin/brew",
HOMEBREW_REPOSITORY/'README.md',
HOMEBREW_REPOSITORY/".gitignore",
*Dir[HOMEBREW_REPOSITORY/"Library/*"]
else
# Don't use ARGV.formulae as that will throw if the file doesn't parse
paths = ARGV.named.map do |name|
HOMEBREW_REPOSITORY/"Library/Formula/#{Formula.caniconical_name name}.rb"
end
unless ARGV.force?
paths.each do |path|
raise FormulaUnavailableError, path.basename('.rb').to_s unless path.file?
end
end
exec_editor *paths
end
end
end

View File

@ -0,0 +1,9 @@
module Homebrew extend self
def home
if ARGV.named.empty?
exec "open", HOMEBREW_WWW
else
exec "open", *ARGV.formulae.map{ |f| f.homepage }
end
end
end

View File

@ -0,0 +1,92 @@
require 'formula'
module Homebrew extend self
def info
if ARGV.named.empty?
if ARGV.include? "--all"
Formula.each do |f|
info_formula f
puts '---'
end
else
puts "#{HOMEBREW_CELLAR.children.length} kegs, #{HOMEBREW_CELLAR.abv}"
end
elsif valid_url ARGV[0]
path = Pathname.new(ARGV.shift)
/(.*?)[-_.]?#{path.version}/.match path.basename
unless $1.to_s.empty?
name = $1
else
name = path.stem
end
puts "#{name} #{path.version}"
else
ARGV.formulae.each{ |f| info_formula f }
end
end
def github_info name
formula_name = Formula.path(name).basename
user = 'mxcl'
branch = 'master'
if system "/usr/bin/which -s git"
gh_user=`git config --global github.user 2>/dev/null`.chomp
/^\*\s*(.*)/.match(`git --work-tree=#{HOMEBREW_REPOSITORY} branch 2>/dev/null`)
unless $1.nil? || $1.empty? || gh_user.empty?
branch = $1.chomp
user = gh_user
end
end
"http://github.com/#{user}/homebrew/commits/#{branch}/Library/Formula/#{formula_name}"
end
def info_formula f
exec 'open', github_info(f.name) if ARGV.flag? '--github'
puts "#{f.name} #{f.version}"
puts f.homepage
puts "Depends on: #{f.deps*', '}" unless f.deps.empty?
rack = f.prefix.parent
if rack.directory?
kegs = rack.children
kegs.each do |keg|
next if keg.basename.to_s == '.DS_Store'
print "#{keg} (#{keg.abv})"
print " *" if f.installed_prefix == keg and kegs.length > 1
puts
end
else
puts "Not installed"
end
if f.caveats
puts
puts f.caveats
puts
end
history = github_info f.name
puts history if history
rescue FormulaUnavailableError
# check for DIY installation
d = HOMEBREW_PREFIX/name
if d.directory?
ohai "DIY Installation"
d.children.each{ |keg| puts "#{keg} (#{keg.abv})" }
else
raise "No such formula or keg"
end
end
private
def valid_url u
u[0..6] == 'http://' or u[0..7] == 'https://' or u[0..5] == 'ftp://'
end
end

View File

@ -0,0 +1,87 @@
require 'formula_installer'
require 'hardware'
module Homebrew extend self
def install
brew_install
end
end
def brew_install
############################################################ sanity checks
case Hardware.cpu_type when :ppc, :dunno
abort "Sorry, Homebrew does not support your computer's CPU architecture.\n"+
"For PPC support, see: http://github.com/sceaga/homebrew/tree/powerpc"
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.3" if llvm_build < RECOMMENDED_LLVM
else
opoo "You should upgrade to Xcode 3.1.4" if (gcc_40_build < RECOMMENDED_GCC_40) or (gcc_42_build < RECOMMENDED_GCC_42)
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 "Software installed with MacPorts and Fink are known to cause problems."
puts "If you experience issues 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
end
def check_for_blacklisted_formula names
return if ARGV.force?
names.each do |name|
case name
when 'tex', 'tex-live', 'texlive' then abort <<-EOS.undent
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.)
We recommend using a MacTeX distribution:
http://www.tug.org/mactex/
EOS
when 'mercurial', 'hg' then abort <<-EOS.undent
Mercurial can be install thusly:
brew install pip && pip install mercurial
EOS
when 'npm' then abort <<-EOS.undent
npm can be installed thusly by following the instructions at
http://npmjs.org/
To do it in one line, use this command:
curl http://npmjs.org/install.sh | sudo sh
EOS
when 'setuptools' then abort <<-EOS.undent
When working with a Homebrew-built Python, distribute is preferred
over setuptools, and can be used as the prerequisite for pip.
Install distribute using:
brew install distribute
EOS
end
end
end

View File

@ -0,0 +1,8 @@
module Homebrew extend self
def link
ARGV.kegs.each do |keg|
print "Linking #{keg}... "
puts "#{keg.link} links created"
end
end
end

View File

@ -0,0 +1,88 @@
module Homebrew extend self
def list
if ARGV.flag? '--unbrewed'
dirs = HOMEBREW_PREFIX.children.select{ |pn| pn.directory? }.map{ |pn| pn.basename.to_s }
dirs -= %w[Library Cellar .git]
cd HOMEBREW_PREFIX
exec 'find', *dirs + %w[-type f ( ! -iname .ds_store ! -iname brew )]
elsif ARGV.flag? '--versions'
if ARGV.named.empty?
HOMEBREW_CELLAR.children.select{ |pn| pn.directory? }
else
ARGV.named.map{ |n| HOMEBREW_CELLAR/n }.select{ |pn| pn.exist? }
end.each do |d|
versions = d.children.select{ |pn| pn.directory? }.map{ |pn| pn.basename.to_s }
puts "#{d.basename} #{versions*' '}"
end
elsif ARGV.named.empty?
ENV['CLICOLOR'] = nil
exec 'ls', *ARGV.options_only << 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
end
end
class PrettyListing
def initialize path
Pathname.new(path).children.sort{ |a,b| a.to_s.downcase <=> b.to_s.downcase }.each do |pn|
case pn.basename.to_s
when 'bin', 'sbin'
pn.find { |pnn| puts pnn unless pnn.directory? }
when 'lib'
print_dir pn do |pnn|
# dylibs have multiple symlinks and we don't care about them
(pnn.extname == '.dylib' or pnn.extname == '.pc') and not pnn.symlink?
end
else
if pn.directory?
if pn.symlink?
puts "#{pn} -> #{pn.readlink}"
else
print_dir pn
end
elsif not (FORMULA_META_FILES + ['.DS_Store']).include? pn.basename.to_s
puts pn
end
end
end
end
def print_dir root
dirs = []
remaining_root_files = []
other = ''
root.children.sort.each do |pn|
if pn.directory?
dirs << pn
elsif block_given? and yield pn
puts pn
other = 'other '
else
remaining_root_files << pn unless pn.basename.to_s == '.DS_Store'
end
end
dirs.each do |d|
files = []
d.find { |pn| files << pn unless pn.directory? }
print_remaining_files files, d
end
print_remaining_files remaining_root_files, root, other
end
def print_remaining_files files, root, other = ''
case files.length
when 0
# noop
when 1
puts files
else
puts "#{root}/ (#{files.length} #{other}files)"
end
end
end

View File

@ -0,0 +1,10 @@
module Homebrew extend self
def log
cd HOMEBREW_REPOSITORY
if ARGV.named.empty?
exec "git", "log", *ARGV.options_only
else
exec "git", "log", *ARGV.formulae.map(&:path), *ARGV.options_only
end
end
end

View File

@ -0,0 +1,28 @@
require 'formula'
module Homebrew extend self
def outdated
outdated_brews.each do |keg, name, version|
if $stdout.tty? and not ARGV.flag? '--quiet'
versions = keg.cd{ Dir['*'] }.join(', ')
puts "#{name} (#{versions} < #{version})"
else
puts name
end
end
end
def outdated_brews
HOMEBREW_CELLAR.subdirs.map do |rack|
# Skip kegs with no versions installed
next unless rack.subdirs
# Skip HEAD formulae, consider them "evergreen"
next if rack.subdirs.map{ |keg| keg.basename.to_s }.include? "HEAD"
name = rack.basename.to_s
f = Formula.factory name rescue nil
[rack, name, f.version] if f and not f.installed?
end.compact
end
end

View File

@ -0,0 +1,31 @@
module Homebrew extend self
# $n and $d are used by the ObserverPathnameExtension to keep track of
# certain filesystem actions.
def prune
$n = 0
$d = 0
dirs = []
%w[bin sbin etc lib include share].map{ |d| HOMEBREW_PREFIX/d }.each do |path|
path.find do |path|
path.extend ObserverPathnameExtension
if path.symlink?
path.unlink unless path.resolved_path_exists?
elsif path.directory?
dirs << path
end
end
end
dirs.sort.reverse_each{ |d| d.rmdir_if_possible }
if $n == 0 and $d == 0
puts "Nothing pruned" if ARGV.verbose?
else
print "Pruned #{$n} symbolic links "
print "and #{$d} directories " if $d > 0
puts "from #{HOMEBREW_PREFIX}"
end
end
end

View File

@ -0,0 +1,42 @@
require "formula"
module Homebrew extend self
def search
if ARGV.include? '--macports'
exec "open", "http://www.macports.org/ports.php?by=name&substr=#{ARGV.next}"
elsif ARGV.include? '--fink'
exec "open", "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}"
end
require 'cmd/install' # for blacklisted? function
blacklisted? ARGV.named do |msg, _|
abort msg
end unless ARGV.force?
puts_columns search_brews(ARGV.first)
end
def search_brews text
if text.to_s.empty?
Formula.names
else
rx = if text =~ %r{^/(.*)/$}
Regexp.new($1)
else
/.*#{Regexp.escape text}.*/i
end
aliases = Formula.aliases
results = (Formula.names+aliases).grep rx
# Filter out aliases when the full name was also found
results.reject do |alias_name|
if aliases.include? alias_name
resolved_name = (HOMEBREW_REPOSITORY/"Library/Aliases"/alias_name).readlink.basename('.rb').to_s
results.include? resolved_name
end
end
end
end
end

View File

@ -0,0 +1,31 @@
require 'keg'
module Homebrew extend self
def uninstall
unless ARGV.force?
ARGV.kegs.each do |keg|
puts "Uninstalling #{keg}..."
keg.unlink
keg.uninstall
end
else
ARGV.formulae.each do |f|
rack = f.prefix.parent
if rack.directory?
puts "Uninstalling #{f}..."
rack.children do |keg|
if keg.directory?
keg = Keg.new(keg)
keg.unlink
keg.rmtree
end
end
rack.rmdir
end
end
end
rescue MultipleVersionsInstalledError => e
onoe e
puts "Use `brew remove --force #{e.name}` to remove all versions."
end
end

View File

@ -0,0 +1,8 @@
module Homebrew extend self
def unlink
ARGV.kegs.each do |keg|
print "Unlinking #{keg}... "
puts "#{keg.unlink} links removed"
end
end
end

View File

@ -1,3 +1,16 @@
module Homebrew extend self
def update
abort "Please `brew install git' first." unless system "/usr/bin/which -s git"
updater = RefreshBrew.new
if updater.update_from_masterbrew!
updater.report
else
puts "Already up-to-date."
end
end
end
class RefreshBrew
REPOSITORY_URL = "http://github.com/mxcl/homebrew.git"
INIT_COMMAND = "git init"

View File

@ -0,0 +1,25 @@
require 'formula'
# `brew uses foo bar` now returns formula that use both foo and bar
# Rationale: If you want the union just run the command twice and
# concatenate the results.
# The intersection is harder to achieve with shell tools.
module Homebrew extend self
def uses
uses = Formula.all.select do |f|
ARGV.formulae.all? do |ff|
# For each formula given, show which other formulas depend on it.
# We only go one level up, ie. direct dependencies.
f.deps.include? ff.name
end
end
if ARGV.include? "--installed"
uses = uses.select do |f|
keg = HOMEBREW_CELLAR/f
keg.directory? and not keg.subdirs.empty?
end
end
puts uses.sort
end
end

View File

@ -0,0 +1,47 @@
# maybe never used by anyone, but alas it must continue to exist
def versions_of(keg_name)
`/bin/ls #{HOMEBREW_CELLAR}/#{keg_name}`.collect { |version| version.strip }.reverse
end
def dump_config
require 'cmd/--config'
Homebrew.__config
end
def dump_build_env env
require 'cmd/--env'
Homebrew.dump_build_env env
end
def gcc_42_build
MacOS.gcc_42_build_version
end
alias :gcc_build :gcc_42_build
def gcc_40_build
MacOS.gcc_40_build_version
end
def llvm_build
MacOS.llvm_build_version
end
def x11_installed?
MacOS.x11_installed?
end
def macports_or_fink_installed?
MacOS.macports_or_fink_installed?
end
def outdated_brews
require 'cmd/outdated'
Homebrew.outdated_brews
end
def search_brews text
require 'cmd/search'
Homebrew.search_brews text
end

View File

@ -0,0 +1,99 @@
class NotAKegError < RuntimeError
end
class FormulaUnavailableError < RuntimeError
attr :name
def initialize name
@name = name
super "No available formula for #{name}"
end
end
module Homebrew
class InstallationError < RuntimeError
attr :formula
def initialize formula
@formula = formula
end
def initialize formula, message
super message
@formula = formula
end
end
end
class FormulaAlreadyInstalledError < Homebrew::InstallationError
def message
"Formula already installed: #{formula}"
end
end
class FormulaInstallationAlreadyAttemptedError < Homebrew::InstallationError
def message
"Formula installation already attempted: #{formula}"
end
end
class UnsatisfiedExternalDependencyError < Homebrew::InstallationError
attr :type
def initialize formula, type
@type = type
@formula = formula
end
def message
<<-EOS.undent
Unsatisfied dependency: #{formula}
Homebrew does not provide #{type.to_s.capitalize} dependencies, #{tool} does:
#{command_line} #{formula}
EOS
end
private
def tool
case type
when :python then 'pip'
when :ruby, :jruby then 'rubygems'
when :perl then 'cpan'
end
end
def command_line
case type
when :python
"#{brew_pip}pip install"
when :ruby
"gem install"
when :perl
"cpan -i"
when :jruby
"jruby -S gem install"
end
end
def brew_pip
'brew install pip && ' unless Formula.factory('pip').installed?
end
end
class BuildError < Homebrew::InstallationError
attr :exit_status
attr :command
attr :env
def initialize formula, cmd, args, es
@command = cmd
@env = ENV.to_hash
@exit_status = es.exitstatus rescue 1
args = args.map{ |arg| arg.gsub " ", "\\ " }.join(" ")
super formula, "Failed executing: #{command} #{args}"
end
def was_running_configure?
@command == './configure'
end
end

View File

@ -152,7 +152,7 @@ module HomebrewEnvExtension
end
def x11
opoo "You do not have X11 installed, this formula may not build." if not x11_installed?
opoo "You do not have X11 installed, this formula may not build." if not MacOS.x11_installed?
# There are some config scripts (e.g. freetype) here that should go in the path
prepend 'PATH', '/usr/X11/bin', ':'

View File

@ -1,15 +1,6 @@
require 'download_strategy'
require 'fileutils'
class FormulaUnavailableError <RuntimeError
def initialize name
@name = name
super "No available formula for #{name}"
end
attr_reader :name
end
class SoftwareSpecification
attr_reader :url, :specs, :using
@ -223,6 +214,22 @@ class Formula
end
end
def == b
name == b.name
end
def eql? b
self == b and self.class.equal? b.class
end
def hash
name.hash
end
def <=> b
name <=> b.name
end
def to_s
name
end
# Standard parameters for CMake builds.
# Using Build Type "None" tells cmake to use our CFLAGS,etc. settings.
# Setting it to Release would ignore our flags.
@ -266,15 +273,25 @@ class Formula
# an array of all Formula, instantiated
def self.all
all = []
map{ |f| f }
end
def self.map
rv = []
each{ |f| rv << yield(f) }
rv
end
def self.each
names.each do |n|
begin
all << Formula.factory(n)
yield Formula.factory(n)
rescue
# Don't let one broken formula break commands.
end
end
return all
end
def inspect
name
end
def self.aliases
@ -320,8 +337,9 @@ class Formula
install_type = :from_path
target_file = path.to_s
else
name = Formula.caniconical_name(name)
# For names, map to the path and then require
require self.path(name)
require Formula.path(name)
install_type = :from_name
end
end
@ -344,7 +362,7 @@ class Formula
end
def self.path name
HOMEBREW_REPOSITORY+"Library/Formula/#{name.downcase}.rb"
HOMEBREW_REPOSITORY/"Library/Formula/#{name.downcase}.rb"
end
def deps
@ -352,7 +370,20 @@ class Formula
end
def external_deps
self.class.external_deps
self.class.external_deps or {}
end
# deps are in an installable order
# which means if a depends on b then b will be ordered before a in this list
def recursive_deps
Formula.expand_deps(self).flatten.uniq
end
def self.expand_deps f
f.deps.map do |dep|
dep = Formula.factory dep
expand_deps(dep) << dep
end
end
protected
@ -381,11 +412,8 @@ protected
raise
end
end
rescue SystemCallError
# usually because exec could not be find the command that was requested
raise
rescue
raise BuildError.new(cmd, args, $?)
raise BuildError.new(self, cmd, args, $?)
end
private

View File

@ -2,6 +2,8 @@ require 'extend/pathname'
require 'extend/ARGV'
require 'extend/string'
require 'utils'
require 'exceptions'
require 'compatibility'
ARGV.extend(HomebrewArgvExtension)
@ -40,3 +42,11 @@ HOMEBREW_USER_AGENT = "Homebrew #{HOMEBREW_VERSION} (Ruby #{RUBY_VERSION}-#{RUBY
RECOMMENDED_LLVM = 2326
RECOMMENDED_GCC_40 = (MACOS_VERSION >= 10.6) ? 5494 : 5493
RECOMMENDED_GCC_42 = (MACOS_VERSION >= 10.6) ? 5664 : 5577
require 'fileutils'
module Homebrew extend self
include FileUtils
end
FORMULA_META_FILES = %w[README README.md ChangeLog COPYING LICENSE LICENCE COPYRIGHT AUTHORS]
PLEASE_REPORT_BUG = "#{Tty.white}Please report this bug at #{Tty.em}\nhttps://github.com/mxcl/homebrew/wiki/new-issue#{Tty.reset}"

View File

@ -30,7 +30,7 @@ at_exit do
require 'fileutils'
require 'hardware'
require 'keg'
require 'brew.h.rb'
require 'compatibility'
ENV.extend(HomebrewEnvExtension)
ENV.setup_build_environment
@ -121,7 +121,8 @@ def install f
ohai 'Finishing up' if ARGV.verbose?
begin
clean f
require 'cleaner'
Cleaner.new f
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"

View File

@ -1,3 +1,5 @@
require 'extend/pathname'
class Keg <Pathname
def initialize path
super path
@ -5,8 +7,6 @@ class Keg <Pathname
raise "#{to_s} is not a directory" unless directory?
end
class NotAKegError <RuntimeError; end
# if path is a file in a keg then this will return the containing Keg object
def self.for path
path = path.realpath

View File

@ -1,6 +1,5 @@
require 'testing_env'
require 'utils'
require 'brew.h'
require 'extend/ENV'
ENV.extend(HomebrewEnvExtension)

View File

@ -5,7 +5,6 @@ ARGV.extend(HomebrewArgvExtension)
require 'test/testball'
require 'utils'
require 'brew.h'
class MockFormula <Formula
def initialize url
@ -79,19 +78,24 @@ class BeerTasting < Test::Unit::TestCase
# end
def test_brew_h
require 'cmd/info'
require 'cmd/prune'
require 'cleaner'
nostdout do
assert_nothing_raised do
f=TestBall.new
make f.url
info f
clean f
prune
Homebrew.info_formula f
Cleaner.new f
Homebrew.prune
#TODO test diy function too
end
end
end
def test_brew_cleanup
require 'cmd/cleanup'
f1=TestBall.new
f1.instance_eval { @version = "0.1" }
f2=TestBall.new
@ -110,7 +114,7 @@ class BeerTasting < Test::Unit::TestCase
assert f3.installed?
nostdout do
cleanup f3
Homebrew.cleanup_formula f3
end
assert !f1.installed?
@ -175,4 +179,16 @@ class BeerTasting < Test::Unit::TestCase
assert_equal 'foo-0.1', foo1.stem
assert_equal '0.1', foo1.version
end
class MockMockFormula < Struct.new(:name); end
def test_formula_equality
f = MockFormula.new('http://example.com/test-0.1.tgz')
g = MockMockFormula.new('test')
assert f == f
assert f == g
assert f.eql? f
assert (not (f.eql? g))
end
end

View File

@ -3,38 +3,12 @@ require 'testing_env'
require 'extend/ARGV' # needs to be after test/unit to avoid conflict with OptionsParser
ARGV.extend(HomebrewArgvExtension)
require 'extend/string'
require 'test/testball'
require 'formula_installer'
require 'utils'
# Custom formula installer that checks deps but does not
# run the install code. We also override the external dep
# install messages, so for instance the Python check doesnt
# look for the pip formula (which isn't avaialble in test
# mode.)
class DontActuallyInstall < FormulaInstaller
def install_private f ; end
def pyerr dep
"Python module install message."
end
def plerr dep
"Perl module install message."
end
def rberr dep
"Ruby module install message."
end
def jrberr dep
"JRuby module install message."
end
end
class BadPerlBall <TestBall
depends_on "notapackage" => :perl
@ -102,14 +76,14 @@ end
class ExternalDepsTests < Test::Unit::TestCase
def check_deps_fail f
assert_raises(RuntimeError) do
DontActuallyInstall.new.install f.new
assert_raises(UnsatisfiedExternalDependencyError) do
FormulaInstaller.check_external_deps f.new
end
end
def check_deps_pass f
assert_nothing_raised do
DontActuallyInstall.new.install f.new
FormulaInstaller.check_external_deps f.new
end
end

View File

@ -5,7 +5,6 @@ ARGV.extend(HomebrewArgvExtension)
require 'test/testball'
require 'utils'
require 'brew.h'
class MostlyAbstractFormula <Formula

View File

@ -26,7 +26,7 @@ class ConfigureTests < Test::Unit::TestCase
f=ConfigureFails.new
begin
f.brew { f.install }
rescue ExecutionError => e
rescue BuildError => e
assert e.was_running_configure?
end
end

View File

@ -7,7 +7,7 @@ ARGV.extend(HomebrewArgvExtension)
require 'formula'
require 'utils'
require 'update'
require 'cmd/update'
class RefreshBrewMock < RefreshBrew
def in_prefix_expect(expect, returns = '')

View File

@ -8,6 +8,7 @@ ABS__FILE__=File.expand_path(__FILE__)
$:.push(File.expand_path(__FILE__+'/../..'))
require 'extend/pathname'
require 'exceptions'
# these are defined in global.rb, but we don't want to break our actual
# homebrew tree, and we do want to test everything :)
@ -26,4 +27,9 @@ at_exit { HOMEBREW_PREFIX.parent.rmtree }
# Test fixtures and files can be found relative to this path
TEST_FOLDER = Pathname.new(ABS__FILE__).parent.realpath
require 'fileutils'
module Homebrew extend self
include FileUtils
end
require 'test/unit' # must be after at_exit

View File

@ -1,38 +1,3 @@
class ExecutionError <RuntimeError
attr :exit_status
attr :command
def initialize cmd, args = [], es = nil
@command = cmd
super "Failure while executing: #{cmd} #{pretty(args)*' '}"
@exit_status = es.exitstatus rescue 1
end
def was_running_configure?
@command == './configure'
end
private
def pretty args
args.collect do |arg|
if arg.to_s.include? ' '
"'#{ arg.gsub "'", "\\'" }'"
else
arg
end
end
end
end
class BuildError <ExecutionError
attr :env
def initialize cmd, args = [], es = nil
super
@env = ENV.to_hash
end
end
class Tty
class <<self
@ -112,7 +77,10 @@ end
# Kernel.system but with exceptions
def safe_system cmd, *args
raise ExecutionError.new(cmd, args, $?) unless Homebrew.system(cmd, *args)
unless Homebrew.system cmd, *args
args = args.map{ |arg| arg.gsub " ", "\\ " } * " "
raise "Failure while executing: #{cmd} #{args}"
end
end
# prints no output
@ -263,28 +231,97 @@ def nostdout
end
end
def dump_build_env env
puts "\"--use-llvm\" was specified" if ARGV.include? '--use-llvm'
%w[ CC CXX LD ].each do |k|
value = env[k]
if value
results = value
if File.exists? value and File.symlink? value
target = Pathname.new(value)
results += " => #{target.realpath}"
end
puts "#{k}: #{results}"
module MacOS extend self
def gcc_42_build_version
`/usr/bin/gcc-4.2 -v 2>&1` =~ /build (\d{4,})/
if $1
$1.to_i
elsif system "/usr/bin/which gcc"
# Xcode 3.0 didn't come with gcc-4.2
# We can't change the above regex to use gcc because the version numbers
# are different and thus, not useful.
# FIXME I bet you 20 quid this causes a side effect — magic values tend to
401
else
nil
end
end
%w[ CFLAGS CXXFLAGS CPPFLAGS LDFLAGS MACOSX_DEPLOYMENT_TARGET MAKEFLAGS PKG_CONFIG_PATH
HOMEBREW_DEBUG HOMEBREW_VERBOSE HOMEBREW_USE_LLVM HOMEBREW_SVN ].each do |k|
value = env[k]
puts "#{k}: #{value}" if value
def gcc_40_build_version
`/usr/bin/gcc-4.0 -v 2>&1` =~ /build (\d{4,})/
if $1
$1.to_i
else
nil
end
end
def llvm_build_version
if MACOS_VERSION >= 10.6
xcode_path = `/usr/bin/xcode-select -print-path`.chomp
return nil if xcode_path.empty?
`#{xcode_path}/usr/bin/llvm-gcc -v 2>&1` =~ /LLVM build (\d{4,})/
$1.to_i
end
end
end
def x11_installed?
Pathname.new('/usr/X11/lib/libpng.dylib').exist?
end
end
def macports_or_fink_installed?
# See these issues for some history:
# http://github.com/mxcl/homebrew/issues/#issue/13
# http://github.com/mxcl/homebrew/issues/#issue/41
# http://github.com/mxcl/homebrew/issues/#issue/48
%w[port fink].each do |ponk|
path = `/usr/bin/which -s #{ponk}`
return ponk unless path.empty?
end
# we do the above check because macports can be relocated and fink may be
# able to be relocated in the future. This following check is because if
# fink and macports are not in the PATH but are still installed it can
# *still* break the build -- because some build scripts hardcode these paths:
%w[/sw/bin/fink /opt/local/bin/port].each do |ponk|
return ponk if File.exist? ponk
end
# finally, sometimes people make their MacPorts or Fink read-only so they
# can quickly test Homebrew out, but still in theory obey the README's
# advise to rename the root directory. This doesn't work, many build scripts
# error out when they try to read from these now unreadable directories.
%w[/sw /opt/local].each do |path|
path = Pathname.new(path)
return path if path.exist? and not path.readable?
end
false
end
end
module GitHub extend self
def issues_for_formula name
# bit basic as depends on the issue at github having the exact name of the
# formula in it. Which for stuff like objective-caml is unlikely. So we
# really should search for aliases too.
name = f.name if Formula === name
require 'open-uri'
require 'yaml'
issues = []
open "http://github.com/api/v2/yaml/issues/search/mxcl/homebrew/open/#{name}" do |f|
YAML::load(f.read)['issues'].each do |issue|
issues << 'http://github.com/mxcl/homebrew/issues/#issue/%s' % issue['number']
end
end
issues
rescue
[]
end
end

343
bin/brew
View File

@ -38,313 +38,48 @@ if MACOS_VERSION < 10.5
abort "Homebrew requires Leopard or higher. For Tiger support, see:\nhttp://github.com/sceaga/homebrew/tree/tiger"
end
def dump_config
require 'hardware'
sha = `cd #{HOMEBREW_REPOSITORY} && git rev-parse --verify HEAD 2> /dev/null`.chomp
sha = "(none)" if sha.empty?
bits = Hardware.bits
cores = Hardware.cores_as_words
kernel_arch = `uname -m`.chomp
system_ruby = Pathname.new("/usr/bin/ruby")
llvm, llvm_msg = _compiler_recommendation llvm_build, RECOMMENDED_LLVM
gcc_42, gcc_42_msg = _compiler_recommendation gcc_42_build, RECOMMENDED_GCC_42
gcc_40, gcc_40_msg = _compiler_recommendation gcc_40_build, RECOMMENDED_GCC_40
xcode = xcode_version || "?"
puts <<-EOS
HOMEBREW_VERSION: #{HOMEBREW_VERSION}
HEAD: #{sha}
HOMEBREW_PREFIX: #{HOMEBREW_PREFIX}
HOMEBREW_CELLAR: #{HOMEBREW_CELLAR}
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}
/usr/bin/ruby => #{system_ruby.realpath}
Xcode: #{xcode}
GCC-4.0: #{gcc_40 ? "build #{gcc_40}" : "N/A"} #{gcc_42_msg}
GCC-4.2: #{gcc_42 ? "build #{gcc_42}" : "N/A"} #{gcc_40_msg}
LLVM: #{llvm ? "build #{llvm}" : "N/A" } #{llvm_msg}
MacPorts or Fink? #{macports_or_fink_installed?}
X11 installed? #{x11_installed?}
EOS
def require? path
require path.to_s.chomp
rescue LoadError => e
# HACK :( because we should raise on syntax errors but
# not if the file doesn't exist. TODO make robust!
raise unless e.to_s.include? path
end
begin
require 'brew.h'
aliases = {'ls' => :list,
'homepage' => :home,
'-S' => :search,
'up' => :update,
'ln' => :link,
'rm' => :uninstall,
'remove' => :uninstall,
'configure' => :diy,
'abv' => :info,
'dr' => :doctor,
'--repo' => '--repository'}
case arg = ARGV.shift
when '--cache'
if ARGV.named.empty?
puts HOMEBREW_CACHE
else
puts ARGV.formulae.collect {|f| f.cached_download}
cmd = ARGV.shift
cmd = aliases[cmd] if aliases[cmd]
# Add example external commands to PATH before checking.
ENV['PATH'] += ":#{HOMEBREW_REPOSITORY}/Library/Contributions/examples"
if system "/usr/bin/which -s brew-#{cmd}"
%w[CACHE CELLAR LIBRARY_PATH PREFIX REPOSITORY].each do |e|
ENV["HOMEBREW_#{e}"] = eval "HOMEBREW_#{e}"
end
when '--prefix'
if ARGV.named.empty?
puts HOMEBREW_PREFIX
else
puts ARGV.formulae.collect {|f| f.prefix}
end
when '--repository'
puts HOMEBREW_REPOSITORY
when '--cellar'
if ARGV.named.empty?
puts HOMEBREW_CELLAR
else
puts ARGV.formulae.collect {|f| HOMEBREW_CELLAR+f.name}
end
when '--config'
dump_config
when '--env'
require 'hardware'
require 'extend/ENV'
ENV.extend(HomebrewEnvExtension)
ENV.setup_build_environment
dump_build_env ENV
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.flag? '--versions'
if ARGV.named.empty?
to_list = HOMEBREW_CELLAR.children.select { |pn| pn.directory? }
else
to_list = ARGV.named.collect { |n| HOMEBREW_CELLAR+n }.select { |pn| pn.exist? }
end
to_list.each do |d|
versions = d.children.select { |pn| pn.directory? }.collect { |pn| pn.basename.to_s }
puts "#{d.basename} #{versions *' '}"
end
elsif ARGV.named.empty?
ENV['CLICOLOR']=nil
exec 'ls', *ARGV.options_only<<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'
if ARGV.include? '--macports'
exec "open", "http://www.macports.org/ports.php?by=name&substr=#{ARGV.next}"
elsif ARGV.include? '--fink'
exec "open", "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}"
end
check_for_blacklisted_formula(ARGV.named)
puts_columns search_brews(ARGV.first)
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
require 'formula'
# Don't use ARGV.formulae as that will throw if the file doesn't parse
paths = ARGV.named.collect do |name|
path = Formula.path(Formula.resolve_alias(name))
unless File.exist? path
raise FormulaUnavailableError, name
else
path
end
end
exec_editor(*paths)
end
when 'up', 'update'
abort "Please `brew install git' first." unless system "/usr/bin/which -s git"
require 'update'
updater = RefreshBrew.new
unless updater.update_from_masterbrew!
puts "Already up-to-date."
else
updater.report
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'
if ARGV.flag? "--force"
require 'formula'
ARGV.formulae.each do |f|
formula_cellar = f.prefix.parent
next unless File.exist? formula_cellar
puts "Uninstalling #{f.name}..."
formula_cellar.children do |k|
keg = Keg.new(k)
keg.unlink
end
formula_cellar.rmtree
end
else
begin
ARGV.kegs.each do |keg|
puts "Uninstalling #{keg}..."
keg.unlink
keg.uninstall
end
rescue MultipleVersionsInstalledError => e
onoe e
puts "Use `brew remove --force #{e.name}` to remove all versions."
end
end
when 'prune'
prune
when 'create'
if ARGV.include? '--macports'
exec "open", "http://www.macports.org/ports.php?by=name&substr=#{ARGV.next}"
elsif ARGV.include? '--fink'
exec "open", "http://pdb.finkproject.org/pdb/browse.php?summary=#{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'
Formula.all.each do |f|
info f
puts '---'
end
else
puts `ls #{HOMEBREW_CELLAR} | wc -l`.strip+" kegs, "+HOMEBREW_CELLAR.abv
end
elsif ARGV[0][0..6] == 'http://' or ARGV[0][0..7] == 'https://' or ARGV[0][0..5] == 'ftp://'
path = Pathname.new(ARGV.shift)
/(.*?)[-_.]?#{path.version}/.match path.basename
unless $1.to_s.empty?
name = $1
else
name = path.stem
end
puts "#{name} #{path.version}"
else
ARGV.formulae.each{ |f| info f }
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)
brew_install
when 'log'
Dir.chdir HOMEBREW_REPOSITORY
args = ARGV.options_only
args += ARGV.formulae.map{ |formula| formula.path } unless ARGV.named.empty?
exec "git", "log", *args
# For each formula given, show which other formulas depend on it.
# We only go one level up, ie. direct dependencies.
when 'uses'
uses = ARGV.formulae.map{ |f| Formula.all.select{ |ff| ff.deps.include? f.name }.map{|f| f.name} }.flatten.uniq
if ARGV.include? "--installed"
uses = uses.select { |f| f = HOMEBREW_CELLAR+f; f.directory? and not f.subdirs.empty? }
end
puts uses.sort
when 'deps'
if ARGV.include?('--all')
require 'formula'
Formula.all.each do |f|
puts "#{f.name}:#{f.deps.join(' ')}"
end
elsif ARGV.include?("-1") or ARGV.include?("--1")
puts ARGV.formulae.map {|f| f.deps or []}.flatten.uniq.sort
else
require 'formula_installer'
puts ARGV.formulae.map {|f| FormulaInstaller.expand_deps(f).map {|f| f.name} }.flatten.uniq.sort
end
when 'cat'
Dir.chdir HOMEBREW_REPOSITORY
exec "cat", ARGV.formulae.first.path, *ARGV.options_only
when 'outdated'
outdated_brews.each do |keg, name, version|
if $stdout.tty? and not ARGV.flag? '--quiet'
versions = keg.cd{ Dir['*'] }.join(', ')
puts "#{name} (#{versions} < #{version})"
else
puts name
end
end
when 'doctor', 'dr'
require 'brew_doctor'
brew_doctor
exec "brew-#{cmd}", *ARGV
elsif require? `/usr/bin/which brew-#{cmd}.rb`
exit 0
elsif require? HOMEBREW_REPOSITORY/"Library/Homebrew/cmd"/cmd
Homebrew.send cmd.to_s.gsub('-', '_')
else
# Add example external commands to PATH before checking.
ENV['PATH'] += ":#{HOMEBREW_REPOSITORY}/Library/Contributions/examples"
# Check for an external shell command
if system "/usr/bin/which -s brew-#{arg}"
# Add some Homebrew vars to the ENV
%w(CACHE CELLAR LIBRARY_PATH PREFIX REPOSITORY).each do |e|
ENV["HOMEBREW_#{e}"] = eval("HOMEBREW_#{e}")
end
exec("brew-#{arg}", *ARGV)
end
# Check for an external ruby command
external_rb = `/usr/bin/which brew-#{arg}.rb`.chomp
unless external_rb.empty?
require external_rb
exit 0
end
# Check for git commands
if %w(branch checkout pull push rebase reset).include? arg
onoe "Unknown command '#{arg}' (did you mean 'git #{arg}'?)"
if %w[branch checkout pull push rebase reset].include? cmd
onoe "Unknown command: #{cmd} (did you mean `git #{cmd}'?)"
else
onoe "Unknown command '#{arg}'"
onoe "Unknown command: #{cmd}"
end
end
@ -362,24 +97,28 @@ rescue Interrupt => e
puts # seemingly a newline is typical
exit 130
rescue BuildError => e
require 'cmd/--config'
require 'cmd/--env'
e.backtrace[1] =~ %r{Library/Formula/(.+)\.rb:(\d+)}
formula_name = $1
error_line = $2
puts "Exit status: #{e.exit_status}"
puts
puts "http://github.com/mxcl/homebrew/blob/master/Library/Formula/#{formula_name}.rb#L#{error_line}"
puts
ohai "Environment"
dump_config
puts Homebrew.config_s
puts
ohai "Build Flags"
dump_build_env e.env
Homebrew.dump_build_env e.env
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 existing issues..." and
# then replace that string with the following when the github api returns
issues = issues_for_formula(formula_name)
issues = GitHub.issues_for_formula formula_name
puts "These existing issues may help you:", *issues unless issues.empty?
if e.was_running_configure?
puts "It looks like an autotools configure failed."