aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew
diff options
context:
space:
mode:
Diffstat (limited to 'Library/Homebrew')
-rw-r--r--Library/Homebrew/brew.h.rb238
-rw-r--r--Library/Homebrew/env.rb25
-rw-r--r--Library/Homebrew/formula.rb315
-rw-r--r--Library/Homebrew/keg.rb163
-rw-r--r--Library/Homebrew/pathname+yeast.rb54
-rwxr-xr-xLibrary/Homebrew/unittest.rb60
-rw-r--r--Library/Homebrew/utils.rb21
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