1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
require 'dependable'
# A dependency on another Homebrew formula.
class Dependency
include Dependable
attr_reader :name, :tags
attr_accessor :env_proc, :option_name
def initialize(name, tags=[])
@name = @option_name = name
@tags = tags
end
def to_s
name
end
def ==(other)
instance_of?(other.class) && name == other.name
end
alias_method :eql?, :==
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 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, deps=dependent.deps, &block)
expanded_deps = []
deps.each do |dep|
# FIXME don't hide cyclic dependencies
next if dependent.name == dep.name
case action(dependent, dep, &block)
when :prune
next
when :skip
expanded_deps.concat(expand(dep.to_formula, &block))
when :keep_but_prune_recursive_deps
expanded_deps << dep
else
expanded_deps.concat(expand(dep.to_formula, &block))
expanded_deps << dep
end
end
merge_repeats(expanded_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)
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
# Keep a dependency, but prune its dependencies
def keep_but_prune_recursive_deps
throw(:action, :keep_but_prune_recursive_deps)
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
|