2010-09-11 20:22:54 +01:00
|
|
|
require 'extend/pathname'
|
2013-02-09 18:19:50 -06:00
|
|
|
require 'formula_lock'
|
2013-04-14 15:38:14 -05:00
|
|
|
require 'ostruct'
|
2010-09-11 20:22:54 +01:00
|
|
|
|
2011-03-15 21:48:24 -07:00
|
|
|
class Keg < Pathname
|
2009-08-10 16:48:30 +01:00
|
|
|
def initialize path
|
|
|
|
super path
|
2009-11-12 11:09:20 -08:00
|
|
|
raise "#{to_s} is not a valid keg" unless parent.parent.realpath == HOMEBREW_CELLAR.realpath
|
2009-08-10 16:48:30 +01:00
|
|
|
raise "#{to_s} is not a directory" unless directory?
|
|
|
|
end
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2012-02-01 22:20:34 -06:00
|
|
|
# locale-specific directories have the form language[_territory][.codeset][@modifier]
|
|
|
|
LOCALEDIR_RX = /(locale|man)\/([a-z]{2}|C|POSIX)(_[A-Z]{2})?(\.[a-zA-Z\-0-9]+(@.+)?)?/
|
2012-03-20 10:40:00 -05:00
|
|
|
INFOFILE_RX = %r[info/([^.].*?\.info|dir)$]
|
2012-11-05 18:13:14 -06:00
|
|
|
TOP_LEVEL_DIRECTORIES = %w[bin etc include lib sbin share var Frameworks]
|
2013-01-07 17:50:23 -06:00
|
|
|
PRUNEABLE_DIRECTORIES = %w[bin etc include lib sbin share Frameworks LinkedKegs].map do |d|
|
|
|
|
case d when 'LinkedKegs' then HOMEBREW_LIBRARY/d else HOMEBREW_PREFIX/d end
|
|
|
|
end
|
2012-01-21 00:51:20 +01:00
|
|
|
|
2009-10-23 16:03:38 +01:00
|
|
|
# if path is a file in a keg then this will return the containing Keg object
|
|
|
|
def self.for path
|
2009-11-12 11:09:20 -08:00
|
|
|
path = path.realpath
|
2009-10-23 16:03:38 +01:00
|
|
|
while not path.root?
|
2009-11-12 11:09:20 -08:00
|
|
|
return Keg.new(path) if path.parent.parent == HOMEBREW_CELLAR.realpath
|
2009-10-23 16:03:38 +01:00
|
|
|
path = path.parent.realpath # realpath() prevents root? failing
|
|
|
|
end
|
2009-12-17 18:45:07 +00:00
|
|
|
raise NotAKegError, "#{path} is not inside a keg"
|
2009-10-23 16:03:38 +01:00
|
|
|
end
|
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
def uninstall
|
|
|
|
rmtree
|
|
|
|
parent.rmdir_if_possible
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
|
2009-08-29 21:07:26 +01:00
|
|
|
def unlink
|
2012-10-04 14:15:16 -05:00
|
|
|
# these are used by the ObserverPathnameExtension to count the number
|
|
|
|
# of files and directories linked
|
|
|
|
$n=$d=0
|
2012-07-20 12:01:37 -05:00
|
|
|
|
2013-02-17 22:54:43 -06:00
|
|
|
TOP_LEVEL_DIRECTORIES.map{ |d| self/d }.each do |dir|
|
|
|
|
next unless dir.exist?
|
|
|
|
dir.find do |src|
|
2012-03-25 13:08:58 +01:00
|
|
|
next if src == self
|
2013-08-09 11:29:19 -05:00
|
|
|
dst = HOMEBREW_PREFIX + src.relative_path_from(self)
|
|
|
|
dst.extend(ObserverPathnameExtension)
|
2012-10-04 08:52:48 -05:00
|
|
|
|
|
|
|
# check whether the file to be unlinked is from the current keg first
|
|
|
|
if !dst.symlink? || !dst.exist? || src != dst.resolved_path
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
2012-03-25 13:08:58 +01:00
|
|
|
dst.uninstall_info if dst.to_s =~ INFOFILE_RX and ENV['HOMEBREW_KEEP_INFO']
|
|
|
|
dst.unlink
|
2013-08-09 11:29:19 -05:00
|
|
|
dst.parent.extend(ObserverPathnameExtension).rmdir_if_possible
|
2012-03-25 13:08:58 +01:00
|
|
|
Find.prune if src.directory?
|
|
|
|
end
|
2009-08-29 21:07:26 +01:00
|
|
|
end
|
2012-08-10 16:33:22 -04:00
|
|
|
linked_keg_record.unlink if linked_keg_record.symlink?
|
2012-10-04 14:15:16 -05:00
|
|
|
$n+$d
|
2009-08-29 21:07:26 +01:00
|
|
|
end
|
|
|
|
|
2011-08-24 11:30:32 +01:00
|
|
|
def fname
|
|
|
|
parent.basename.to_s
|
|
|
|
end
|
|
|
|
|
2013-01-23 00:26:25 -06:00
|
|
|
def lock
|
2013-02-09 18:19:50 -06:00
|
|
|
FormulaLock.new(fname).with_lock { yield }
|
2013-01-23 00:26:25 -06:00
|
|
|
end
|
|
|
|
|
2011-08-24 11:30:32 +01:00
|
|
|
def linked_keg_record
|
|
|
|
@linked_keg_record ||= HOMEBREW_REPOSITORY/"Library/LinkedKegs"/fname
|
|
|
|
end
|
|
|
|
|
2012-01-01 15:18:25 -06:00
|
|
|
def linked?
|
|
|
|
linked_keg_record.directory? and self == linked_keg_record.realpath
|
|
|
|
end
|
|
|
|
|
2012-07-18 03:22:00 -05:00
|
|
|
def completion_installed? shell
|
|
|
|
dir = case shell
|
|
|
|
when :bash then self/'etc/bash_completion.d'
|
|
|
|
when :zsh then self/'share/zsh/site-functions'
|
|
|
|
end
|
|
|
|
return if dir.nil?
|
|
|
|
dir.directory? and not dir.children.length.zero?
|
|
|
|
end
|
|
|
|
|
2012-11-25 15:06:41 +00:00
|
|
|
def plist_installed?
|
|
|
|
Dir.chdir self do
|
|
|
|
not Dir.glob("*.plist").empty?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-07-10 22:08:06 -05:00
|
|
|
def version
|
|
|
|
require 'version'
|
|
|
|
Version.new(basename.to_s)
|
|
|
|
end
|
|
|
|
|
|
|
|
def basename
|
|
|
|
Pathname.new(self.to_s).basename
|
|
|
|
end
|
|
|
|
|
2012-10-20 20:54:11 -05:00
|
|
|
def link mode=OpenStruct.new
|
2011-08-24 11:30:32 +01:00
|
|
|
raise "Cannot link #{fname}\nAnother version is already linked: #{linked_keg_record.realpath}" if linked_keg_record.directory?
|
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
$n=0
|
|
|
|
$d=0
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2012-10-15 18:03:07 -05:00
|
|
|
share_mkpaths = %w[aclocal doc info locale man]
|
|
|
|
share_mkpaths.concat((1..8).map { |i| "man/man#{i}" })
|
|
|
|
share_mkpaths.concat((1..8).map { |i| "man/cat#{i}" })
|
2013-07-02 08:19:29 -07:00
|
|
|
# Paths used by Gnome Desktop support
|
2013-07-13 10:15:54 -07:00
|
|
|
share_mkpaths.concat %w[applications gnome gnome/help icons mime-info pixmaps sounds]
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
# yeah indeed, you have to force anything you need in the main tree into
|
|
|
|
# these dirs REMEMBER that *NOT* everything needs to be in the main tree
|
2012-06-17 16:54:20 -05:00
|
|
|
link_dir('etc', mode) {:mkpath}
|
|
|
|
link_dir('bin', mode) {:skip_dir}
|
|
|
|
link_dir('sbin', mode) {:skip_dir}
|
|
|
|
link_dir('include', mode) {:link}
|
2012-01-04 20:50:19 -06:00
|
|
|
|
2012-06-17 16:54:20 -05:00
|
|
|
link_dir('share', mode) do |path|
|
2012-02-26 19:28:46 -06:00
|
|
|
case path.to_s
|
2012-03-11 10:18:12 -05:00
|
|
|
when 'locale/locale.alias' then :skip_file
|
2012-03-20 14:27:07 -05:00
|
|
|
when INFOFILE_RX then ENV['HOMEBREW_KEEP_INFO'] ? :info : :skip_file
|
2012-02-26 19:28:46 -06:00
|
|
|
when LOCALEDIR_RX then :mkpath
|
|
|
|
when *share_mkpaths then :mkpath
|
2013-07-02 08:19:29 -07:00
|
|
|
when /^icons\/.*\/icon-theme\.cache$/ then :skip_file
|
|
|
|
# all icons subfolders should also mkpath
|
|
|
|
when /^icons\// then :mkpath
|
2012-04-29 02:12:26 -04:00
|
|
|
when /^zsh/ then :mkpath
|
2012-02-26 19:28:46 -06:00
|
|
|
else :link
|
2012-01-04 20:50:19 -06:00
|
|
|
end
|
|
|
|
end
|
2009-07-29 00:56:22 +01:00
|
|
|
|
2012-06-17 16:54:20 -05:00
|
|
|
link_dir('lib', mode) do |path|
|
2009-09-17 21:26:17 +01:00
|
|
|
case path.to_s
|
2012-03-11 10:18:12 -05:00
|
|
|
when 'charset.alias' then :skip_file
|
2010-04-29 15:50:24 -07:00
|
|
|
# pkg-config database gets explicitly created
|
|
|
|
when 'pkgconfig' then :mkpath
|
|
|
|
# lib/language folders also get explicitly created
|
2012-03-08 20:31:32 -06:00
|
|
|
when /^gdk-pixbuf/ then :mkpath
|
2010-04-29 15:50:24 -07:00
|
|
|
when 'ghc' then :mkpath
|
|
|
|
when 'lua' then :mkpath
|
2013-07-31 13:51:05 -05:00
|
|
|
when /^node/ then :mkpath
|
2012-01-30 00:02:28 -06:00
|
|
|
when /^ocaml/ then :mkpath
|
2009-09-17 21:26:17 +01:00
|
|
|
when /^perl5/ then :mkpath
|
2010-04-29 15:50:24 -07:00
|
|
|
when 'php' then :mkpath
|
2012-04-26 20:35:09 -05:00
|
|
|
when /^python[23]\.\d/ then :mkpath
|
2011-03-13 12:19:25 -07:00
|
|
|
when 'ruby' then :mkpath
|
2010-08-21 10:51:43 -07:00
|
|
|
# Everything else is symlinked to the cellar
|
2010-04-29 15:50:24 -07:00
|
|
|
else :link
|
2009-09-17 21:26:17 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-17 09:01:37 +02:00
|
|
|
link_dir('Frameworks', mode) do |path|
|
|
|
|
# Frameworks contain symlinks pointing into a subdir, so we have to use
|
|
|
|
# the :link strategy. However, for Foo.framework and
|
|
|
|
# Foo.framework/Versions we have to use :mkpath so that multiple formulae
|
|
|
|
# can link their versions into it and `brew [un]link` works.
|
|
|
|
if path.to_s =~ /[^\/]*\.framework(\/Versions)?$/
|
|
|
|
:mkpath
|
|
|
|
else
|
|
|
|
:link
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-11-05 18:12:26 -06:00
|
|
|
unless mode.dry_run
|
|
|
|
linked_keg_record.make_relative_symlink(self)
|
|
|
|
optlink
|
|
|
|
end
|
2012-08-10 16:33:22 -04:00
|
|
|
|
2012-03-19 12:24:13 +00:00
|
|
|
return $n + $d
|
2012-08-10 16:33:22 -04:00
|
|
|
rescue Exception
|
|
|
|
opoo "Could not link #{fname}. Unlinking..."
|
|
|
|
unlink
|
|
|
|
raise
|
|
|
|
end
|
|
|
|
|
|
|
|
def optlink
|
|
|
|
from = HOMEBREW_PREFIX/:opt/fname
|
|
|
|
if from.symlink?
|
|
|
|
from.delete
|
|
|
|
elsif from.directory?
|
|
|
|
from.rmdir
|
|
|
|
elsif from.exist?
|
|
|
|
from.delete
|
|
|
|
end
|
|
|
|
from.make_relative_symlink(self)
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
|
2013-04-07 00:49:56 -05:00
|
|
|
protected
|
|
|
|
|
2009-10-23 16:03:38 +01:00
|
|
|
def resolve_any_conflicts dst
|
|
|
|
# if it isn't a directory then a severe conflict is about to happen. Let
|
|
|
|
# it, and the exception that is generated will message to the user about
|
|
|
|
# the situation
|
|
|
|
if dst.symlink? and dst.directory?
|
|
|
|
src = (dst.parent+dst.readlink).cleanpath
|
|
|
|
keg = Keg.for(src)
|
|
|
|
dst.unlink
|
|
|
|
keg.link_dir(src) { :mkpath }
|
|
|
|
return true
|
|
|
|
end
|
2009-12-17 18:45:07 +00:00
|
|
|
rescue NotAKegError
|
|
|
|
puts "Won't resolve conflicts for symlink #{dst} as it doesn't resolve into the Cellar" if ARGV.verbose?
|
2009-10-23 16:03:38 +01:00
|
|
|
end
|
|
|
|
|
2012-10-20 20:54:11 -05:00
|
|
|
def make_relative_symlink dst, src, mode=OpenStruct.new
|
2012-03-19 12:24:13 +00:00
|
|
|
if dst.exist? and dst.realpath == src.realpath
|
|
|
|
puts "Skipping; already exists: #{dst}" if ARGV.verbose?
|
2013-07-10 08:50:53 -07:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2012-06-17 16:54:20 -05:00
|
|
|
# cf. git-clean -n: list files to delete, don't really link or delete
|
2013-07-10 08:50:53 -07:00
|
|
|
if mode.dry_run and mode.overwrite
|
|
|
|
if dst.symlink?
|
|
|
|
puts "#{dst} -> #{dst.resolved_path}"
|
|
|
|
elsif dst.exist?
|
|
|
|
puts dst
|
|
|
|
end
|
2012-06-17 16:54:20 -05:00
|
|
|
return
|
2013-07-10 08:50:53 -07:00
|
|
|
end
|
|
|
|
|
2012-10-20 20:54:11 -05:00
|
|
|
# list all link targets
|
2013-07-10 08:50:53 -07:00
|
|
|
if mode.dry_run
|
2012-10-20 20:54:11 -05:00
|
|
|
puts dst
|
|
|
|
return
|
2012-03-19 12:24:13 +00:00
|
|
|
end
|
2013-07-10 08:50:53 -07:00
|
|
|
|
|
|
|
dst.delete if mode.overwrite && (dst.exist? or dst.symlink?)
|
|
|
|
dst.make_relative_symlink src
|
2012-03-19 12:24:13 +00:00
|
|
|
end
|
|
|
|
|
2013-06-06 16:50:19 +02:00
|
|
|
# symlinks the contents of self+foo recursively into #{HOMEBREW_PREFIX}/foo
|
2012-10-20 20:54:11 -05:00
|
|
|
def link_dir foo, mode=OpenStruct.new
|
2009-10-23 16:03:38 +01:00
|
|
|
root = self+foo
|
2010-04-04 13:45:02 -07:00
|
|
|
return unless root.exist?
|
2009-08-10 16:48:30 +01:00
|
|
|
root.find do |src|
|
|
|
|
next if src == root
|
2009-10-23 16:03:38 +01:00
|
|
|
dst = HOMEBREW_PREFIX+src.relative_path_from(self)
|
2009-08-10 16:48:30 +01:00
|
|
|
dst.extend ObserverPathnameExtension
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
if src.file?
|
2012-02-26 19:28:46 -06:00
|
|
|
Find.prune if File.basename(src) == '.DS_Store'
|
2013-01-21 10:33:56 +01:00
|
|
|
# Don't link pyc files because Python overwrites these cached object
|
|
|
|
# files and next time brew wants to link, the pyc file is in the way.
|
|
|
|
if src.extname.to_s == '.pyc' && src.to_s =~ /site-packages/
|
|
|
|
Find.prune
|
|
|
|
end
|
2012-02-26 19:28:46 -06:00
|
|
|
|
|
|
|
case yield src.relative_path_from(root)
|
2012-03-20 14:27:07 -05:00
|
|
|
when :skip_file, nil
|
2012-02-26 19:28:46 -06:00
|
|
|
Find.prune
|
|
|
|
when :info
|
2012-03-20 16:57:49 -05:00
|
|
|
next if File.basename(src) == 'dir' # skip historical local 'dir' files
|
2012-06-17 16:54:20 -05:00
|
|
|
make_relative_symlink dst, src, mode
|
2012-02-26 19:28:46 -06:00
|
|
|
dst.install_info
|
|
|
|
else
|
2012-06-17 16:54:20 -05:00
|
|
|
make_relative_symlink dst, src, mode
|
2012-02-26 19:28:46 -06:00
|
|
|
end
|
2009-08-10 16:48:30 +01:00
|
|
|
elsif src.directory?
|
2009-10-23 16:03:38 +01:00
|
|
|
# if the dst dir already exists, then great! walk the rest of the tree tho
|
|
|
|
next if dst.directory? and not dst.symlink?
|
2009-07-24 15:10:01 +01:00
|
|
|
# no need to put .app bundles in the path, the user can just use
|
|
|
|
# spotlight, or the open command and actual mac apps use an equivalent
|
2009-08-10 16:48:30 +01:00
|
|
|
Find.prune if src.extname.to_s == '.app'
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
case yield src.relative_path_from(root)
|
2012-03-11 10:18:12 -05:00
|
|
|
when :skip_dir
|
2009-10-23 16:03:38 +01:00
|
|
|
Find.prune
|
|
|
|
when :mkpath
|
|
|
|
dst.mkpath unless resolve_any_conflicts(dst)
|
|
|
|
else
|
|
|
|
unless resolve_any_conflicts(dst)
|
2012-07-11 22:32:00 -05:00
|
|
|
make_relative_symlink dst, src, mode
|
2009-10-23 16:03:38 +01:00
|
|
|
Find.prune
|
|
|
|
end
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2009-08-10 16:48:30 +01:00
|
|
|
end
|
2010-05-10 00:19:14 +01:00
|
|
|
|
|
|
|
require 'keg_fix_install_names'
|