diff options
Diffstat (limited to 'Library/Homebrew')
| -rw-r--r-- | Library/Homebrew/brewkit.rb | 371 | ||||
| -rw-r--r-- | Library/Homebrew/env.rb | 95 | ||||
| -rw-r--r-- | Library/Homebrew/formula.rb | 263 | ||||
| -rw-r--r-- | Library/Homebrew/keg.rb | 153 | ||||
| -rwxr-xr-x | Library/Homebrew/unittest.rb | 2 |
5 files changed, 514 insertions, 370 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 |
