Dependency resolution with fancy syntax

Is it a DSL? No. But people call it that apparently.

To add a dependency:

class Doe <Formula
  depends_on 'ray'
  depends_on 'mee' => :optional
  depends_on 'far' => :recommended
  depends_on Sew.new
end

Sew would be a formula you have defined in this Formula file. This is useful,
eg. see Python's formula. Formula specified in this fashion cannot be linked
into the HOMEBREW_PREFIX, they are considered private libraries. This allows
you to create custom installations that are very specific to your formula.

More features to come, like specifying versions
This commit is contained in:
Max Howell 2009-09-18 19:16:39 +01:00
parent a793e30405
commit 77dd27e8e6
3 changed files with 47 additions and 36 deletions

View File

@ -144,31 +144,12 @@ def clean f
end end
# NOTE this is ugly code, and inefficient too, and can have infinite cycles def expand_deps ff
# I have no time currently to improve it, feel free to submit a more elegant
# solution. Thanks! --mxcl
def expand_deps fae
deps = [] deps = []
fae.each do |f| ff.deps.collect do |f|
case f.deps deps += expand_deps(Formula.factory(f))
when String, Array
f.deps.each do |name|
f = Formula.factory name
deps << expand_deps(f) if f.deps # hideous inefficient
deps << f unless f.installed?
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 end
deps << ff
# TODO much more efficient to use a set and not recurse stuff already done
return deps.flatten.uniq
end end

View File

@ -114,8 +114,6 @@ class Formula
# :p2 => ['http://moo.com/patch5', 'http://moo.com/patch6'] # :p2 => ['http://moo.com/patch5', 'http://moo.com/patch6']
# } # }
def patches; [] end def patches; [] end
# reimplement and specify dependencies
def deps; end
# sometimes the clean process breaks things, return true to skip anything # sometimes the clean process breaks things, return true to skip anything
def skip_clean? path; false end def skip_clean? path; false end
@ -157,6 +155,7 @@ class Formula
end end
def self.factory name def self.factory name
return name if name.kind_of? Formula
require self.path(name) require self.path(name)
return eval(self.class(name)).new(name) return eval(self.class(name)).new(name)
rescue LoadError rescue LoadError
@ -167,6 +166,10 @@ class Formula
HOMEBREW_PREFIX+'Library'+'Formula'+"#{name.downcase}.rb" HOMEBREW_PREFIX+'Library'+'Formula'+"#{name.downcase}.rb"
end end
def deps
self.class.deps or []
end
protected protected
# Pretty titles the command and buffers stdout/stderr # Pretty titles the command and buffers stdout/stderr
# Throws if there's an error # Throws if there's an error
@ -280,9 +283,31 @@ private
end end
class <<self class <<self
attr_reader :url, :version, :homepage, :head attr_reader :url, :version, :homepage, :head, :deps
attr_reader *CHECKSUM_TYPES attr_reader *CHECKSUM_TYPES
end
def depends_on name, *args
@deps ||= []
case name
when String
# noop
when Hash
name = name.keys.first # indeed, we only support one mapping
when Symbol
name = name.to_s
when Formula
@deps << name
return # we trust formula dev to not dupe their own instantiations
else
raise "Unsupported type #{name.class}"
end
# we get duplicates because every new fork of this process repeats this
# step for some reason I am not sure about
@deps << name unless @deps.include? name
end
end
end end
# see ack.rb for an example usage # see ack.rb for an example usage

View File

@ -38,6 +38,7 @@ unless system "which -s gcc-4.2" and $?.success?
abort "Sorry, Homebrew requires gcc 4.2, which is provided by Xcode 3.1" abort "Sorry, Homebrew requires gcc 4.2, which is provided by Xcode 3.1"
end end
begin begin
case ARGV.shift case ARGV.shift
when '--prefix' then puts HOMEBREW_PREFIX when '--prefix' then puts HOMEBREW_PREFIX
@ -104,7 +105,7 @@ begin
unless system "which #{ENV['CC'] or 'cc'} &> /dev/null" and $?.success? unless system "which #{ENV['CC'] or 'cc'} &> /dev/null" and $?.success?
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
fae = ARGV.formulae.reject do |f| formulae = ARGV.formulae.reject do |f|
if f.installed? if f.installed?
message = "Formula already installed: #{f.prefix}" message = "Formula already installed: #{f.prefix}"
if ARGV.formulae.count > 1 if ARGV.formulae.count > 1
@ -115,20 +116,24 @@ begin
true true
end end
end end
exit 0 if fae.empty? exit 0 if formulae.empty?
else else
fae=ARGV.formulae formulae = ARGV.formulae
end end
# the resulting order will be optimal for super-deps and deps deps = []
fae=expand_deps fae formulae.each { |f| deps += expand_deps f }
formulae = deps.reject { |f| f.installed? }
require 'set'
done = Set.new
require 'beer_events' require 'beer_events'
watch_out_for_spill do watch_out_for_spill do
fae.each do |f| formulae.each do |f|
# we need to ensure a pristine ENV for each process or the formula next if done.include? f.class
# will start with the ENV from the previous build done << f.class
pid=fork pid=fork
if pid.nil? if pid.nil?
exec __FILE__, "install-just-one", f.name, *ARGV.options exec __FILE__, "install-just-one", f.name, *ARGV.options