提交 fb5aefd4 编写于 作者: M Max Howell

Refactor--object orientate where sensible

上级 5eb9d651
......@@ -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
......@@ -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
# 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
# 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
......@@ -2,7 +2,7 @@
$:.unshift File.dirname(__FILE__)
require 'test/unit'
require 'brewkit'
require 'formula'
require 'stringio'
class TestFormula <Formula
......
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
......
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册