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
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
|
require 'pathname'
require 'mach'
# we enhance pathname to make our code more readable
class Pathname
include MachO
BOTTLE_EXTNAME_RX = /(\.[a-z]+\.bottle\.(\d+\.)?tar\.gz)$/
OLD_BOTTLE_EXTNAME_RX = /((\.[a-z]+)?[\.-]bottle\.tar\.gz)$/
def install *sources
results = []
sources.each do |src|
case src
when Array
if src.empty?
opoo "tried to install empty array to #{self}"
return []
end
src.each {|s| results << install_p(s) }
when Hash
if src.empty?
opoo "tried to install empty hash to #{self}"
return []
end
src.each {|s, new_basename| results << install_p(s, new_basename) }
else
results << install_p(src)
end
end
return results
end
def install_p src, new_basename = nil
if new_basename
new_basename = File.basename(new_basename) # rationale: see Pathname.+
dst = self+new_basename
return_value = Pathname.new(dst)
else
dst = self
return_value = self+File.basename(src)
end
src = src.to_s
dst = dst.to_s
# if it's a symlink, don't resolve it to a file because if we are moving
# files one by one, it's likely we will break the symlink by moving what
# it points to before we move it
# and also broken symlinks are not the end of the world
raise "#{src} does not exist" unless File.symlink? src or File.exist? src
mkpath
if File.symlink? src
# we use the BSD mv command because FileUtils copies the target and
# not the link! I'm beginning to wish I'd used Python quite honestly!
raise unless Kernel.system 'mv', src, dst
else
# we mv when possible as it is faster and you should only be using
# this function when installing from the temporary build directory
FileUtils.mv src, dst
end
return return_value
end
# Creates symlinks to sources in this folder.
def install_symlink *sources
results = []
sources.each do |src|
case src
when Array
src.each {|s| results << install_symlink_p(s) }
when Hash
src.each {|s, new_basename| results << install_symlink_p(s, new_basename) }
else
results << install_symlink_p(src)
end
end
return results
end
def install_symlink_p src, new_basename = nil
if new_basename.nil?
dst = self+File.basename(src)
else
dst = self+File.basename(new_basename)
end
src = src.to_s
dst = dst.to_s
mkpath
FileUtils.ln_s src, dst
return dst
end
# we assume this pathname object is a file obviously
def write content
#raise "Will not overwrite #{to_s}" if exist? and not ARGV.force?
dirname.mkpath
File.open(self, 'w') {|f| f.write content }
end
# NOTE always overwrites
def atomic_write content
require 'tempfile'
tf = Tempfile.new(self.basename.to_s)
tf.write(content)
tf.close
FileUtils.mv tf.path, self.to_s
end
def cp dst
if file?
FileUtils.cp to_s, dst
else
FileUtils.cp_r to_s, dst
end
return dst
end
# extended to support common double extensions
alias extname_old extname
def extname
BOTTLE_EXTNAME_RX.match to_s
return $1 if $1
OLD_BOTTLE_EXTNAME_RX.match to_s
return $1 if $1
/(\.(tar|cpio)\.(gz|bz2|xz|Z))$/.match to_s
return $1 if $1
return File.extname(to_s)
end
# for filetypes we support, basename without extension
def stem
return File.basename(to_s, extname)
end
# I don't trust the children.length == 0 check particularly, not to mention
# it is slow to enumerate the whole directory just to see if it is empty,
# instead rely on good ol' libc and the filesystem
def rmdir_if_possible
rmdir
true
rescue SystemCallError => e
# OK, maybe there was only a single `.DS_Store` file in that folder
if (self/'.DS_Store').exist? && self.children.length == 1
(self/'.DS_Store').unlink
retry
end
raise unless e.errno == Errno::ENOTEMPTY::Errno or e.errno == Errno::EACCES::Errno or e.errno == Errno::ENOENT::Errno
false
end
def chmod_R perms
require 'fileutils'
FileUtils.chmod_R perms, to_s
end
def abv
out=''
n=`find #{to_s} -type f ! -name .DS_Store | wc -l`.to_i
out<<"#{n} files, " if n > 1
out<<`/usr/bin/du -hd0 #{to_s} | cut -d"\t" -f1`.strip
end
def version
require 'version'
Version.parse(self)
end
def compression_type
# Don't treat jars or wars as compressed
return nil if self.extname == '.jar'
return nil if self.extname == '.war'
# OS X installer package
return :pkg if self.extname == '.pkg'
# Get enough of the file to detect common file types
# POSIX tar magic has a 257 byte offset
magic_bytes = nil
File.open(self) { |f| magic_bytes = f.read(262) }
# magic numbers stolen from /usr/share/file/magic/
case magic_bytes
when /^PK\003\004/ then :zip
when /^\037\213/ then :gzip
when /^BZh/ then :bzip2
when /^\037\235/ then :compress
when /^.{257}ustar/ then :tar
when /^\xFD7zXZ\x00/ then :xz
when /^Rar!/ then :rar
else
# This code so that bad-tarballs and zips produce good error messages
# when they don't unarchive properly.
case extname
when ".tar.gz", ".tgz", ".tar.bz2", ".tbz" then :tar
when ".zip" then :zip
when ".7z" then :p7zip
end
end
end
def text_executable?
%r[^#!\s*\S+] === open('r') { |f| f.read(1024) }
end
def incremental_hash(hasher)
incr_hash = hasher.new
self.open('r') do |f|
while(buf = f.read(1024))
incr_hash << buf
end
end
incr_hash.hexdigest
end
def md5
require 'digest/md5'
incremental_hash(Digest::MD5)
end
def sha1
require 'digest/sha1'
incremental_hash(Digest::SHA1)
end
def sha2
require 'digest/sha2'
incremental_hash(Digest::SHA2)
end
alias_method :sha256, :sha2
def verify_checksum expected
raise ChecksumMissingError if expected.nil? or expected.empty?
actual = Checksum.new(expected.hash_type, send(expected.hash_type).downcase)
raise ChecksumMismatchError.new(expected, actual) unless expected == actual
end
if '1.9' <= RUBY_VERSION
alias_method :to_str, :to_s
end
def cd
Dir.chdir(self){ yield }
end
def subdirs
children.select{ |child| child.directory? }
end
def resolved_path
self.symlink? ? dirname+readlink : self
end
def resolved_path_exists?
(dirname+readlink).exist?
end
# perhaps confusingly, this Pathname object becomes the symlink pointing to
# the src paramter.
def make_relative_symlink src
src = Pathname.new(src) unless src.kind_of? Pathname
self.dirname.mkpath
Dir.chdir self.dirname do
# NOTE only system ln -s will create RELATIVE symlinks
quiet_system 'ln', '-s', src.relative_path_from(self.dirname), self.basename
if not $?.success?
if self.exist?
raise <<-EOS.undent
Could not symlink file: #{src.expand_path}
Target #{self} already exists. You may need to delete it.
To force the link and delete this file, do:
brew link --overwrite formula_name
To list all files that would be deleted:
brew link --overwrite --dry-run formula_name
EOS
elsif !dirname.writable_real?
raise <<-EOS.undent
Could not symlink file: #{src.expand_path}
#{dirname} is not writable. You should change its permissions.
EOS
else
raise <<-EOS.undent
Could not symlink file: #{src.expand_path}
#{self} may already exist.
#{dirname} may not be writable.
EOS
end
end
end
end
def / that
join that.to_s
end
def ensure_writable
saved_perms = nil
unless writable_real?
saved_perms = stat.mode
chmod 0644
end
yield
ensure
chmod saved_perms if saved_perms
end
def install_info
unless self.symlink?
raise "Cannot install info entry for unbrewed info file '#{self}'"
end
system '/usr/bin/install-info', '--quiet', self.to_s, (self.dirname+'dir').to_s
end
def uninstall_info
unless self.symlink?
raise "Cannot uninstall info entry for unbrewed info file '#{self}'"
end
system '/usr/bin/install-info', '--delete', '--quiet', self.to_s, (self.dirname+'dir').to_s
end
def all_formula pwd = self
children.map{ |child| child.relative_path_from(pwd) }.each do |pn|
yield pn if pn.to_s =~ /.rb$/
end
children.each do |child|
child.all_formula(pwd) do |pn|
yield pn
end if child.directory?
end
end
def find_formula
# remove special casing once tap is established and alt removed
if self == HOMEBREW_LIBRARY/"Taps/adamv-alt"
all_formula do |file|
yield file
end
return
end
[self/:Formula, self/:HomebrewFormula, self].each do |d|
if d.exist?
d.children.map{ |child| child.relative_path_from(self) }.each do |pn|
yield pn if pn.to_s =~ /.rb$/
end
break
end
end
end
# Writes an exec script in this folder for each target pathname
def write_exec_script *targets
targets = [targets].flatten
if targets.empty?
opoo "tried to write exec sripts to #{self} for an empty list of targets"
end
targets.each do |target|
target = Pathname.new(target) # allow pathnames or strings
(self+target.basename()).write <<-EOS.undent
#!/bin/bash
exec "#{target}" "$@"
EOS
end
end
# Writes an exec script that invokes a java jar
def write_jar_script target_jar, script_name, java_opts=""
(self+script_name).write <<-EOS.undent
#!/bin/bash
exec java #{java_opts} -jar #{target_jar} "$@"
EOS
end
def install_metafiles from=nil
# Default to current path, and make sure we have a pathname, not a string
from = "." if from.nil?
from = Pathname.new(from.to_s)
from.children.each do |p|
next if p.directory?
next unless FORMULA_META_FILES.should_copy? p
# Some software symlinks these files (see help2man.rb)
filename = p.resolved_path
# Some software links metafiles together, so by the time we iterate to one of them
# we may have already moved it. libxml2's COPYING and Copyright are affected by this.
next unless filename.exist?
filename.chmod 0644
self.install filename
end
end
end
# sets $n and $d so you can observe creation of stuff
module ObserverPathnameExtension
def unlink
super
puts "rm #{to_s}" if ARGV.verbose?
$n+=1
end
def rmdir
super
puts "rmdir #{to_s}" if ARGV.verbose?
$d+=1
end
def make_relative_symlink src
super
$n+=1
end
def install_info
super
puts "info #{to_s}" if ARGV.verbose?
end
def uninstall_info
super
puts "uninfo #{to_s}" if ARGV.verbose?
end
end
$n=0
$d=0
|