aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Library/Homebrew/brewkit.rb371
-rw-r--r--Library/Homebrew/env.rb95
-rw-r--r--Library/Homebrew/formula.rb263
-rw-r--r--Library/Homebrew/keg.rb153
-rwxr-xr-xLibrary/Homebrew/unittest.rb2
-rw-r--r--README33
-rwxr-xr-xbin/brew378
7 files changed, 682 insertions, 613 deletions
diff --git a/Library/Homebrew/brewkit.rb b/Library/Homebrew/brewkit.rb
index a32f015d5..f5d819f1b 100644
--- a/Library/Homebrew/brewkit.rb
+++ b/Library/Homebrew/brewkit.rb
@@ -15,9 +15,8 @@
# You should have received a copy of the GNU General Public License
# along with Homebrew. If not, see <http://www.gnu.org/licenses/>.
-require 'pathname'
require 'osx/cocoa' # to get number of cores
-require 'env'
+require 'formula'
# optimise all the way to eleven, references:
# http://en.gentoo-wiki.com/wiki/Safe_Cflags/Intel
@@ -38,343 +37,7 @@ unless $root.to_s == '/usr/local'
end
-def ohai title
- n=`tput cols`.strip.to_i-4
- puts "\033[0;34m==>\033[0;0;1m #{title[0,n]}\033[0;0m"
-end
-
-def cache
- cache=File.expand_path "~/Library/Caches/Homebrew"
- FileUtils.mkpath cache
- return cache
-end
-
-class BuildError <RuntimeError
- def initialize cmd
- super "Build failed during: #{cmd}"
- end
-end
-
-# pass in the basename of the filename _without_ any file extension
-def extract_version basename
- # eg. boost_1_39_0
- /((\d+_)+\d+)$/.match basename
- return $1.gsub('_', '.') if $1
-
- # eg. foobar-4.5.1-1
- /-((\d+\.)*\d+-\d+)$/.match basename
- return $1 if $1
-
- # eg. foobar-4.5.1
- /-((\d+\.)*\d+)$/.match basename
- return $1 if $1
-
- # eg. foobar-4.5.1b
- /-((\d+\.)*\d+([abc]|rc\d))$/.match basename
- return $1 if $1
-
- # eg foobar-4.5.0-beta1
- /-((\d+\.)*\d+-beta\d+)$/.match basename
- return $1 if $1
-
- # eg. foobar4.5.1
- /((\d+\.)*\d+)$/.match basename
- return $1 if $1
-
- # eg. otp_src_R13B (this is erlang's style)
- # eg. astyle_1.23_macosx.tar.gz
- basename.scan /_([^_]+)/ do |match|
- return match.first if /\d/.match $1
- end
-end
-
-
-# make our code neater
-class Pathname
- def mv dst
- FileUtils.mv to_s, dst
- end
-
- def rename dst
- dst=Pathname.new dst
- dst.unlink if dst.exist?
- mv dst
- end
-
- def install src
- if src.is_a? Array
- src.each {|src| install src }
- elsif File.exist? src
- mkpath
- if File.symlink? src
- # we cp symlinks because FileUtils.mv is shit and won't mv a symlink
- # if its final destination has an invalid target! FFS. Ruby is shit.
- FileUtils.cp src, to_s
- else
- # we mv when possible as it is faster and you should only be using
- # this function when installing from the temporary build directory
- FileUtils.mv src, to_s
- end
- end
- end
-
- def cp dst
- if file?
- FileUtils.cp to_s, dst
- else
- FileUtils.cp_r to_s, dst
- end
- end
-
- # for filetypes we support
- def extname
- /\.(zip|tar\.(gz|bz2)|tgz)$/.match to_s
- return ".#{$1}" if $1
- return File.extname(to_s)
- end
-
- # for filetypes we support, basename without extension
- def stem
- return File.basename(to_s, extname)
- end
-end
-
-
-# the base class variety of formula, you don't get a prefix, so it's not really
-# useful. See the derived classes for fun and games.
-class AbstractFormula
- require 'find'
- require 'fileutils'
-
- # fuck knows, ruby is weird
- # TODO please fix!
- def self.url
- @url
- end
- def url
- self.class.url
- end
- def self.md5
- @md5
- end
- def md5
- self.class.md5
- end
- def self.homepage
- @homepage
- end
- def homepage
- self.class.homepage
- end
- # end ruby is weird section
-
- def version
- @version
- end
- def name
- @name
- end
-
- def initialize name=nil
- @name=name
- # fuck knows, ruby is weird
- @url=url if @url.nil?
- raise "@url.nil?" if @url.nil?
- @md5=md5 if @md5.nil?
- # end ruby is weird section
- end
-
-public
- def prefix
- raise "@name.nil!" if @name.nil?
- raise "@version.nil?" if @version.nil?
- $cellar+@name+@version
- end
- def bin
- prefix+'bin'
- end
- def doc
- prefix+'share'+'doc'+name
- end
- def man
- prefix+'share'+'man'
- end
- def man1
- prefix+'share'+'man'+'man1'
- end
- def lib
- prefix+'lib'
- end
- def include
- prefix+'include'
- end
-
- def caveats
- nil
- end
-
- # yields self with current working directory set to the uncompressed tarball
- def brew
- ohai "Downloading #{@url}"
- Dir.chdir cache do
- tmp=tgz=nil
- begin
- tgz=Pathname.new(fetch()).realpath
- md5=`md5 -q "#{tgz}"`.strip
- raise "MD5 mismatch: #{md5}" unless @md5 and md5 == @md5.downcase
-
- # we make an additional subdirectory so know exactly what we are
- # recursively deleting later
- # we use mktemp rather than appsupport/blah because some build scripts
- # can't handle being built in a directory with spaces in it :P
- tmp=`mktemp -dt #{File.basename @url}`.strip
- Dir.chdir tmp do
- Dir.chdir uncompress(tgz) do
- yield self
- end
- end
- rescue Interrupt, RuntimeError
- if ARGV.include? '--debug'
- # debug mode allows the packager to intercept a failed build and
- # investigate the problems
- puts "Rescued build at: #{tmp}"
- exit! 1
- else
- raise
- end
- ensure
- FileUtils.rm_rf tmp if tmp
- end
- end
- end
-
- def clean
- #TODO strip libexec too
- [bin,lib].each {|path| path.find do |path|
- if not path.file?
- next
- elsif path.extname == '.la'
- # .la files are stupid
- path.unlink
- else
- fo=`file -h #{path}`
- args=nil
- perms=0444
- if fo =~ /Mach-O dynamically linked shared library/
- args='-SxX'
- elsif fo =~ /Mach-O executable/ # defaults strip everything
- args='' # still do the strip
- perms=0544
- elsif fo =~ /script text executable/
- perms=0544
- end
- if args
- puts "Stripping: #{path}" if ARGV.include? '--verbose'
- path.chmod 0644 # so we can strip
- unless path.stat.nlink > 1
- `strip #{args} #{path}`
- else
- # strip unlinks the file and recreates it, thus breaking hard links!
- # is this expected behaviour? patch does it too… still,mktm this fixes it
- tmp=`mktemp -t #{path.basename}`.strip
- `strip -o #{tmp} #{path}`
- `cat #{tmp} > #{path}`
- File.unlink tmp
- end
- end
- path.chmod perms
- end
- end}
-
- # remove empty directories
- `perl -MFile::Find -e"finddepth(sub{rmdir},'#{prefix}')"`
- end
-
-protected
- def uncompress path
- path.dirname
- end
-
-private
- def fetch
- %r[http://(www.)?github.com/.*/(zip|tar)ball/].match @url
- if $2
- # curl doesn't do the redirect magic that we would like, so we get a
- # stupidly named file, this is why wget would be beter, but oh well
- tgz="#{@name}-#{@version}.#{$2=='tar' ? 'tgz' : $2}"
- oarg="-o #{tgz}"
- else
- oarg='-O' #use the filename that curl gets
- tgz=File.expand_path File.basename(@url)
- end
-
- agent="Homebrew #{HOMEBREW_VERSION} (Ruby #{VERSION}; Mac OS X 10.5 Leopard)"
-
- unless File.exists? tgz
- `curl -#LA "#{agent}" #{oarg} "#{@url}"`
- raise "Download failed" unless $? == 0
- else
- puts "File already downloaded and cached"
- end
- return tgz
- end
-
- def method_added method
- raise 'You cannot override Formula.brew' if method == 'brew'
- end
-end
-
-# somewhat useful, it'll raise if you call prefix, but it'll unpack a tar/zip
-# for you, check the md5, and allow you to yield from brew
-class UnidentifiedFormula <AbstractFormula
- def initialize name=nil
- super name
- end
-
-private
- def uncompress(path)
- if path.extname == '.zip'
- `unzip -qq "#{path}"`
- else
- `tar xf "#{path}"`
- end
-
- raise "Compression tool failed" if $? != 0
-
- entries=Dir['*']
- if entries.nil? or entries.length == 0
- raise "Empty tarball!"
- elsif entries.length == 1
- # if one dir enter it as that will be where the build is
- entries.first
- else
- # if there's more than one dir, then this is the build directory already
- Dir.pwd
- end
- end
-end
-
-# this is what you will mostly use, reimplement install, prefix won't raise
-class Formula <UnidentifiedFormula
- def initialize name
- super name
- @version=extract_version Pathname.new(File.basename(@url)).stem unless @version
- end
-end
-
-# see ack.rb for an example usage
-class ScriptFileFormula <AbstractFormula
- def install
- bin.install name
- end
-end
-
-class GithubGistFormula <ScriptFileFormula
- def initialize
- super File.basename(url)
- @version=File.basename(File.dirname(url))[0,6]
- end
-end
+######################################################################## utils
def inreplace(path, before, after)
before=before.to_s.gsub('"', '\"').gsub('/', '\/')
@@ -385,34 +48,6 @@ def inreplace(path, before, after)
#TODO optimise it by taking before and after as arrays
#Bah, just make the script writers do it themselves with a standard collect block
#TODO use ed -- less to escape
+ #TODO the above doesn't escape all regexp symbols!
`perl -pi -e "s/#{before}/#{after}/g" "#{path}"`
end
-
-def system cmd
- ohai cmd
-
- out=''
- IO.popen("#{cmd} 2>&1") do |f|
- until f.eof?
- s=f.gets
- if ARGV.include? '--verbose'
- puts s
- else
- out+=s
- end
- end
- end
-
- unless $? == 0
- puts out unless ARGV.include? '--verbose' #already did that above
- raise BuildError.new(cmd)
- end
-end
-
-####################################################################### script
-if $0 == __FILE__
- d=$cellar.parent+'bin'
- d.mkpath unless d.exist?
- Dir.chdir d
- Pathname.new('brew').make_symlink Pathname.new('../Cellar')+'homebrew'+'brew'
-end \ No newline at end of file
diff --git a/Library/Homebrew/env.rb b/Library/Homebrew/env.rb
index 951d9f9dc..dbba19e8a 100644
--- a/Library/Homebrew/env.rb
+++ b/Library/Homebrew/env.rb
@@ -21,4 +21,97 @@ $root=Pathname.new(__FILE__).dirname.parent.parent.realpath
$formula=$root+'Library'+'Formula'
$cellar=$root+'Cellar'
-HOMEBREW_VERSION='0.2' \ No newline at end of file
+HOMEBREW_VERSION='0.3'
+HOMEBREW_CACHE=File.expand_path "~/Library/Caches/Homebrew"
+
+
+######################################################################## utils
+def ohai title
+ n=`tput cols`.strip.to_i-4
+ puts "\033[0;34m==>\033[0;0;1m #{title[0,n]}\033[0;0m"
+end
+
+
+############################################################### class Pathname
+# we enhance Pathname to make our code more legible
+# of course this kind of thing is evil, but meh
+class Pathname
+ def mv dst
+ FileUtils.mv to_s, dst
+ end
+
+ def rename dst
+ dst=Pathname.new dst
+ dst.unlink if dst.exist?
+ mv dst
+ end
+
+ def install src
+ if src.is_a? Array
+ src.each {|src| install src }
+ elsif File.exist? src
+ mkpath
+ if File.symlink? src
+ # we use the BSD mv command because FileUtils copies the target and
+ # not the link! I'm beginning to wish I'd used Python quite honestly!
+ `mv #{src} #{to_s}`
+ else
+ # we mv when possible as it is faster and you should only be using
+ # this function when installing from the temporary build directory
+ FileUtils.mv src, to_s
+ end
+ end
+ end
+
+ def cp dst
+ if file?
+ FileUtils.cp to_s, dst
+ else
+ FileUtils.cp_r to_s, dst
+ end
+ end
+
+ # extended to support the double extensions .tar.gz and .tar.bz2
+ def extname
+ /(\.tar\.(gz|bz2))$/.match to_s
+ return $1 if $1
+ return File.extname(to_s)
+ end
+
+ # for filetypes we support, basename without extension
+ def stem
+ return File.basename(to_s, extname)
+ end
+
+ def version
+ # eg. boost_1_39_0
+ /((\d+_)+\d+)$/.match stem
+ return $1.gsub('_', '.') if $1
+
+ # eg. foobar-4.5.1-1
+ /-((\d+\.)*\d+-\d+)$/.match stem
+ return $1 if $1
+
+ # eg. foobar-4.5.1
+ /-((\d+\.)*\d+)$/.match stem
+ return $1 if $1
+
+ # eg. foobar-4.5.1b
+ /-((\d+\.)*\d+([abc]|rc\d))$/.match stem
+ return $1 if $1
+
+ # eg foobar-4.5.0-beta1
+ /-((\d+\.)*\d+-beta\d+)$/.match stem
+ return $1 if $1
+
+ # eg. foobar4.5.1
+ /((\d+\.)*\d+)$/.match stem
+ return $1 if $1
+
+ # eg. otp_src_R13B (this is erlang's style)
+ # eg. astyle_1.23_macosx.tar.gz
+ stem.scan /_([^_]+)/ do |match|
+ return match.first if /\d/.match $1
+ end
+ end
+end
diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb
new file mode 100644
index 000000000..443ab1de1
--- /dev/null
+++ b/Library/Homebrew/formula.rb
@@ -0,0 +1,263 @@
+# Copyright 2009 Max Howell <max@methylblue.com>
+#
+# This file is part of Homebrew.
+#
+# Homebrew is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Homebrew is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Homebrew. If not, see <http://www.gnu.org/licenses/>.
+
+require 'env'
+
+class BuildError <RuntimeError
+ def initialize cmd
+ super "Build failed during: #{cmd}"
+ end
+end
+
+# the base class variety of formula, you don't get a prefix, so it's not really
+# useful. See the derived classes for fun and games.
+class AbstractFormula
+ require 'find'
+ require 'fileutils'
+
+ # fuck knows, ruby is weird
+ # TODO please fix!
+ def self.url
+ @url
+ end
+ def url
+ self.class.url
+ end
+ def self.md5
+ @md5
+ end
+ def md5
+ self.class.md5
+ end
+ def self.homepage
+ @homepage
+ end
+ def homepage
+ self.class.homepage
+ end
+ # end ruby is weird section
+
+ def version
+ @version
+ end
+ def name
+ @name
+ end
+
+ # if the dir is there, but it's empty we consider it not installed
+ def installed?
+ return prefix.children.count > 0
+ rescue
+ return false
+ end
+
+ def initialize name=nil
+ @name=name
+ # fuck knows, ruby is weird
+ @url=url if @url.nil?
+ raise "@url.nil?" if @url.nil?
+ @md5=md5 if @md5.nil?
+ # end ruby is weird section
+ end
+
+public
+ def prefix
+ raise "@name.nil!" if @name.nil?
+ raise "@version.nil?" if @version.nil?
+ $cellar+@name+@version
+ end
+ def bin
+ prefix+'bin'
+ end
+ def doc
+ prefix+'share'+'doc'+name
+ end
+ def man
+ prefix+'share'+'man'
+ end
+ def man1
+ man+'man1'
+ end
+ def lib
+ prefix+'lib'
+ end
+ def include
+ prefix+'include'
+ end
+
+ def caveats
+ nil
+ end
+
+ # Pretty titles the command and buffers stdout/stderr
+ # Throws if there's an error
+ def system cmd
+ ohai cmd
+ if ARGV.include? '--verbose'
+ Kernel.system cmd
+ else
+ out=''
+ IO.popen "#{cmd} 2>&1" do |f|
+ until f.eof?
+ out+=f.gets
+ end
+ end
+ puts out unless $? == 0
+ end
+
+ raise BuildError.new(cmd) unless $? == 0
+ end
+
+ # yields self with current working directory set to the uncompressed tarball
+ def brew
+ ohai "Downloading #{@url}"
+ FileUtils.mkpath HOMEBREW_CACHE
+ Dir.chdir HOMEBREW_CACHE do
+ tmp=tgz=nil
+ begin
+ tgz=Pathname.new(fetch()).realpath
+ md5=`md5 -q "#{tgz}"`.strip
+ raise "MD5 mismatch: #{md5}" unless @md5 and md5 == @md5.downcase
+
+ # we make an additional subdirectory so know exactly what we are
+ # recursively deleting later
+ # we use mktemp rather than appsupport/blah because some build scripts
+ # can't handle being built in a directory with spaces in it :P
+ tmp=`mktemp -dt #{File.basename @url}`.strip
+ Dir.chdir tmp do
+ Dir.chdir uncompress(tgz) do
+ yield self
+ end
+ end
+ rescue Interrupt, RuntimeError
+ if ARGV.include? '--debug'
+ # debug mode allows the packager to intercept a failed build and
+ # investigate the problems
+ puts "Rescued build at: #{tmp}"
+ exit! 1
+ else
+ raise
+ end
+ ensure
+ FileUtils.rm_rf tmp if tmp
+ end
+ end
+ end
+
+protected
+ # returns the directory where the archive was uncompressed
+ # in this Abstract case we assume there is no archive
+ def uncompress path
+ path.dirname
+ end
+
+private
+ def fetch
+ %r[http://(www.)?github.com/.*/(zip|tar)ball/].match @url
+ if $2
+ # curl doesn't do the redirect magic that we would like, so we get a
+ # stupidly named file, this is why wget would be beter, but oh well
+ tgz="#{@name}-#{@version}.#{$2=='tar' ? 'tgz' : $2}"
+ oarg="-o #{tgz}"
+ else
+ oarg='-O' #use the filename that curl gets
+ tgz=File.expand_path File.basename(@url)
+ end
+
+ agent="Homebrew #{HOMEBREW_VERSION} (Ruby #{VERSION}; Mac OS X 10.5 Leopard)"
+
+ unless File.exists? tgz
+ `curl -#LA "#{agent}" #{oarg} "#{@url}"`
+ raise "Download failed" unless $? == 0
+ else
+ puts "File already downloaded and cached"
+ end
+ return tgz
+ end
+
+ def method_added method
+ raise 'You cannot override Formula.brew' if method == 'brew'
+ end
+end
+
+# somewhat useful, it'll raise if you call prefix, but it'll unpack a tar/zip
+# for you, check the md5, and allow you to yield from brew
+class UnidentifiedFormula <AbstractFormula
+ def initialize name=nil
+ super name
+ end
+
+private
+ def uncompress(path)
+ if path.extname == '.zip'
+ `unzip -qq "#{path}"`
+ else
+ `tar xf "#{path}"`
+ end
+
+ raise "Compression tool failed" if $? != 0
+
+ entries=Dir['*']
+ if entries.nil? or entries.length == 0
+ raise "Empty tarball!"
+ elsif entries.length == 1
+ # if one dir enter it as that will be where the build is
+ entries.first
+ else
+ # if there's more than one dir, then this is the build directory already
+ Dir.pwd
+ end
+ end
+end
+
+# this is what you will mostly use, reimplement install, prefix won't raise
+class Formula <UnidentifiedFormula
+ def initialize name
+ super name
+ @version=Pathname.new(@url).version unless @version
+ end
+
+ def self.class name
+ #remove invalid characters and camelcase
+ name.capitalize.gsub(/[-_\s]([a-zA-Z0-9])/) { $1.upcase }
+ end
+
+ def self.path name
+ $formula+(name.downcase+'.rb')
+ end
+
+ def self.create name
+ require Formula.path(name)
+ return eval(Formula.class(name)).new(name)
+ rescue
+ raise "No formula for #{name}"
+ end
+end
+
+# see ack.rb for an example usage
+class ScriptFileFormula <AbstractFormula
+ def install
+ bin.install name
+ end
+end
+
+class GithubGistFormula <ScriptFileFormula
+ def initialize
+ super File.basename(url)
+ @version=File.basename(File.dirname(url))[0,6]
+ end
+end
diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb
new file mode 100644
index 000000000..8330c650f
--- /dev/null
+++ b/Library/Homebrew/keg.rb
@@ -0,0 +1,153 @@
+# Copyright 2009 Max Howell <max@methylblue.com>
+#
+# This file is part of Homebrew.
+#
+# Homebrew is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Homebrew is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Homebrew. If not, see <http://www.gnu.org/licenses/>.
+
+require 'env'
+require 'formula'
+
+class Keg
+ attr_reader :path, :version, :name
+
+ def initialize formula
+ if formula.is_a? AbstractFormula
+ @path=formula.prefix
+ @name=formula.name
+ @version=formula.version
+ elsif formula.is_a? Pathname
+ # TODO
+ elsif formula.is_a? String
+ kids=($cellar+formula).children
+ raise "Multiple versions installed" if kids.length > 1
+ @path=kids[0]
+ @name=formula
+ @version=@path.basename
+ end
+ end
+
+ def clean
+ # TODO unset write permission more
+ %w[bin lib].each {|d| (Pathname.new(path)+d).find do |path|
+ if not path.file?
+ next
+ elsif path.extname == '.la'
+ # .la files are stupid
+ path.unlink
+ else
+ fo=`file -h #{path}`
+ args=nil
+ perms=0444
+ if fo =~ /Mach-O dynamically linked shared library/
+ args='-SxX'
+ elsif fo =~ /Mach-O executable/ # defaults strip everything
+ args='' # still do the strip
+ perms=0544
+ elsif fo =~ /script text executable/
+ perms=0544
+ end
+ if args
+ puts "Stripping: #{path}" if ARGV.include? '--verbose'
+ path.chmod 0644 # so we can strip
+ unless path.stat.nlink > 1
+ `strip #{args} #{path}`
+ else
+ # strip unlinks the file and recreates it, thus breaking hard links!
+ # is this expected behaviour? patch does it too… still,mktm this fixes it
+ tmp=`mktemp -t #{path.basename}`.strip
+ `strip -o #{tmp} #{path}`
+ `cat #{tmp} > #{path}`
+ File.unlink tmp
+ end
+ end
+ path.chmod perms
+ end
+ end}
+
+ # remove empty directories TODO Rubyize!
+ `perl -MFile::Find -e"finddepth(sub{rmdir},'#{path}')"`
+ end
+
+ def rm
+ if path.directory?
+ FileUtils.chmod_R 0777, path # ensure we have permission to delete
+ path.rmtree
+ end
+ end
+
+private
+ def __symlink_relative_to from, to
+ tod=to.dirname
+ tod.mkpath
+ Dir.chdir(tod) do
+ #TODO use Ruby function so we get exceptions
+ #NOTE Ruby functions are fucked up!
+ `ln -sf "#{from.relative_path_from tod}"`
+ @n+=1
+ end
+ end
+
+ # symlinks a directory recursively into our FHS tree
+ def __ln start
+ start=path+start
+ return unless start.directory?
+
+ start.find do |from|
+ next if from == start
+
+ prune=false
+
+ relative_path=from.relative_path_from path
+ to=$root+relative_path
+
+ if from.file?
+ __symlink_relative_to from, to
+ elsif from.directory?
+ # no need to put .app bundles in the path, the user can just use
+ # spotlight, or the open command and actual mac apps use an equivalent
+ Find.prune if from.extname.to_s == '.app'
+
+ branch=from.relative_path_from start
+
+ case yield branch when :skip
+ Find.prune
+ when :mkpath
+ to.mkpath
+ @n+=1
+ else
+ __symlink_relative_to from, to
+ Find.prune
+ end
+ end
+ end
+ end
+
+public
+ def ln
+ # yeah indeed, you have to force anything you need in the main tree into
+ # these dirs REMEMBER that *NOT* everything needs to be in the main tree
+ # TODO consider using hardlinks
+ @n=0
+
+ __ln('etc') {:mkpath}
+ __ln('bin') {:link}
+ __ln('lib') {|path| :mkpath if ['pkgconfig','php'].include? path.to_s}
+ __ln('include') {:link}
+
+ mkpaths=(1..9).collect {|x| "man/man#{x}"} <<'man'<<'doc'<<'locale'<<'info'<<'aclocal'
+ __ln('share') {|path| :mkpath if mkpaths.include? path.to_s}
+
+ return @n
+ end
+end \ No newline at end of file
diff --git a/Library/Homebrew/unittest.rb b/Library/Homebrew/unittest.rb
index 84f0d3f81..7b15b2949 100755
--- a/Library/Homebrew/unittest.rb
+++ b/Library/Homebrew/unittest.rb
@@ -2,7 +2,7 @@
$:.unshift File.dirname(__FILE__)
require 'test/unit'
-require 'brewkit'
+require 'formula'
require 'stringio'
class TestFormula <Formula
diff --git a/README b/README
index 233d799bf..3a327070c 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
Homebrew
========
-Homebrew's purpose is the same as MacPorts or Fink, ie. to let you easily
-install other open source software on your Mac.
+Homebrew's purpose is basically the same as MacPorts or Fink, ie. to let you
+easily install other open source software on your Mac.
Here's why you may prefer Homebrew to the alternatives:
@@ -34,30 +34,43 @@ Here's why you may prefer Homebrew to the alternatives:
Homebrew will automatically open it for you to tweak with TextMate or
$EDITOR.
- Or skip going via a package entirely, just install into the Cellar and use
- "brew ln" to symlink it into the main tree.
+6. DIY package installation
+ MacPorts doesn't support the beta version? Need an older version? Need
+ custom compile flags? The Homebrew toolchain is carefully segregated so
+ you can just build your own stuff while still reaping the benefits of
+ package management.
-6. Optimisation
+ Just install to the Cellar and then call brew ln to symlink that
+ installation into /usr/local, eg.
+
+ ./configure --prefix=/usr/local/Cellar/wget/1.10
+ make install
+ brew ln wget
+
+ This means you can also install multiple versions of the same package and
+ switch on demand.
+
+7. Optimisation
We optimise for Leopard Intel, binaries are stripped, compile flags
tweaked. Nobody wants crappy, slow software. Apart from MacPorts and Fink.
-7. Integration with existing OS X technologies
+8. Integration with existing OS X technologies
Homebrew integrates with Ruby gems, CPAN and Python disttools. These tools
exist already and do the job great. We don't reinvent the wheel, we just
improve it by making these tools install with more management options.
-8. Complimenting what OS X already has
+9. Complimenting what OS X already has
Macports is an autarky. You get a duplicate copy of libz, OpenSSL, Python
etc. They do this to support OS X Tiger, etc. more easily. We don't support
Tiger, we duplicate nothing. Homebrew compliments OS X, it doesn't seek to
operate independently of it.
-9. Homebrew has a beer theme
+A. Homebrew has a beer theme
Beer goggles will help you to evangelise Homebrew more effectively.
-X. Homebrew helps you get chicks
+B. Homebrew helps get you chicks
There's no conclusive scientific evidence as yet, but I firmly believe it's
- just a matter of statistics and time.
+ just a matter of time and statistics.
I know I've made it sound so awesome you can hardly wait to rip MacPorts out
and embrace the fresh hoppy taste of Homebrew, but I should point out that it
diff --git a/bin/brew b/bin/brew
index 4ec8a6468..d40296768 100755
--- a/bin/brew
+++ b/bin/brew
@@ -8,6 +8,7 @@ require 'env'
# often causes Ruby to throw exception ffs
Dir.chdir '/' unless File.directory? ENV['PWD']
+######################################################################## funcs
def prune
n=0
dirs=Array.new
@@ -37,130 +38,113 @@ def prune
return n
end
-def formulize name
- name=Pathname.new name
- return name if name.directory? and name.parent.realpath == $cellar
- return File.basename(name, '.rb') if name.file? and name.extname == '.rb' and name.parent.realpath == $formula
-
- name=name.to_s
- raise "#{name} is an invalid name for a formula" if name.include? '/'
-
- return name if ($formula+(name+'.rb')).file?
- return name if ($cellar+name).directory?
-
- raise "No formula or keg for #{name} found"
-end
-
-def shift_formulae_from_ARGV
- fae=Array.new
- i=0
- while name=ARGV[i]
- unless name[0,1] == '-'
- fae<<formulize(ARGV.shift).to_s
+# we actually remove formulae from ARGV so that any other analysis of ARGV
+# only includes relevent arguments
+# TODO require will throw if no formula, so we should catch no?
+def extract_named_args
+ args=Array.new
+ ARGV.delete_if do |arg|
+ if arg[0,1] == '-'
+ false
else
- i+=1
+ args<<arg
+ true
end
end
- raise "You must specify a formula" if fae.empty?
- return fae
-end
-
-def __class name
- #remove invalid characters and camelcase
- name.capitalize.gsub(/[-_\s]([a-zA-Z0-9])/) { $1.upcase }
-end
-
-def __rb name
- $formula+(name+'.rb')
+ return args
end
-def __obj name
- require "#{__rb name}"
- return eval(__class(name)).new(name)
+def extract_kegs
+ require 'keg'
+ kegs=extract_named_args.collect {|name| Keg.new name}
+ raise "Expecting the name of a keg or formula, eg:\n\tbrew #{ARGV.join ' '} wget" if kegs.empty?
+ return kegs
end
-def rm keg
- #TODO if multiple versions don't rm all unless --force
- path=$cellar+keg
- `chmod -R u+rw #{path}` # we leave things read only
- path.rmtree
- puts "#{path} removed (#{prune} files)"
+def abv keg=nil
+ path=keg ? keg.path : $cellar
+ if path.directory?
+ `find #{path} -type f | wc -l`.strip+' files, '+`du -hd0 #{path} | cut -d"\t" -f1`.strip
+ else
+ nil
+ end
end
-def ln name
- keg=$cellar+name
- keg=keg.realpath
-
- if keg.parent.parent == $root
- # we are one dir too high
- kids=keg.children
- raise "#{keg} is empty :(" if kids.length == 0
- raise "There are multiple versions of #{keg.basename} installed please specify one" if kids.length > 1
- keg=keg.children.first
- raise "#{keg} is not a directory" unless keg.directory?
- elsif keg.parent.parent.parent != $root
- raise '#{keg} is not a keg'
+def install formula
+ require 'keg'
+
+ raise "#{formula.name} already installed!\n\t#{formula.prefix}" if formula.installed?
+
+ beginning = Time.now
+
+ formula.brew do
+ if ARGV.include? '--interactive'
+ ohai "Entering interactive mode"
+ puts "Type `exit' to return and finalize the installation"
+ puts "Install to this prefix: #{formula.prefix}"
+ pid=fork
+ if pid.nil?
+ exec 'bash'
+ else
+ Process.wait pid
+ end
+ elsif ARGV.include? '--help'
+ ohai './configure --help'
+ puts `./configure --help`
+ exit
+ else
+ formula.prefix.mkpath
+ formula.install
+ %w[README ChangeLog COPYING COPYRIGHT AUTHORS].each do |file|
+ formula.prefix.install file if File.file? file
+ end
+ end
end
-
- # yeah indeed, you have to force anything you need in the main tree into
- # these directories :P
- # NOTE that not everything needs to be in the main tree
- # TODO consider using hardlinks
- $n=0
- lnd(keg, 'etc') {:mkdir}
- lnd(keg, 'include') {:link}
- lnd(keg, 'bin') {:link}
- lnd(keg, 'lib') {|path| :mkpath if ['pkgconfig','php'].include? path.to_s}
- lnd(keg, 'share') do |path|
- mkpaths=(1..9).collect {|x| "man/man#{x}"} <<'man'<<'doc'<<'locale'<<'info'<<'aclocal'
- :mkpath if mkpaths.include? path.to_s
+ ohai 'Finishing up'
+ keg=Keg.new formula
+ keg.clean
+ keg.ln
+ if formula.caveats
+ ohai "Caveats"
+ puts formula.caveats
+ ohai "Summary"
end
-
- return $n
+ puts "#{keg.path}: "+abv(keg)+", built in #{pretty_duration Time.now-beginning}"
+rescue Exception
+ formula.prefix.rmtree
+ raise
end
-def symlink_relative_to from, to
- tod=to.dirname
- tod.mkpath
- Dir.chdir(tod) do
- #TODO use ruby function so we get exceptions
- `ln -sf "#{from.relative_path_from tod}"`
- $n+=1
- end
+def mk url
+ require 'formula'
+ path=Pathname.new(url)
+
+ /(.*?)[-_.]?#{path.version}/.match path.basename
+ raise "Couldn't parse name from #{url}" if $1.nil? or $1.empty?
+
+ path=Formula.path $1
+ raise "#{path} already exists!" if File.exist? path
+
+ f=File.new path, 'w'
+ f.puts "require 'brewkit'"
+ f.puts
+ f.puts "class #{Formula.class $1} <Formula"
+ f.puts " @url='#{url}'"
+ f.puts " @homepage=''" # second because you fill in these two first
+ f.puts " @md5=''"
+ f.puts
+ f.puts " def install"
+ f.puts " system \"./configure --disable-debug --prefix='\#{prefix}'\""
+ f.puts " system \"make install\""
+ f.puts " end"
+ f.print "end"
+ f.close
+
+ return path
end
-# symlinks a directory recursively into our FHS tree
-def lnd keg, start
- start=keg+start
- return unless start.directory?
-
- start.find do |from|
- next if from == start
-
- prune=false
- relative_path=from.relative_path_from keg
- to=$root+relative_path
-
- if from.directory?
- # no need to put .app bundles in the path, the user can just use
- # spotlight, or the open command and actual mac apps use an equivalent
- Find.prune if from.extname.to_s == '.app'
-
- cmd=yield from.relative_path_from(start)
-
- if :skip == cmd
- Find.prune
- elsif :mkpath == cmd
- to.mkpath
- $n+=1
- else
- symlink_relative_to from, to
- Find.prune
- end
- elsif from.file?
- symlink_relative_to from, to
- end
- end
+def prefix
+ Pathname.new(__FILE__).dirname.parent.expand_path
end
def usage
@@ -181,11 +165,6 @@ Commands:
EOS
end
-def abv keg=''
- keg=$cellar+keg
- return nil if not File.directory? keg
- `find #{keg} -type f | wc -l`.strip+' files, '+`du -hd0 #{keg} | cut -d"\t" -f1`.strip
-end
######################################################################## utils
def pretty_duration s
@@ -197,147 +176,80 @@ end
######################################################################### impl
begin
case ARGV.shift
- when 'prune'
- puts "Pruned #{prune} files"
- when '--prefix'
- # we use the cwd because __FILE__ can be relative and expand_path
- # resolves the symlink for the working directory if fed a relative path
- # NOTE we don't use Dir.pwd because it resolves the symlink :(
- cwd=Pathname.new `pwd`.strip
- puts File.expand_path(cwd+__FILE__+'../../')
- when '--cache'
- puts File.expand_path('~/Library/Application Support/Homebrew')
- when '-h', '--help', '--usage', '-?'
- puts usage
- when '-v', '--version'
- puts HOMEBREW_VERSION
- when 'list'
- fae=shift_formulae_from_ARGV.collect do |name|
- keg=$cellar+name
- keg.directory? ? keg : nil
- end
- raise 'No such keg' if fae.first.nil? and fae.length == 1
- puts `find #{fae.join' '} -type f -print`
- when 'macports'
- exec "open 'http://www.macports.org/ports.php?by=name&substr=#{ARGV.shift}'"
+ when '--prefix' then puts prefix
+ when '--cache' then puts Homebrew::cache
+ when '-h', '--help', '--usage', '-?' then puts usage
+ when '-v', '--version' then puts HOMEBREW_VERSION
+ when 'macports' then exec "open 'http://www.macports.org/ports.php?by=name&substr=#{ARGV.shift}'"
+
+ when 'ls', 'list'
+ dirs=extract_kegs.collect {|keg| keg.path}
+ exec "find #{dirs.join' '} -not -type d -print"
+
when 'edit'
if ARGV.empty?
exec "mate #{$formula} #{$root}/Library/Homebrew #{$root}/bin/brew #{$root}/README"
else
- exec "mate #{$formula}/#{ARGV.shift}.rb"
+ paths=extract_kegs.collect {|keg| keg.formula_path.to_s.gsub ' ', '\\ '}
+ exec "mate #{paths.join ' '}"
end
when 'install'
- shift_formulae_from_ARGV.each do |name|
- beginning = Time.now
- o=__obj(name)
- begin
- raise "#{o.prefix} already exists!" if o.prefix.exist?
- o.brew do
- if ARGV.include? '--interactive'
- ohai "Entering interactive mode, type `exit' to return to finalize installation"
- puts "Install to this prefix: #{o.prefix}"
- pid=fork
- if pid.nil?
- exec 'bash'
- else
- Process.wait pid
- end
- elsif ARGV.include? '--help'
- ohai './configure --help'
- puts `./configure --help`
- exit
- else
- o.prefix.mkpath
- o.install
- %w[README ChangeLog COPYING COPYRIGHT AUTHORS].each do |file|
- FileUtils.cp file, o.prefix if File.file? file
- end
- #this is common, and we don't want it
- versioned_docs=o.doc.parent+"#{o.name}-#{o.version}"
- versioned_docs.rename o.doc if versioned_docs.exist?
- end
- end
- ohai 'Finishing up'
- o.clean
- ln name
- if o.caveats
- ohai "Caveats"
- puts o.caveats
- ohai "Summary"
- end
- puts "#{o.prefix}: "+abv(name)+", built in #{pretty_duration Time.now-beginning}"
- rescue Exception
- FileUtils.rm_rf o.prefix
- raise
- end
+ require 'formula'
+ extract_named_args.each do |name|
+ install Formula.create(name)
end
- when 'ln'
+ when 'ln', 'link'
n=0
- shift_formulae_from_ARGV.each {|name| n+=ln name}
+ (kegs=extract_kegs).each do |keg|
+ n+=nn=keg.ln
+ puts "Created #{nn} links for #{keg.name}" if kegs.length > 1
+ end
puts "Created #{n} links"
when 'rm', 'uninstall'
- shift_formulae_from_ARGV.each {|name| rm name}
-
- when 'mk'
- require 'brewkit'
- url=ARGV.shift
- version=extract_version File.basename(url, Pathname.new(url).extname)
-
- /(.*?)[-_.]?#{version}/.match File.basename(url)
- raise "Couldn't parse name from #{url}" if $1.nil? or $1.empty?
-
- path=$formula+($1.downcase+'.rb')
- raise "#{path} already exists!" if File.exist? path
-
- f=File.new path, 'w'
- f.puts "require 'brewkit'"
- f.puts
- f.puts "class #{__class $1} <Formula"
- f.puts " @url='#{url}'"
- f.puts " @homepage=''" # second because you fill in these two first
- f.puts " @md5=''"
- f.puts
- f.puts " def install"
- f.puts " system \"./configure --disable-debug --prefix='\#{prefix}'\""
- f.puts " system \"make install\""
- f.puts " end"
- f.print "end"
- f.close
+ extract_kegs.each do |keg|
+ puts "Removing #{keg.name}..."
+ keg.rm
+ end
+ print "Pruning #{prefix}/..."
+ puts " #{prune} symbolic links pruned"
- if Kernel.system "which mate > /dev/null" and $? == 0
- exec "mate #{path}"
+ when 'prune'
+ puts "Pruned #{prune} symbolic links"
+
+ when 'mk', 'make'
+ paths=ARGV.collect {|arg| mk arg}
+ if paths.empty?
+ raise "Invalid URL"
+ elsif Kernel.system "which mate > /dev/null" and $? == 0
+ paths=paths.collect {|path| path.to_s.gsub " ", "\\ "}
+ exec "mate #{paths.join ' '}"
else
- puts path
+ puts paths.join("\n")
end
- when 'info','abv'
+ when 'info', 'abv'
if ARGV.empty?
puts abv
+ elsif ARGV[0][0..6] == 'http://'
+ puts Pathname.new(ARGV.shift).version
else
- if ARGV[0][0..6] == 'http://'
- require 'brewkit'
- path=Pathname.new ARGV[0]
- basename=File.basename path, path.extname
- v=extract_version basename
- puts v
- else
- o=__obj shift_formulae_from_ARGV[0]
- puts "#{o.name} #{o.version}"
- puts o.homepage
- if abv=abv(o.name)
- ohai "Installation"
- puts abv
- end
- if o.caveats
- ohai 'Caveats'
- puts o.caveats
- end
+ #TODO show outdated status and that
+ keg=extract_kegs[0]
+ frm=Formula.create keg.name
+ puts "#{keg.name} #{keg.version}"
+ puts frm.homepage
+ if keg.installed?
+ puts "#{abv keg} (installed to #{keg.path})"
+ end
+ if frm.caveats
+ ohai 'Caveats'
+ puts frm.caveats
end
end
-
+
else
puts usage
end