mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
418 lines
10 KiB
Ruby
418 lines
10 KiB
Ruby
# 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'
|
|
require 'osx/cocoa' # to get number of cores
|
|
require 'env'
|
|
|
|
# optimise all the way to eleven, references:
|
|
# http://en.gentoo-wiki.com/wiki/Safe_Cflags/Intel
|
|
# http://forums.mozillazine.org/viewtopic.php?f=12&t=577299
|
|
# http://gcc.gnu.org/onlinedocs/gcc-4.0.1/gcc/i386-and-x86_002d64-Options.html
|
|
ENV['MACOSX_DEPLOYMENT_TARGET']='10.5'
|
|
ENV['CFLAGS']=ENV['CXXFLAGS']='-O3 -w -pipe -fomit-frame-pointer -march=prescott'
|
|
|
|
# lets use gcc 4.2, it is newer and "better", at least I believe so, mail me
|
|
# if I'm wrong
|
|
ENV['CC']='gcc-4.2'
|
|
ENV['CXX']='g++-4.2'
|
|
ENV['MAKEFLAGS']="-j#{OSX::NSProcessInfo.processInfo.processorCount}"
|
|
|
|
unless $root.to_s == '/usr/local'
|
|
ENV['CPPFLAGS']='-I'+$root+'include'
|
|
ENV['LDFLAGS']='-L'+$root+'lib'
|
|
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
|
|
|
|
def inreplace(path, before, after)
|
|
before=before.to_s.gsub('"', '\"').gsub('/', '\/')
|
|
after=after.to_s.gsub('"', '\"').gsub('/', '\/')
|
|
|
|
# we're not using Ruby because the perl script is more concise
|
|
#TODO the above escapes are worse, use a proper ruby script :P
|
|
#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
|
|
`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 |