diff options
Diffstat (limited to 'Library/Homebrew')
| -rw-r--r-- | Library/Homebrew/brew.h.rb | 238 | ||||
| -rw-r--r-- | Library/Homebrew/env.rb | 25 | ||||
| -rw-r--r-- | Library/Homebrew/formula.rb | 315 | ||||
| -rw-r--r-- | Library/Homebrew/keg.rb | 163 | ||||
| -rw-r--r-- | Library/Homebrew/pathname+yeast.rb | 54 | ||||
| -rwxr-xr-x | Library/Homebrew/unittest.rb | 60 | ||||
| -rw-r--r-- | Library/Homebrew/utils.rb | 21 |
7 files changed, 565 insertions, 311 deletions
diff --git a/Library/Homebrew/brew.h.rb b/Library/Homebrew/brew.h.rb new file mode 100644 index 000000000..3dee3ec2d --- /dev/null +++ b/Library/Homebrew/brew.h.rb @@ -0,0 +1,238 @@ +# 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/>. + +def make 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 path.exist? + + template=<<-EOS + require 'brewkit' + + class #{Formula.class $1} <Formula + @url='#{url}' + @homepage='' + @md5='' + + cmake def deps + cmake BinaryDep.new 'cmake' + cmake end + cmake + def install + autotools system "./configure --prefix='\#{prefix}' --disable-debug --disable-dependency-tracking" + cmake system "cmake . \#{cmake_std_parameters}" + system "make install" + end + end + EOS + + mode=nil + if ARGV.include? '--cmake' + mode= :cmake + elsif ARGV.include? '--autotools' + mode= :autotools + end + + f=File.new path, 'w' + template.each_line do |s| + if s.strip.empty? + f.puts + next + end + cmd=s[0..11].strip + if cmd.empty? + cmd=nil + else + cmd=cmd.to_sym + end + out=s[12..-1] || '' + + if mode.nil? + # we show both but comment out cmake as it is less common + # the implication being the pacakger should remove whichever is not needed + if cmd == :cmake and not out.empty? + f.print '#' + out = out[1..-1] + end + elsif cmd != mode and not cmd.nil? + next + end + f.puts out + end + f.close + + return path +end + + +def info name + require 'formula' + + history="http://github.com/mxcl/homebrew/commits/masterbrew/Library/Formula/#{Formula.path(name).basename}" + exec 'open', history if ARGV.flag? '--github' + + f=Formula.factory name + puts "#{f.name} #{f.version}" + puts f.homepage + + if f.prefix.parent.directory? + kids=f.prefix.parent.children + kids.each do |keg| + print "#{keg} (#{keg.abv})" + print " *" if f.prefix == keg and kids.length > 1 + puts + end + else + puts "Not installed" + end + + if f.caveats + puts + puts f.caveats + puts + end + + puts 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 clean f + Cleaner.new f + # remove empty directories TODO Rubyize! + `perl -MFile::Find -e"finddepth(sub{rmdir},'#{f.prefix}')"` +end + + +def install f + f.brew do + if ARGV.flag? '--interactive' + ohai "Entering interactive mode" + puts "Type `exit' to return and finalize the installation" + puts "Install to this prefix: #{f.prefix}" + interactive_shell + elsif ARGV.include? '--help' + system './configure --help' + exit $? + else + f.prefix.mkpath + f.install + %w[README ChangeLog COPYING LICENSE COPYRIGHT AUTHORS].each do |file| + f.prefix.install file if File.file? file + end + end + end +end + + +def prune + $n=0 + $d=0 + + dirs=Array.new + paths=%w[bin 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 #{$n} directories " if $d > 0 + puts "from #{HOMEBREW_PREFIX}" + end +end + + +################################################################ class Cleaner +class Cleaner + def initialize f + @f=f + [f.bin, f.lib].each {|d| clean_dir d} + end + +private + def strip path, args='' + return if @f.skip_clean? path + puts "strip #{path}" if ARGV.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 #{args} -o #{tmp} #{path}` + `cat #{tmp} > #{path}` + File.unlink tmp + end + end + + def clean_file path + perms=0444 + case `file -h #{path}` + when /Mach-O dynamically linked shared library/ + strip path, '-SxX' + when /Mach-O [^ ]* ?executable/ + strip path + perms=0544 + when /script text executable/ + perms=0544 + end + path.chmod perms + end + + def clean_dir d + d.find do |path| + if not path.file? + next + elsif path.extname == '.la' and not @f.skip_clean? path + # *.la files are stupid + path.unlink + else + clean_file path + end + end + end +end diff --git a/Library/Homebrew/env.rb b/Library/Homebrew/env.rb deleted file mode 100644 index 3710aa793..000000000 --- a/Library/Homebrew/env.rb +++ /dev/null @@ -1,25 +0,0 @@ -# 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 'pathname+yeast' -require 'utils' - -# TODO if whoami == root then use /Library/Caches/Homebrew instead -HOMEBREW_VERSION='0.3' -HOMEBREW_CACHE=Pathname.new("~/Library/Caches/Homebrew").expand_path -HOMEBREW_PREFIX=Pathname.new(__FILE__).dirname.parent.parent.cleanpath -HOMEBREW_CELLAR=HOMEBREW_PREFIX+'Cellar' diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 4f02e2077..f01798eba 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -15,30 +15,31 @@ # You should have received a copy of the GNU General Public License # along with Homebrew. If not, see <http://www.gnu.org/licenses/>. -require 'utils' -class BuildError <RuntimeError - def initialize cmd - super "Build failed during: #{cmd}" +class ExecutionError <RuntimeError + def initialize cmd, args=[] + super "#{cmd} #{args*' '}" 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' +class BuildError <ExecutionError; end -private - class <<self - attr_reader :url, :version, :md5, :url, :homepage, :sha1 +class FormulaUnavailableError <RuntimeError + def initialize name + super "No available formula for #{name}" end +end -public - attr_reader :url, :version, :url, :homepage, :name - - # reimplement if your package has dependencies - def deps +# the base class variety of formula, you don't get a prefix, so it's not +# useful. See the derived classes for fun and games. +class AbstractFormula + def initialize noop=nil + @version=self.class.version unless @version + @url=self.class.url unless @url + @homepage=self.class.homepage unless @homepage + @md5=self.class.md5 unless @md5 + @sha1=self.class.sha1 unless @sha1 + raise "@url is nil" if @url.nil? end # if the dir is there, but it's empty we consider it not installed @@ -48,61 +49,115 @@ public return false end - def initialize name=nil - @name=name - @version=self.class.version unless @version - @url=self.class.url unless @url - @homepage=self.class.homepage unless @homepage - @md5=self.class.md5 unless @md5 - @sha1=self.class.sha1 unless @sha1 - raise "@url.nil?" if @url.nil? - end - def prefix - raise "@name.nil!" if @name.nil? - raise "@version.nil?" if @version.nil? + raise "Invalid @name" if @name.nil? or @name.empty? + raise "Invalid @version" if @version.nil? or @version.empty? HOMEBREW_CELLAR+@name+@version end - def bin; prefix+'bin' end - def doc; prefix+'share'+'doc'+name end - def lib; prefix+'lib' end - def man; prefix+'share'+'man' end + def path + Formula.path name + end + + attr_reader :url, :version, :url, :homepage, :name + + def bin; prefix+'bin' end + def doc; prefix+'share'+'doc'+name end + def lib; prefix+'lib' end + def man; prefix+'share'+'man' end def man1; man+'man1' end + def info; prefix+'share'+'info' end def include; prefix+'include' end - def caveats - nil + # tell the user about any caveats regarding this package + def caveats; nil end + # patches are automatically applied after extracting the tarball + def patches; [] end + # reimplement and specify dependencies + def deps; end + # sometimes the clean process breaks things, return true to skip anything + def skip_clean? path; false end + + # yields self with current working directory set to the uncompressed tarball + def brew + ohai "Downloading #{@url}" + tgz=HOMEBREW_CACHE+File.basename(@url) + unless tgz.exist? + HOMEBREW_CACHE.mkpath + curl @url, '-o', tgz + else + puts "File already downloaded and cached" + end + + verify_download_integrity tgz + + mktemp do + Dir.chdir uncompress(tgz) + begin + patch + yield self + rescue Interrupt, RuntimeError, SystemCallError => e + raise unless ARGV.debug? + onoe e.inspect + puts e.backtrace + ohai "Rescuing build..." + puts "Type `exit' and Homebrew will attempt to finalize the installation" + puts "If nothing is installed to #{prefix}, then Homebrew will abort" + interactive_shell + end + end end - + +protected # 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 + def system cmd, *args + full="#{cmd} #{args*' '}".strip + ohai full + if ARGV.verbose? + safe_system cmd, *args else out='' - IO.popen "#{cmd} 2>&1" do |f| + # TODO write a ruby extension that does a good popen :P + IO.popen "#{full} 2>&1" do |f| until f.eof? out+=f.gets end end - puts out unless $? == 0 + unless $? == 0 + puts out + raise + end end + rescue + raise BuildError.new(cmd, args) + end - raise BuildError.new(cmd) unless $? == 0 +private + def mktemp + tmp=Pathname.new `mktemp -dt #{File.basename @url}`.strip + raise if not tmp.directory? or $? != 0 + begin + wd=Dir.pwd + Dir.chdir tmp + yield + ensure + Dir.chdir wd + tmp.rmtree + end end - # we don't have a std_autotools variant because autotools is a lot less - # consistent and the standard parameters are more memorable - # really Homebrew should determine what works inside brew() then - # we could add --disable-dependency-tracking when it will work - def std_cmake_parameters - # The None part makes cmake use the environment's CFLAGS etc. settings - "-DCMAKE_INSTALL_PREFIX='#{prefix}' -DCMAKE_BUILD_TYPE=None" + # Kernel.system but with exceptions + def safe_system cmd, *args + puts "#{cmd} #{args*' '}" if ARGV.verbose? + # stderr is shown, so hopefully that will explain the problem + raise ExecutionError.new(cmd, args) unless Kernel.system cmd, *args and $? == 0 end - + + def curl url, *args + safe_system 'curl', '-f#LA', HOMEBREW_USER_AGENT, url, *args + end + def verify_download_integrity fn require 'digest' type='MD5' @@ -110,7 +165,7 @@ public supplied=eval "@#{type.downcase}" hash=eval("Digest::#{type}").hexdigest(fn.read) - if supplied + if supplied and not supplied.empty? raise "#{type} mismatch: #{hash}" unless supplied.upcase == hash.upcase else opoo "Cannot verify package integrity" @@ -119,91 +174,69 @@ public end end - # yields self with current working directory set to the uncompressed tarball - def brew - ohai "Downloading #{@url}" - HOMEBREW_CACHE.mkpath - Dir.chdir HOMEBREW_CACHE do - tmp=nil - tgz=Pathname.new(fetch()).realpath - begin - verify_download_integrity tgz - - # 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 + def patch + unless patches.empty? + ohai "Patching" + ff=(1..patches.length).collect {|n| '%03d-homebrew.patch'%n} + curl *patches+ff.collect {|f|"-o#{f}"} + ff.each {|f| safe_system 'patch', '-p0', '-i', f} end end + + class <<self + attr_reader :url, :version, :md5, :url, :homepage, :sha1 + 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 +# This is the meat. See the examples. +class Formula <AbstractFormula + def initialize name=nil + super + @name=name + @version=Pathname.new(@url).version unless @version 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 + def self.class name + #remove invalid characters and camelcase + name.capitalize.gsub(/[-_\s]([a-zA-Z0-9])/) { $1.upcase } + end - unless File.exists? tgz - `curl -#LA "#{HOMEBREW_USER_AGENT}" #{oarg} "#{@url}"` - raise "Download failed" unless $? == 0 - else - puts "File already downloaded and cached" - end - return tgz + def self.factory name + require self.path(name) + return eval(self.class(name)).new(name) + rescue LoadError + raise FormulaUnavailableError.new(name) 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 + def self.path name + HOMEBREW_PREFIX+'Library'+'Formula'+"#{name.downcase}.rb" + end + + # we don't have a std_autotools variant because autotools is a lot less + # consistent and the standard parameters are more memorable + # really Homebrew should determine what works inside brew() then + # we could add --disable-dependency-tracking when it will work + def std_cmake_parameters + # The None part makes cmake use the environment's CFLAGS etc. settings + "-DCMAKE_INSTALL_PREFIX='#{prefix}' -DCMAKE_BUILD_TYPE=None" end private - def uncompress(path) - if path.extname == '.zip' - `unzip -qq "#{path}"` + def uncompress_args + rx=%r[http://(www.)?github.com/.*/(zip|tar)ball/] + if rx.match @url and $2 == '.zip' or Pathname.new(@url).extname == '.zip' + %w[unzip -qq] else - `tar xf "#{path}"` + %w[tar xf] end + end - raise "Compression tool failed" if $? != 0 + def uncompress path + safe_system *uncompress_args<<path entries=Dir['*'] - if entries.nil? or entries.length == 0 - raise "Empty tarball!" + if entries.length == 0 + raise "Empty archive" elsif entries.length == 1 # if one dir enter it as that will be where the build is entries.first @@ -211,30 +244,6 @@ private # 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 - Pathname.new(HOMEBREW_PREFIX)+'Library'+'Formula'+(name.downcase+'.rb') - end - - def self.create name - require Formula.path(name) - return eval(Formula.class(name)).new(name) - rescue LoadError - raise "No formula for #{name}" end def method_added method @@ -243,16 +252,24 @@ class Formula <UnidentifiedFormula end # see ack.rb for an example usage -# you need to set @version and @name class ScriptFileFormula <AbstractFormula + def initialize name=nil + super + @name=name + end + def uncompress path + path.dirname + end def install - bin.install name + bin.install File.basename(@url) end end +# see flac.rb for example usage class GithubGistFormula <ScriptFileFormula - def initialize - super File.basename(self.class.url) + def initialize name=nil + super + @name=name @version=File.basename(File.dirname(url))[0,6] end end diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index d2db862af..69e11983d 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -14,146 +14,61 @@ # # You should have received a copy of the GNU General Public License # along with Homebrew. If not, see <http://www.gnu.org/licenses/>. +# +class Keg <Pathname + def initialize path + super path + raise "#{to_s} is not a valid keg" unless parent.parent == HOMEBREW_CELLAR + raise "#{to_s} is not a directory" unless directory? + end -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 - path=HOMEBREW_CELLAR+formula - kids=path.children - raise "Empty installation: #{path}" if kids.length < 1 - raise "Multiple versions installed" if kids.length > 1 - @path=kids[0] - @name=formula - @version=@path.basename - end + def uninstall + chmod_R 0777 # ensure we have permission to delete + rmtree + parent.rmdir_if_possible 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/ - args='' # use strip defaults - 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} + def link + $n=0 + $d=0 - # remove empty directories TODO Rubyize! - `perl -MFile::Find -e"finddepth(sub{rmdir},'#{path}')"` - end + mkpaths=(1..9).collect {|x| "man/man#{x}"} <<'man'<<'doc'<<'locale'<<'info'<<'aclocal' - def rm - # don't rmtree shit if we aren't positive about our location! - raise "Bad stuff!" unless path.parent.parent == HOMEBREW_CELLAR + # 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 + link_dir('etc') {:mkpath} + link_dir('bin') {:link} + link_dir('lib') {|path| :mkpath if %w[pkgconfig php].include? path.to_s} + link_dir('include') {:link} + link_dir('share') {|path| :mkpath if mkpaths.include? path.to_s} - if path.directory? - FileUtils.chmod_R 0777, path # ensure we have permission to delete - path.rmtree # HOMEBREW_CELLAR/foo/1.2.0 - path.parent.rmdir if path.parent.children.length == 0 # HOMEBREW_CELLAR/foo - end + return $n+$d 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? - - root=Pathname.new HOMEBREW_PREFIX - start.find do |from| - next if from == start + # symlinks the contents of self+foo recursively into /usr/local/foo + def link_dir foo + root=self+foo - prune=false + root.find do |src| + next if src == root - relative_path=from.relative_path_from path - to=root+relative_path + dst=HOMEBREW_PREFIX+src.relative_path_from(self) + dst.extend ObserverPathnameExtension - if from.file? - __symlink_relative_to from, to - elsif from.directory? + if src.file? + dst.make_relative_symlink src + elsif src.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' + Find.prune if src.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 + case yield src.relative_path_from(root) + when :skip then Find.prune + when :mkpath then dst.mkpath + else dst.make_relative_symlink src; 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 +end diff --git a/Library/Homebrew/pathname+yeast.rb b/Library/Homebrew/pathname+yeast.rb index 67a21cfc2..43c07c5ab 100644 --- a/Library/Homebrew/pathname+yeast.rb +++ b/Library/Homebrew/pathname+yeast.rb @@ -68,6 +68,24 @@ class Pathname return File.basename(to_s, extname) end + # I don't trust the children.length == 0 check particularly, not to mention + # it is slow to enumerate the whole directory just to see if it is empty, + # instead rely on good ol' libc and the filesystem + def rmdir_if_possible + rmdir + rescue SystemCallError => e + raise unless e.errno == Errno::ENOTEMPTY::Errno + end + + def chmod_R perms + require 'fileutils' + FileUtils.chmod_R perms, to_s + end + + def abv + `find #{to_s} -type f | wc -l`.strip+' files, '+`du -hd0 #{to_s} | cut -d"\t" -f1`.strip + end + def version # eg. boost_1_39_0 /((\d+_)+\d+)$/.match stem @@ -100,3 +118,39 @@ class Pathname end end end + +# sets $n and $d so you can observe creation of stuff +module ObserverPathnameExtension + def unlink + super + puts "rm #{to_s}" if ARGV.verbose? + $n+=1 + end + def rmdir + super + puts "rmdir #{to_s}" if ARGV.verbose? + $d+=1 + end + def resolved_path_exists? + (dirname+readlink).exist? + end + def mkpath + super + puts "mkpath #{to_s}" if ARGV.verbose? + $d+=1 + end + def make_relative_symlink src + dirname.mkpath + Dir.chdir dirname do + # TODO use Ruby function so we get exceptions + # NOTE Ruby functions may work, but I had a lot of problems + rv=system 'ln', '-sf', src.relative_path_from(dirname) + raise "Could not create symlink #{to_s}" unless rv and $? == 0 + puts "ln #{to_s}" if ARGV.verbose? + $n+=1 + end + end +end + +$n=0 +$d=0 diff --git a/Library/Homebrew/unittest.rb b/Library/Homebrew/unittest.rb index 878b05ab9..1bee7b3a6 100755 --- a/Library/Homebrew/unittest.rb +++ b/Library/Homebrew/unittest.rb @@ -1,9 +1,8 @@ #!/usr/bin/ruby $:.unshift File.dirname(__FILE__) +require 'pathname+yeast' require 'formula' require 'keg' -require 'pathname+yeast' -require 'stringio' require 'utils' # these are defined in env.rb usually, but we don't want to break our actual @@ -17,6 +16,7 @@ HOMEBREW_CELLAR.mkpath raise "HOMEBREW_CELLAR couldn't be created!" unless HOMEBREW_CELLAR.directory? at_exit { HOMEBREW_PREFIX.parent.rmtree } require 'test/unit' # must be after at_exit +require 'ARGV+yeast' # needs to be after test/unit to avoid conflict with OptionsParser class MockFormula <Formula @@ -26,12 +26,16 @@ class MockFormula <Formula end end +class MostlyAbstractFormula <AbstractFormula + @url='' +end + class TestBall <Formula def initialize @url="file:///#{Pathname.new(__FILE__).parent.realpath}/testball-0.1.tbz" super "testball" end - + def install prefix.install "bin" prefix.install "libexec" @@ -51,16 +55,28 @@ class TestBallOverrideBrew <Formula super "foo" end def brew - puts "We can't override brew" + # We can't override brew end end +class TestScriptFileFormula <ScriptFileFormula + @url="file:///#{Pathname.new(__FILE__).realpath}" + @version="1" + + def initialize + super + @name='test-script-formula' + end +end def nostdout - tmp=$stdout + require 'stringio' + tmpo=$stdout + tmpe=$stderr $stdout=StringIO.new yield - $stdout=tmp +ensure + $stdout=tmpo end @@ -162,9 +178,10 @@ class BeerTasting <Test::Unit::TestCase def test_install f=TestBall.new + assert_equal Formula.path(f.name), f.path assert !f.installed? - nostdout do + nostdout do f.brew do f.install end @@ -180,17 +197,29 @@ class BeerTasting <Test::Unit::TestCase assert !(f.prefix+'main.c').exist? assert f.installed? - keg=Keg.new f - keg.ln + keg=Keg.new f.prefix + keg.link assert_equal 2, HOMEBREW_PREFIX.children.length assert (HOMEBREW_PREFIX+'bin').directory? assert_equal 3, (HOMEBREW_PREFIX+'bin').children.length - keg.rm - assert !keg.path.exist? + keg.uninstall + assert !keg.exist? assert !f.installed? end + def test_script_install + f=TestScriptFileFormula.new + + nostdout do + f.brew do + f.install + end + end + + assert_equal 1, f.bin.children.length + end + def test_md5 assert_nothing_raised { nostdout { TestBallValidMd5.new.brew {} } } end @@ -213,10 +242,17 @@ class BeerTasting <Test::Unit::TestCase path.dirname.mkpath `echo "require 'brewkit'; class #{classname} <Formula; @url=''; end" > #{path}` - assert_not_nil Formula.create(FOOBAR) + assert_not_nil Formula.factory(FOOBAR) end def test_cant_override_brew assert_raises(RuntimeError) { TestBallOverrideBrew.new } end + + def test_abstract_formula + f=MostlyAbstractFormula.new + assert_nil f.name + assert_raises(RuntimeError) { f.prefix } + nostdout { assert_raises(ExecutionError) { f.brew } } + end end diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index 112c6b6b6..bb64c3851 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -22,5 +22,24 @@ end # shows a warning in delicious pink def opoo warning - puts "WARNING \033[1;35m#{warning}\033[0;0m" + puts "\033[1;35m==>\033[0;0;1m Warning\033[0;0m: #{warning}" +end + +def onoe error + puts "\033[1;31m==>\033[0;0;1m Error\033[0;0m: #{error}" +end + +def pretty_duration s + return "#{(s*1000).to_i} milliseconds" if s < 3 + return "#{s.to_i} seconds" if s < 10*60 + return "#{(s/60).to_i} minutes" +end + +def interactive_shell + pid=fork + if pid.nil? + exec ENV['SHELL'] + else + Process.wait pid + end end |
