brew/Library/Homebrew/requirement.rb
Jack Nagel 1408610b81 Proper recursive expansion of requirements with filtering
Expand requirements recursively while applying the same optional? and
recommended? filters that dependencies are run through. Options
generated by requirements are now checked against the correct list of
requirements, eliminating the temporary "best guess" logic in the
installer.
2013-01-31 15:40:06 -06:00

160 lines
3.8 KiB
Ruby

require 'dependable'
require 'build_environment'
# A base class for non-formula requirements needed by formulae.
# A "fatal" requirement is one that will fail the build if it is not present.
# By default, Requirements are non-fatal.
class Requirement
include Dependable
extend BuildEnvironmentDSL
attr_reader :tags, :name
def initialize(*tags)
@tags = tags.flatten.compact
@tags << :build if self.class.build
end
# The message to show when the requirement is not met.
def message; "" end
# Overriding #satisfied? is deprepcated.
# Pass a block or boolean to the satisfied DSL method instead.
def satisfied?
result = self.class.satisfy.yielder do |proc|
instance_eval(&proc)
end
infer_env_modification(result)
!!result
end
# Overriding #fatal? is deprecated.
# Pass a boolean to the fatal DSL method instead.
def fatal?
self.class.fatal || false
end
# Overriding #modify_build_environment is deprecated.
# Pass a block to the the env DSL method instead.
def modify_build_environment
satisfied? and env.modify_build_environment(self)
end
def env
@env ||= self.class.env
end
def eql?(other)
instance_of?(other.class) && hash == other.hash
end
def hash
[name, *tags].hash
end
private
def infer_env_modification(o)
case o
when Pathname
self.class.env do
unless ENV["PATH"].split(":").include?(o.parent.to_s)
append("PATH", o.parent, ":")
end
end
end
end
class << self
def fatal(val=nil)
val.nil? ? @fatal : @fatal = val
end
def build(val=nil)
val.nil? ? @build : @build = val
end
def satisfy(options={}, &block)
@satisfied ||= Requirement::Satisfier.new(options, &block)
end
end
class Satisfier
def initialize(options={}, &block)
case options
when Hash
@options = { :build_env => true }
@options.merge!(options)
else
@satisfied = options
end
@proc = block
end
def yielder
if instance_variable_defined?(:@satisfied)
@satisfied
elsif @options[:build_env]
require 'superenv'
ENV.with_build_environment do
ENV.userpaths!
yield @proc
end
else
yield @proc
end
end
end
# Expand the requirements of dependent recursively, optionally yielding
# [dependent, req] pairs to allow callers to apply arbitrary filters to
# the list.
# The default filter, which is applied when a block is not given, omits
# optionals and recommendeds based on what the dependent has asked for.
def self.expand(dependent, &block)
reqs = ComparableSet.new
formulae = dependent.recursive_dependencies.map(&:to_formula)
formulae.unshift(dependent)
formulae.map(&:requirements).each do |requirements|
requirements.each do |req|
prune = catch(:prune) do
if block_given?
yield dependent, req
elsif req.optional? || req.recommended?
Requirement.prune unless dependent.build.with?(req.name)
end
end
next if prune
reqs << req
end
end
# We special case handling of X11Dependency and its subclasses to
# ensure the correct dependencies are present in the final list.
# If an X11Dependency is present after filtering, we eliminate
# all X11Dependency::Proxy objects from the list. If there aren't
# any X11Dependency objects, then we eliminate all but one of the
# proxy objects.
proxy = unless reqs.any? { |r| r.instance_of?(X11Dependency) }
reqs.find { |r| r.kind_of?(X11Dependency::Proxy) }
end
reqs.reject! do |r|
r.kind_of?(X11Dependency::Proxy)
end
reqs << proxy unless proxy.nil?
reqs
end
# Used to prune requirements when calling expand with a block.
def self.prune
throw(:prune, true)
end
end