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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
require 'build_environment'
## This file defines dependencies and requirements.
##
## A dependency is a formula that another formula needs to install.
## A requirement is something other than a formula that another formula
## needs to be present. This includes external language modules,
## command-line tools in the path, or any arbitrary predicate.
##
## The `depends_on` method in the formula DSL is used to declare
## dependencies and requirements.
# This class is used by `depends_on` in the formula DSL to turn dependency
# specifications into the proper kinds of dependencies and requirements.
class DependencyCollector
# Define the languages that we can handle as external dependencies.
LANGUAGE_MODULES = [
:chicken, :jruby, :lua, :node, :ocaml, :perl, :python, :rbx, :ruby
].freeze
attr_reader :deps, :requirements
def initialize
@deps = Dependencies.new
@requirements = ComparableSet.new
end
def add spec
tag = nil
spec, tag = spec.shift if spec.is_a? Hash
dep = parse_spec(spec, tag)
# Some symbol specs are conditional, and resolve to nil if there is no
# dependency needed for the current platform.
return if dep.nil?
# Add dep to the correct bucket
(dep.is_a?(Requirement) ? @requirements : @deps) << dep
end
private
def parse_spec spec, tag
case spec
when Symbol
parse_symbol_spec(spec, tag)
when String
if LANGUAGE_MODULES.include? tag
LanguageModuleDependency.new(tag, spec)
else
Dependency.new(spec, tag)
end
when Formula
Dependency.new(spec.name, tag)
when Dependency, Requirement
spec
when Class
if spec < Requirement
spec.new
else
raise "#{spec} is not a Requirement subclass"
end
else
raise "Unsupported type #{spec.class} for #{spec}"
end
end
def parse_symbol_spec spec, tag
case spec
when :autoconf, :automake, :bsdmake, :libtool
# Xcode no longer provides autotools or some other build tools
Dependency.new(spec.to_s, tag) unless MacOS::Xcode.provides_autotools?
when :libpng, :freetype, :pixman, :fontconfig, :cairo
if MacOS.version >= :mountain_lion
Dependency.new(spec.to_s, tag)
else
X11Dependency.new(tag)
end
when :x11
X11Dependency.new(tag)
when :xcode
XcodeDependency.new(tag)
when :mysql
MysqlInstalled.new(tag)
when :postgresql
PostgresqlInstalled.new(tag)
when :tex
TeXInstalled.new(tag)
when :clt
CLTDependency.new(tag)
else
raise "Unsupported special dependency #{spec}"
end
end
end
class Dependencies
include Enumerable
def initialize(*args)
@deps = Array.new(*args)
end
def each(*args, &block)
@deps.each(*args, &block)
end
def <<(o)
@deps << o unless @deps.include? o
self
end
def empty?
@deps.empty?
end
def *(arg)
@deps * arg
end
def to_a
@deps
end
alias_method :to_ary, :to_a
end
module Dependable
RESERVED_TAGS = [:build, :optional, :recommended]
def build?
tags.include? :build
end
def optional?
tags.include? :optional
end
def recommended?
tags.include? :recommended
end
def options
Options.coerce(tags - RESERVED_TAGS)
end
end
# A dependency on another Homebrew formula.
class Dependency
include Dependable
attr_reader :name, :tags
def initialize(name, *tags)
@name = name
@tags = tags.flatten.compact
end
def to_s
name
end
def ==(other)
name == other.name
end
def eql?(other)
other.is_a?(self.class) && hash == other.hash
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 universal!
tags << 'universal' if to_formula.build.has_option? 'universal'
end
# Expand the dependencies of f recursively, optionally yielding
# [f, dep] to allow callers to apply arbitrary filters to the list.
# The default filter, which is used when a block is not supplied,
# omits optionals and recommendeds based on what the dependent has
# asked for.
def self.expand(dependent, &block)
dependent.deps.map do |dep|
prune = catch(:prune) do
if block_given?
yield dependent, dep
elsif dep.optional? || dep.recommended?
Dependency.prune unless dependent.build.with?(dep.name)
end
end
next if prune
expand(dep.to_formula, &block) << dep
end.flatten.compact.uniq
end
# Used to prune dependencies when calling expand_dependencies with a block.
def self.prune
throw(:prune, true)
end
end
# 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
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)
other.is_a?(self.class) && hash == other.hash
end
def hash
message.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
end
require 'requirements'
|