2009-08-31 16:01:36 +01:00
|
|
|
# Copyright 2009 Max Howell and other contributors.
|
2009-07-24 15:10:01 +01:00
|
|
|
#
|
2009-08-31 16:01:36 +01:00
|
|
|
# Redistribution and use in source and binary forms, with or without
|
|
|
|
# modification, are permitted provided that the following conditions
|
|
|
|
# are met:
|
2009-07-24 15:10:01 +01:00
|
|
|
#
|
2009-08-31 16:01:36 +01:00
|
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
|
|
# notice, this list of conditions and the following disclaimer.
|
|
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
|
|
# documentation and/or other materials provided with the distribution.
|
2009-07-24 15:10:01 +01:00
|
|
|
#
|
2009-08-31 16:01:36 +01:00
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2009-08-21 20:30:13 +01:00
|
|
|
#
|
2009-08-10 16:48:30 +01:00
|
|
|
class ExecutionError <RuntimeError
|
|
|
|
def initialize cmd, args=[]
|
2009-09-05 20:46:07 +01:00
|
|
|
super "Failure while executing: #{cmd} #{args*' '}"
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
end
|
2009-08-21 20:30:13 +01:00
|
|
|
class BuildError <ExecutionError
|
|
|
|
end
|
2009-08-10 16:48:30 +01:00
|
|
|
class FormulaUnavailableError <RuntimeError
|
|
|
|
def initialize name
|
|
|
|
super "No available formula for #{name}"
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
2009-08-10 16:48:30 +01:00
|
|
|
end
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2009-08-11 18:16:12 +01:00
|
|
|
|
2009-08-21 20:20:44 +01:00
|
|
|
# Derive and define at least @url, see Library/Formula for examples
|
|
|
|
class Formula
|
|
|
|
# Homebrew determines the name
|
2009-08-22 17:26:15 +01:00
|
|
|
def initialize name='__UNKNOWN__'
|
2009-08-10 16:48:30 +01:00
|
|
|
@url=self.class.url unless @url
|
2009-08-23 17:57:45 +01:00
|
|
|
|
|
|
|
@head=self.class.head unless @head
|
|
|
|
if @head and (not @url or ARGV.flag? '--HEAD')
|
|
|
|
@url=@head
|
|
|
|
@version='HEAD'
|
|
|
|
end
|
|
|
|
|
2009-08-11 12:20:55 -07:00
|
|
|
raise if @url.nil?
|
2009-08-21 20:20:44 +01:00
|
|
|
@name=name
|
2009-08-22 17:26:15 +01:00
|
|
|
validate_variable :name
|
2009-08-21 20:20:44 +01:00
|
|
|
@version=self.class.version unless @version
|
|
|
|
@version=Pathname.new(@url).version unless @version
|
2009-08-22 17:26:15 +01:00
|
|
|
validate_variable :version if @version
|
2009-08-21 20:20:44 +01:00
|
|
|
@homepage=self.class.homepage unless @homepage
|
|
|
|
@md5=self.class.md5 unless @md5
|
|
|
|
@sha1=self.class.sha1 unless @sha1
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
# if the dir is there, but it's empty we consider it not installed
|
|
|
|
def installed?
|
2009-07-29 00:57:55 +01:00
|
|
|
return prefix.children.length > 0
|
2009-07-24 15:10:01 +01:00
|
|
|
rescue
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
def prefix
|
2009-08-22 17:26:15 +01:00
|
|
|
validate_variable :name
|
|
|
|
validate_variable :version
|
2009-07-31 02:51:17 +01:00
|
|
|
HOMEBREW_CELLAR+@name+@version
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
2009-08-01 17:54:18 +01:00
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
def path
|
2009-08-21 20:20:44 +01:00
|
|
|
self.class.path name
|
2009-08-10 16:48:30 +01:00
|
|
|
end
|
|
|
|
|
2009-08-11 12:20:55 -07:00
|
|
|
attr_reader :url, :version, :homepage, :name
|
2009-08-10 16:48:30 +01:00
|
|
|
|
|
|
|
def bin; prefix+'bin' end
|
2009-08-12 09:43:16 +08:00
|
|
|
def sbin; prefix+'sbin' end
|
2009-08-10 16:48:30 +01:00
|
|
|
def doc; prefix+'share'+'doc'+name end
|
2009-08-31 22:34:42 -06:00
|
|
|
def etc; prefix+'etc' end
|
2009-08-10 16:48:30 +01:00
|
|
|
def lib; prefix+'lib' end
|
2009-08-21 16:24:14 -07:00
|
|
|
def libexec; prefix+'libexec' end
|
2009-08-10 16:48:30 +01:00
|
|
|
def man; prefix+'share'+'man' end
|
2009-08-01 17:54:18 +01:00
|
|
|
def man1; man+'man1' end
|
2009-08-10 16:48:30 +01:00
|
|
|
def info; prefix+'share'+'info' end
|
2009-08-01 17:54:18 +01:00
|
|
|
def include; prefix+'include' end
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2009-08-11 12:20:55 -07:00
|
|
|
# reimplement if we don't autodetect the download strategy you require
|
|
|
|
def download_strategy
|
|
|
|
case url
|
|
|
|
when %r[^svn://] then SubversionDownloadStrategy
|
|
|
|
when %r[^git://] then GitDownloadStrategy
|
|
|
|
when %r[^http://(.+?\.)?googlecode\.com/svn] then SubversionDownloadStrategy
|
2009-09-01 15:05:18 +02:00
|
|
|
when %r[^http://svn.apache.org/repos/] then SubversionDownloadStrategy
|
2009-08-11 12:20:55 -07:00
|
|
|
else HttpDownloadStrategy
|
|
|
|
end
|
|
|
|
end
|
2009-08-10 16:48:30 +01:00
|
|
|
# tell the user about any caveats regarding this package
|
|
|
|
def caveats; nil end
|
|
|
|
# patches are automatically applied after extracting the tarball
|
2009-08-11 18:16:12 +01:00
|
|
|
# return an array of strings, or if you need a patch level other than -p0
|
|
|
|
# return a Hash eg.
|
|
|
|
# {
|
|
|
|
# :p0 => ['http://foo.com/patch1', 'http://foo.com/patch2'],
|
|
|
|
# :p1 => 'http://bar.com/patch2',
|
|
|
|
# :p2 => ['http://moo.com/patch5', 'http://moo.com/patch6']
|
|
|
|
# }
|
2009-08-10 16:48:30 +01:00
|
|
|
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
|
2009-08-22 17:26:15 +01:00
|
|
|
validate_variable :name
|
|
|
|
validate_variable :version
|
|
|
|
|
2009-08-11 12:20:55 -07:00
|
|
|
stage do
|
2009-08-10 16:48:30 +01:00
|
|
|
begin
|
|
|
|
patch
|
|
|
|
yield self
|
|
|
|
rescue Interrupt, RuntimeError, SystemCallError => e
|
|
|
|
raise unless ARGV.debug?
|
|
|
|
onoe e.inspect
|
|
|
|
puts e.backtrace
|
|
|
|
ohai "Rescuing build..."
|
2009-08-27 13:46:19 -07:00
|
|
|
puts "When you exit this shell Homebrew will attempt to finalise the installation."
|
|
|
|
puts "If nothing is installed or the shell exits with a non-zero error code,"
|
|
|
|
puts "Homebrew will abort. The installation prefix is:"
|
|
|
|
puts prefix
|
2009-08-10 16:48:30 +01:00
|
|
|
interactive_shell
|
|
|
|
end
|
|
|
|
end
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
2009-08-10 16:48:30 +01:00
|
|
|
|
2009-08-21 20:20:44 +01:00
|
|
|
# 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
|
|
|
|
|
|
|
|
def self.class name
|
|
|
|
#remove invalid characters and camelcase
|
|
|
|
name.capitalize.gsub(/[-_\s]([a-zA-Z0-9])/) { $1.upcase }
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.factory name
|
|
|
|
require self.path(name)
|
|
|
|
return eval(self.class(name)).new(name)
|
|
|
|
rescue LoadError
|
|
|
|
raise FormulaUnavailableError.new(name)
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.path name
|
|
|
|
HOMEBREW_PREFIX+'Library'+'Formula'+"#{name.downcase}.rb"
|
|
|
|
end
|
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
protected
|
2009-07-24 15:10:01 +01:00
|
|
|
# Pretty titles the command and buffers stdout/stderr
|
|
|
|
# Throws if there's an error
|
2009-08-10 16:48:30 +01:00
|
|
|
def system cmd, *args
|
|
|
|
full="#{cmd} #{args*' '}".strip
|
|
|
|
ohai full
|
|
|
|
if ARGV.verbose?
|
|
|
|
safe_system cmd, *args
|
2009-07-24 15:10:01 +01:00
|
|
|
else
|
|
|
|
out=''
|
2009-08-10 16:48:30 +01:00
|
|
|
# TODO write a ruby extension that does a good popen :P
|
|
|
|
IO.popen "#{full} 2>&1" do |f|
|
2009-07-24 15:10:01 +01:00
|
|
|
until f.eof?
|
|
|
|
out+=f.gets
|
|
|
|
end
|
|
|
|
end
|
2009-08-10 16:48:30 +01:00
|
|
|
unless $? == 0
|
|
|
|
puts out
|
|
|
|
raise
|
|
|
|
end
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
2009-08-10 16:48:30 +01:00
|
|
|
rescue
|
|
|
|
raise BuildError.new(cmd, args)
|
|
|
|
end
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
private
|
2009-08-11 12:20:55 -07:00
|
|
|
# creates a temporary directory then yields, when the block returns it
|
|
|
|
# recursively deletes the temporary directory
|
2009-08-10 16:48:30 +01:00
|
|
|
def mktemp
|
2009-08-23 17:57:45 +01:00
|
|
|
# I used /tmp rather than mktemp -td because that generates a directory
|
|
|
|
# name with exotic characters like + in it, and these break badly written
|
|
|
|
# scripts that don't escape strings before trying to regexp them :(
|
|
|
|
tmp=Pathname.new `mktemp -d /tmp/homebrew-#{name}-#{version}-XXXX`.strip
|
|
|
|
raise "Couldn't create build sandbox" if not tmp.directory? or $? != 0
|
2009-08-10 16:48:30 +01:00
|
|
|
begin
|
|
|
|
wd=Dir.pwd
|
|
|
|
Dir.chdir tmp
|
|
|
|
yield
|
|
|
|
ensure
|
|
|
|
Dir.chdir wd
|
|
|
|
tmp.rmtree
|
|
|
|
end
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
|
2009-07-31 11:05:23 -07:00
|
|
|
def verify_download_integrity fn
|
|
|
|
require 'digest'
|
|
|
|
type='MD5'
|
|
|
|
type='SHA1' if @sha1
|
|
|
|
supplied=eval "@#{type.downcase}"
|
|
|
|
hash=eval("Digest::#{type}").hexdigest(fn.read)
|
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
if supplied and not supplied.empty?
|
2009-07-31 11:05:23 -07:00
|
|
|
raise "#{type} mismatch: #{hash}" unless supplied.upcase == hash.upcase
|
|
|
|
else
|
|
|
|
opoo "Cannot verify package integrity"
|
|
|
|
puts "The formula did not provide a download checksum"
|
2009-08-11 12:20:55 -07:00
|
|
|
puts "For your reference the #{type} is: #{hash}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def stage
|
|
|
|
ds=download_strategy.new url, name, version
|
|
|
|
HOMEBREW_CACHE.mkpath
|
|
|
|
dl=ds.fetch
|
|
|
|
verify_download_integrity dl if dl.kind_of? Pathname
|
|
|
|
mktemp do
|
|
|
|
ds.stage
|
|
|
|
yield
|
2009-07-31 11:05:23 -07:00
|
|
|
end
|
|
|
|
end
|
2009-08-03 15:15:58 +01:00
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
def patch
|
2009-08-11 18:16:12 +01:00
|
|
|
return if patches.empty?
|
|
|
|
ohai "Patching"
|
|
|
|
if patches.kind_of? Hash
|
|
|
|
patch_args=[]
|
|
|
|
curl_args=[]
|
|
|
|
n=0
|
|
|
|
patches.each do |arg, urls|
|
|
|
|
urls.each do |url|
|
|
|
|
dst='%03d-homebrew.patch' % n+=1
|
|
|
|
curl_args<<url<<'-o'<<dst
|
|
|
|
patch_args<<["-#{arg}",'-i',dst]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
# downloading all at once is much more efficient, espeically for FTP
|
|
|
|
curl *curl_args
|
|
|
|
patch_args.each do |args|
|
|
|
|
# -f means it doesn't prompt the user if there are errors, if just
|
|
|
|
# exits with non-zero status
|
|
|
|
safe_system 'patch', '-f', *args
|
|
|
|
end
|
|
|
|
else
|
2009-08-10 16:48:30 +01:00
|
|
|
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}
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
end
|
2009-08-11 18:16:12 +01:00
|
|
|
|
2009-08-22 17:26:15 +01:00
|
|
|
def validate_variable name
|
2009-08-30 16:11:44 +01:00
|
|
|
v=eval "@#{name}"
|
|
|
|
raise "Invalid @#{name}" if v.to_s.empty? or v =~ /\s/
|
2009-08-22 17:26:15 +01:00
|
|
|
end
|
|
|
|
|
2009-07-31 14:17:56 +01:00
|
|
|
def method_added method
|
|
|
|
raise 'You cannot override Formula.brew' if method == 'brew'
|
|
|
|
end
|
2009-08-21 20:20:44 +01:00
|
|
|
|
|
|
|
class <<self
|
2009-08-23 17:57:45 +01:00
|
|
|
attr_reader :url, :version, :homepage, :md5, :sha1, :head
|
2009-08-21 20:20:44 +01:00
|
|
|
end
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
# see ack.rb for an example usage
|
2009-08-21 20:20:44 +01:00
|
|
|
class ScriptFileFormula <Formula
|
2009-07-24 15:10:01 +01:00
|
|
|
def install
|
2009-08-11 12:20:55 -07:00
|
|
|
bin.install Dir['*']
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
# see flac.rb for example usage
|
2009-07-24 15:10:01 +01:00
|
|
|
class GithubGistFormula <ScriptFileFormula
|
2009-08-30 15:32:15 +01:00
|
|
|
def initialize name='__UNKNOWN__'
|
2009-08-21 20:20:44 +01:00
|
|
|
super name
|
2009-08-30 15:32:15 +01:00
|
|
|
@version=File.basename(File.dirname(url))[0,6]
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
end
|