Dependency resolution

Specify dependencies in your formula's deps function. You can return an Array,
String or Hash, eg:

    def deps
      { :optional => 'libogg', :required => %w[flac sdl], :recommended => 'cmake' }
    end

Note currently the Hash is flattened and qualifications are ignored. If you
only return an Array or String, the qualification is assumed to be :required.

Other packaging systems have problems when it comes to packages requiring a
specific version of a package, or some patches that may not work well with
other software. With Homebrew we have some options:

1.  If the formula is vanilla but an older version we can cherry-pick the old
    version and install it in the Cellar in parallel, but just not symlink it
    into /usr/local while forcing the formula that depends on it to link to
    that one and not any other versions of it.
2.  If the dependency requires patches then we shouldn't install this for use
    by any other tools, (I guess this needs to be decided on a per-situation
    basis). It can be installed into the parent formula's prefix, and not
    symlinked into /usr/local. In this case the dependency's Formula
    derivation should be saved in the parent formula's file (check git or
    flac for an example of this).

Both the above can be done currently with hacks, so I'll flesh out a proper
way sometime this week.
This commit is contained in:
Max Howell 2009-09-07 01:06:08 +01:00
parent 111a75d262
commit 0a31190fdc
4 changed files with 59 additions and 24 deletions

View File

@ -144,6 +144,33 @@ def clean f
end end
# NOTE this is ugly code, and inefficient too, and can have infinite cycles
# I have no time currently to improve it, feel free to submit a more elegant
# solution. Thanks! --mxcl
def expand_deps fae
deps = []
fae.each do |f|
case f.deps
when String, Array
f.deps.each do |name|
f = Formula.factory name
deps << expand_deps(f) if f.deps # hideous inefficient
deps << f
end
when Hash
# TODO implement optional and recommended
names = []
f.deps.each_value {|v| names << v}
ff=names.flatten.collect {|name| Formula.factory name}
deps << expand_deps(ff)
end
deps << f
end
# TODO much more efficient to use a set and not recurse stuff already done
return deps.flatten.uniq
end
def install f def install f
f.brew do f.brew do
if ARGV.flag? '--interactive' if ARGV.flag? '--interactive'

View File

@ -123,7 +123,7 @@ class Formula
def brew def brew
validate_variable :name validate_variable :name
validate_variable :version validate_variable :version
stage do stage do
begin begin
patch patch

View File

@ -41,7 +41,8 @@ class MostlyAbstractFormula <Formula
end end
class TestBall <Formula class TestBall <Formula
def initialize # name parameter required for some Formula::factory
def initialize name=nil
@url="file:///#{Pathname.new(ABS__FILE__).parent.realpath}/testball-0.1.tbz" @url="file:///#{Pathname.new(ABS__FILE__).parent.realpath}/testball-0.1.tbz"
super "testball" super "testball"
end end
@ -98,9 +99,13 @@ def nostdout
end end
module ExtendArgvPlusYeast module ExtendArgvPlusYeast
def stick_an_arg_in_thar def reset
@named=nil @named=nil
unshift 'foo' @formulae=nil
@kegs=nil
while ARGV.count > 0
ARGV.shift
end
end end
end end
ARGV.extend ExtendArgvPlusYeast ARGV.extend ExtendArgvPlusYeast
@ -347,8 +352,9 @@ class BeerTasting <Test::Unit::TestCase
end end
def test_no_ARGV_dupes def test_no_ARGV_dupes
ARGV.unshift'foo' ARGV.reset
ARGV.unshift'foo' ARGV.unshift 'foo'
ARGV.unshift 'foo'
n=0 n=0
ARGV.named.each{|arg| n+=1 if arg == 'foo'} ARGV.named.each{|arg| n+=1 if arg == 'foo'}
assert_equal 1, n assert_equal 1, n
@ -360,9 +366,10 @@ class BeerTasting <Test::Unit::TestCase
assert_raises(UsageError) { ARGV.kegs } assert_raises(UsageError) { ARGV.kegs }
assert ARGV.named_empty? assert ARGV.named_empty?
(HOMEBREW_CELLAR+'foo'+'0.1').mkpath (HOMEBREW_CELLAR+'mxcl'+'10.0').mkpath
ARGV.stick_an_arg_in_thar ARGV.reset
ARGV.unshift 'mxcl'
assert_equal 1, ARGV.named.length assert_equal 1, ARGV.named.length
assert_equal 1, ARGV.kegs.length assert_equal 1, ARGV.kegs.length
assert_raises(FormulaUnavailableError) { ARGV.formulae } assert_raises(FormulaUnavailableError) { ARGV.formulae }
@ -395,7 +402,7 @@ class BeerTasting <Test::Unit::TestCase
nostdout do nostdout do
assert_nothing_raised do assert_nothing_raised do
f=TestBall.new f=TestBall.new
make 'http://example.com/testball-0.1.tbz' make f.url
info f.name info f.name
clean f clean f
prune prune

View File

@ -95,20 +95,24 @@ begin
raise "We cannot find a c compiler, have you installed the latest Xcode?" raise "We cannot find a c compiler, have you installed the latest Xcode?"
end end
require 'beer_events' fae = ARGV.formulae.reject do |f|
if f.installed? and not ARGV.force?
watch_out_for_spill do message = "Formula already installed: #{f.prefix}"
ARGV.formulae.each do |f| if ARGV.formulae.count > 1
if f.installed? and not ARGV.force? opoo message
message = "Formula already installed: #{f.prefix}" else
if ARGV.formulae.count > 1 puts message # if only one is being installed a warning looks severe
opoo message
else
puts message # if only one is being installed a warning looks severe
end
next
end end
true
end
end
fae=expand_deps ARGV.formulae
require 'beer_events'
watch_out_for_spill do
fae.each do |f|
# we need to ensure a pristine ENV for each process or the formula # we need to ensure a pristine ENV for each process or the formula
# will start with the ENV from the previous build # will start with the ENV from the previous build
pid=fork pid=fork
@ -162,9 +166,6 @@ begin
when 'unlink' when 'unlink'
ARGV.kegs.each {|keg| puts "#{keg.unlink} links removed for #{keg}"} ARGV.kegs.each {|keg| puts "#{keg.unlink} links removed for #{keg}"}
when 'unlink'
ARGV.kegs.each {|keg| puts "#{keg.unlink} links removed for #{keg}"}
when 'rm', 'uninstall', 'remove' when 'rm', 'uninstall', 'remove'
ARGV.kegs.each do |keg| ARGV.kegs.each do |keg|
puts "Uninstalling #{keg}..." puts "Uninstalling #{keg}..."