brew/Library/Homebrew/dependency.rb
Jack Nagel a2c949bfb7 Allow Dependency objects to be marshaled
When an exception is raised in the build process, it is marshaled and
sent to the main process over a pipe. The marshaled exception has
a reference to the formula, so the formula and all objects it references
are marshaled as well.

Previously this did not include dependencies, as they were stored only
on the class and not referenced by an instance variable. However, now
they are stored on SoftwareSpec instances, to which the formula *does*
have a direct reference, so they must be marshalable.
2013-09-27 23:36:57 -05:00

128 lines
2.7 KiB
Ruby

require 'dependable'
# A dependency on another Homebrew formula.
class Dependency
include Dependable
attr_reader :name, :tags
attr_accessor :env_proc
def initialize(name, tags=[])
@name = name
@tags = tags
end
def to_s
name
end
def ==(other)
name == other.name
end
def eql?(other)
instance_of?(other.class) && name == other.name
end
def hash
name.hash
end
def to_formula
f = Formula.factory(name)
# Add this dependency's options to the formula's build args
f.build.args = f.build.args.concat(options)
f
end
def installed?
to_formula.installed?
end
def requested?
ARGV.formulae.include?(to_formula) rescue false
end
def satisfied?
installed? && missing_options.empty?
end
def missing_options
options - Tab.for_formula(to_formula).used_options - to_formula.build.implicit_options
end
def universal!
tags << 'universal' if to_formula.build.has_option? 'universal'
end
def modify_build_environment
env_proc.call unless env_proc.nil?
end
def inspect
"#<#{self.class}: #{name.inspect} #{tags.inspect}>"
end
# Define marshaling semantics because we cannot serialize @env_proc
def _dump(*)
Marshal.dump([name, tags])
end
def self._load(marshaled)
new(*Marshal.load(marshaled))
end
class << self
# Expand the dependencies of dependent recursively, optionally yielding
# [dependent, dep] 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 expand(dependent, &block)
deps = dependent.deps.map do |dep|
case action(dependent, dep, &block)
when :prune
next []
when :skip
expand(dep.to_formula, &block)
else
expand(dep.to_formula, &block) << dep
end
end.flatten
merge_repeats(deps)
end
def action(dependent, dep, &block)
catch(:action) do
if block_given?
yield dependent, dep
elsif dep.optional? || dep.recommended?
prune unless dependent.build.with?(dep.name)
end
end
end
# Prune a dependency and its dependencies recursively
def prune
throw(:action, :prune)
end
# Prune a single dependency but do not prune its dependencies
def skip
throw(:action, :skip)
end
def merge_repeats(deps)
grouped = deps.group_by(&:name)
deps.uniq.map do |dep|
tags = grouped.fetch(dep.name).map(&:tags).flatten.uniq
merged_dep = dep.class.new(dep.name, tags)
merged_dep.env_proc = dep.env_proc
merged_dep
end
end
end
end