2024-08-12 10:30:59 +01:00
|
|
|
# typed: true # rubocop:todo Sorbet/StrictSigil
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-07-14 11:56:34 -07:00
|
|
|
require "keg_relocate"
|
2018-09-06 18:38:43 +01:00
|
|
|
require "language/python"
|
2017-05-22 03:23:50 +02:00
|
|
|
require "lock_file"
|
2025-06-09 19:06:16 +01:00
|
|
|
require "cachable"
|
2010-09-11 20:22:54 +01:00
|
|
|
|
2020-08-17 18:45:48 +02:00
|
|
|
# Installation prefix of a formula.
|
2014-06-26 19:06:31 -05:00
|
|
|
class Keg
|
2019-11-05 20:33:32 +00:00
|
|
|
extend Cachable
|
|
|
|
|
2020-08-17 18:45:48 +02:00
|
|
|
# Error for when a keg is already linked.
|
2014-04-21 09:40:24 -05:00
|
|
|
class AlreadyLinkedError < RuntimeError
|
|
|
|
def initialize(keg)
|
2017-10-15 02:28:32 +02:00
|
|
|
super <<~EOS
|
2014-06-24 19:04:52 -05:00
|
|
|
Cannot link #{keg.name}
|
2014-04-21 09:40:24 -05:00
|
|
|
Another version is already linked: #{keg.linked_keg_record.resolved_path}
|
2018-06-06 23:34:19 -04:00
|
|
|
EOS
|
2014-04-21 09:40:24 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-17 18:45:48 +02:00
|
|
|
# Error for when a keg cannot be linked.
|
2014-04-21 09:40:23 -05:00
|
|
|
class LinkError < RuntimeError
|
|
|
|
attr_reader :keg, :src, :dst
|
|
|
|
|
2014-09-09 14:24:18 -05:00
|
|
|
def initialize(keg, src, dst, cause)
|
2014-04-21 09:40:23 -05:00
|
|
|
@src = src
|
|
|
|
@dst = dst
|
|
|
|
@keg = keg
|
2014-09-09 14:24:18 -05:00
|
|
|
@cause = cause
|
|
|
|
super(cause.message)
|
|
|
|
set_backtrace(cause.backtrace)
|
2014-04-21 09:40:23 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-17 18:45:48 +02:00
|
|
|
# Error for when a file already exists or belongs to another keg.
|
2014-04-21 09:40:23 -05:00
|
|
|
class ConflictError < LinkError
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(String) }
|
2014-04-21 09:40:23 -05:00
|
|
|
def suggestion
|
|
|
|
conflict = Keg.for(dst)
|
2014-07-30 16:29:10 -05:00
|
|
|
rescue NotAKegError, Errno::ENOENT
|
2014-12-24 16:59:40 +01:00
|
|
|
"already exists. You may want to remove it:\n rm '#{dst}'\n"
|
2014-04-21 09:40:23 -05:00
|
|
|
else
|
2017-10-15 02:28:32 +02:00
|
|
|
<<~EOS
|
|
|
|
is a symlink belonging to #{conflict.name}. You can unlink it:
|
|
|
|
brew unlink #{conflict.name}
|
2014-04-21 09:40:23 -05:00
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(String) }
|
2014-04-21 09:40:23 -05:00
|
|
|
def to_s
|
|
|
|
s = []
|
2014-07-02 10:47:54 -05:00
|
|
|
s << "Could not symlink #{src}"
|
2014-04-21 09:40:23 -05:00
|
|
|
s << "Target #{dst}" << suggestion
|
2017-10-15 02:28:32 +02:00
|
|
|
s << <<~EOS
|
2014-04-21 09:40:23 -05:00
|
|
|
To force the link and overwrite all conflicting files:
|
2014-06-24 19:04:52 -05:00
|
|
|
brew link --overwrite #{keg.name}
|
2014-04-21 09:40:23 -05:00
|
|
|
|
|
|
|
To list all files that would be deleted:
|
Improve user ergonomics of `brew link --overwrite` help
When a Keg is unlinked, brew-link gives a helpful message for how to proceed: adding the `--overwrite` flag.
For safety, it also recommends running in `--dry-run` mode first to see what would be deleted.
So a user's common flow would be:
1. run `brew link foo`
2. get error message with guidance
3. run `brew link --overwrite --dry-run foo`
4. inspect
5. run `brew link --overwrite foo`
In this flow, steps 3-5 are likely very common. Common enough that a user may use their shell history to re-populate their prompt with step 3's command, delete the `--dry-run` flag, and re-run. (The end goal, of course, is to link `foo`.)
The `--dry-run` flag needs to be removed from the command, of course. If it had been at the _end_ of the command, it would make the subsequent modification easier.
Instead of "up arrow, left-arrow a bunch, then backspace over --dry-run, hopefully not backspacing over the formula name", it would be easier for the user if the dry-run flag were already at the end of the command. Then the user can "up arrow, backspace a few times and hit enter".
What's more, if the last arg were `--dry-run`, a more advanced bash user could even use `!:-` to re-run the link command with all-but-the-last-arg.
2024-02-12 09:29:21 -05:00
|
|
|
brew link --overwrite #{keg.name} --dry-run
|
2018-06-06 23:34:19 -04:00
|
|
|
EOS
|
2014-04-21 09:40:23 -05:00
|
|
|
s.join("\n")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-17 18:45:48 +02:00
|
|
|
# Error for when a directory is not writable.
|
2014-04-21 09:40:23 -05:00
|
|
|
class DirectoryNotWritableError < LinkError
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(String) }
|
2018-01-17 10:42:43 +00:00
|
|
|
def to_s
|
|
|
|
<<~EOS
|
|
|
|
Could not symlink #{src}
|
|
|
|
#{dst.dirname} is not writable.
|
2014-04-21 09:40:23 -05:00
|
|
|
EOS
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Locale-specific directories have the form `language[_territory][.codeset][@modifier]`
|
2024-01-18 22:18:42 +00:00
|
|
|
LOCALEDIR_RX = %r{(locale|man)/([a-z]{2}|C|POSIX)(_[A-Z]{2})?(\.[a-zA-Z\-0-9]+(@.+)?)?}
|
2024-09-21 22:28:24 -04:00
|
|
|
INFOFILE_RX = %r{info/([^.].*?\.info(\.gz)?|dir)$}
|
2012-01-21 00:51:20 +01:00
|
|
|
|
2014-03-27 17:13:39 -05:00
|
|
|
# These paths relative to the keg's share directory should always be real
|
|
|
|
# directories in the prefix, never symlinks.
|
|
|
|
SHARE_PATHS = %w[
|
2015-09-29 11:09:25 -07:00
|
|
|
aclocal doc info java locale man
|
2014-03-27 17:13:39 -05:00
|
|
|
man/man1 man/man2 man/man3 man/man4
|
|
|
|
man/man5 man/man6 man/man7 man/man8
|
|
|
|
man/cat1 man/cat2 man/cat3 man/cat4
|
|
|
|
man/cat5 man/cat6 man/cat7 man/cat8
|
|
|
|
applications gnome gnome/help icons
|
2015-11-06 17:32:14 +08:00
|
|
|
mime-info pixmaps sounds postgresql
|
2016-09-17 15:17:27 +01:00
|
|
|
].freeze
|
2014-03-27 17:13:39 -05:00
|
|
|
|
2020-09-11 10:29:21 +01:00
|
|
|
ELISP_EXTENSIONS = %w[.el .elc].freeze
|
|
|
|
PYC_EXTENSIONS = %w[.pyc .pyo].freeze
|
|
|
|
LIBTOOL_EXTENSIONS = %w[.la .lai].freeze
|
|
|
|
|
2025-04-01 15:12:12 +01:00
|
|
|
KEEPME_FILE = ".keepme"
|
|
|
|
|
2020-11-05 15:19:56 -05:00
|
|
|
# @param path if this is a file in a keg, returns the containing {Keg} object.
|
2015-08-03 13:09:07 +01:00
|
|
|
def self.for(path)
|
2020-11-02 12:56:59 +00:00
|
|
|
original_path = path
|
2020-11-27 13:12:09 +00:00
|
|
|
raise Errno::ENOENT, original_path.to_s unless original_path.exist?
|
|
|
|
|
|
|
|
if (path = original_path.realpath)
|
2020-11-02 12:56:59 +00:00
|
|
|
until path.root?
|
|
|
|
return Keg.new(path) if path.parent.parent == HOMEBREW_CELLAR.realpath
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2020-11-02 12:56:59 +00:00
|
|
|
path = path.parent.realpath # realpath() prevents root? failing
|
|
|
|
end
|
2009-10-23 16:03:38 +01:00
|
|
|
end
|
2020-11-02 12:56:59 +00:00
|
|
|
raise NotAKegError, "#{original_path} is not inside a keg"
|
2009-10-23 16:03:38 +01:00
|
|
|
end
|
|
|
|
|
2016-12-27 17:26:21 +00:00
|
|
|
def self.all
|
|
|
|
Formula.racks.flat_map(&:subdirs).map { |d| new(d) }
|
|
|
|
end
|
|
|
|
|
2024-10-05 13:46:24 -07:00
|
|
|
def self.keg_link_directories
|
|
|
|
@keg_link_directories ||= %w[
|
|
|
|
bin etc include lib sbin share var
|
|
|
|
].freeze
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.must_exist_subdirectories
|
|
|
|
@must_exist_subdirectories ||= (
|
|
|
|
keg_link_directories - %w[var] + %w[
|
|
|
|
opt
|
|
|
|
var/homebrew/linked
|
|
|
|
]
|
|
|
|
).map { |dir| HOMEBREW_PREFIX/dir }.sort.uniq.freeze
|
|
|
|
end
|
|
|
|
|
|
|
|
# Keep relatively in sync with
|
|
|
|
# {https://github.com/Homebrew/install/blob/HEAD/install.sh}
|
|
|
|
def self.must_exist_directories
|
|
|
|
@must_exist_directories ||= (must_exist_subdirectories + [
|
|
|
|
HOMEBREW_CELLAR,
|
|
|
|
].sort.uniq).freeze
|
|
|
|
end
|
|
|
|
|
|
|
|
# Keep relatively in sync with
|
|
|
|
# {https://github.com/Homebrew/install/blob/HEAD/install.sh}
|
|
|
|
def self.must_be_writable_directories
|
|
|
|
@must_be_writable_directories ||= (
|
|
|
|
%w[
|
|
|
|
etc/bash_completion.d lib/pkgconfig
|
|
|
|
share/aclocal share/doc share/info share/locale share/man
|
|
|
|
share/man/man1 share/man/man2 share/man/man3 share/man/man4
|
|
|
|
share/man/man5 share/man/man6 share/man/man7 share/man/man8
|
|
|
|
share/zsh share/zsh/site-functions
|
2025-03-02 18:17:38 -08:00
|
|
|
share/pwsh share/pwsh/completions
|
2024-10-05 13:46:24 -07:00
|
|
|
var/log
|
|
|
|
].map { |dir| HOMEBREW_PREFIX/dir } + must_exist_subdirectories + [
|
|
|
|
HOMEBREW_CACHE,
|
|
|
|
HOMEBREW_CELLAR,
|
|
|
|
HOMEBREW_LOCKS,
|
|
|
|
HOMEBREW_LOGS,
|
|
|
|
HOMEBREW_REPOSITORY,
|
|
|
|
Language::Python.homebrew_site_packages,
|
|
|
|
]
|
|
|
|
).sort.uniq.freeze
|
|
|
|
end
|
|
|
|
|
2014-06-30 20:16:01 -05:00
|
|
|
attr_reader :path, :name, :linked_keg_record, :opt_record
|
2020-05-12 08:32:27 +01:00
|
|
|
|
2014-06-26 19:06:31 -05:00
|
|
|
protected :path
|
2014-06-23 22:52:41 -05:00
|
|
|
|
2017-01-18 22:21:22 +00:00
|
|
|
extend Forwardable
|
|
|
|
|
|
|
|
def_delegators :path,
|
2024-04-26 14:04:55 +02:00
|
|
|
:to_path, :hash, :abv, :disk_usage, :file_count, :directory?, :exist?, :/,
|
2019-04-30 08:44:35 +01:00
|
|
|
:join, :rename, :find
|
2017-01-18 22:21:22 +00:00
|
|
|
|
2024-04-28 03:23:21 +02:00
|
|
|
sig { params(path: Pathname).void }
|
2015-08-03 13:09:07 +01:00
|
|
|
def initialize(path)
|
2016-12-31 16:38:05 +00:00
|
|
|
path = path.resolved_path if path.to_s.start_with?("#{HOMEBREW_PREFIX}/opt/")
|
2020-07-09 14:21:09 +01:00
|
|
|
raise "#{path} is not a valid keg" if path.parent.parent.realpath != HOMEBREW_CELLAR.realpath
|
2014-06-26 19:06:31 -05:00
|
|
|
raise "#{path} is not a directory" unless path.directory?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2014-06-26 19:06:31 -05:00
|
|
|
@path = path
|
|
|
|
@name = path.parent.basename.to_s
|
2016-09-15 18:28:42 +01:00
|
|
|
@linked_keg_record = HOMEBREW_LINKED_KEGS/name
|
|
|
|
@opt_record = HOMEBREW_PREFIX/"opt/#{name}"
|
2023-04-27 04:09:28 +01:00
|
|
|
@oldname_opt_records = []
|
2017-02-11 13:43:00 +01:00
|
|
|
@require_relocation = false
|
2014-03-27 22:35:08 -05:00
|
|
|
end
|
|
|
|
|
2015-05-17 20:34:31 +08:00
|
|
|
def rack
|
|
|
|
path.parent
|
|
|
|
end
|
|
|
|
|
2024-04-26 14:04:55 +02:00
|
|
|
sig { returns(String) }
|
|
|
|
def to_s = path.to_s
|
2014-06-26 19:06:31 -05:00
|
|
|
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(String) }
|
2014-06-26 19:06:31 -05:00
|
|
|
def inspect
|
|
|
|
"#<#{self.class.name}:#{path}>"
|
|
|
|
end
|
|
|
|
|
|
|
|
def ==(other)
|
|
|
|
instance_of?(other.class) && path == other.path
|
|
|
|
end
|
2016-09-23 18:13:48 +02:00
|
|
|
alias eql? ==
|
2014-06-26 19:06:31 -05:00
|
|
|
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(T::Boolean) }
|
2016-01-09 18:59:34 +08:00
|
|
|
def empty_installation?
|
2017-02-24 17:44:27 +09:00
|
|
|
Pathname.glob("#{path}/*") do |file|
|
|
|
|
return false if file.directory? && !file.children.reject(&:ds_store?).empty?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-01-09 18:59:34 +08:00
|
|
|
basename = file.basename.to_s
|
2024-07-14 08:49:39 -04:00
|
|
|
|
|
|
|
require "metafiles"
|
2016-01-09 18:59:34 +08:00
|
|
|
next if Metafiles.copy?(basename)
|
|
|
|
next if %w[.DS_Store INSTALL_RECEIPT.json].include?(basename)
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-01-09 18:59:34 +08:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2017-02-11 13:43:00 +01:00
|
|
|
def require_relocation?
|
|
|
|
@require_relocation
|
|
|
|
end
|
|
|
|
|
2014-06-30 20:09:13 -05:00
|
|
|
def linked?
|
|
|
|
linked_keg_record.symlink? &&
|
|
|
|
linked_keg_record.directory? &&
|
|
|
|
path == linked_keg_record.resolved_path
|
|
|
|
end
|
|
|
|
|
|
|
|
def remove_linked_keg_record
|
|
|
|
linked_keg_record.unlink
|
|
|
|
linked_keg_record.parent.rmdir_if_possible
|
|
|
|
end
|
|
|
|
|
2014-06-30 20:16:58 -05:00
|
|
|
def optlinked?
|
|
|
|
opt_record.symlink? && path == opt_record.resolved_path
|
|
|
|
end
|
|
|
|
|
2017-07-28 11:41:12 +01:00
|
|
|
def remove_old_aliases
|
|
|
|
opt = opt_record.parent
|
2020-07-09 14:17:21 +01:00
|
|
|
linkedkegs = linked_keg_record.parent
|
2017-07-28 11:41:12 +01:00
|
|
|
|
|
|
|
tap = begin
|
|
|
|
to_formula.tap
|
2020-03-04 14:17:08 +00:00
|
|
|
rescue
|
2017-07-28 11:41:12 +01:00
|
|
|
# If the formula can't be found, just ignore aliases for now.
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
|
|
|
if tap
|
|
|
|
bad_tap_opt = opt/tap.user
|
2019-02-19 13:11:32 +00:00
|
|
|
FileUtils.rm_rf bad_tap_opt if !bad_tap_opt.symlink? && bad_tap_opt.directory?
|
2017-07-28 11:41:12 +01:00
|
|
|
end
|
|
|
|
|
2017-08-05 18:03:04 +01:00
|
|
|
aliases.each do |a|
|
2018-04-08 16:02:02 -07:00
|
|
|
# versioned aliases are handled below
|
2019-10-13 19:26:39 +01:00
|
|
|
next if a.match?(/.+@./)
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2021-01-14 16:02:06 +00:00
|
|
|
remove_alias_symlink(opt/a, opt_record)
|
|
|
|
remove_alias_symlink(linkedkegs/a, linked_keg_record)
|
2017-08-05 18:03:04 +01:00
|
|
|
end
|
|
|
|
|
2017-07-28 11:41:12 +01:00
|
|
|
Pathname.glob("#{opt_record}@*").each do |a|
|
2018-04-08 16:02:02 -07:00
|
|
|
a = a.basename.to_s
|
2017-07-28 11:41:12 +01:00
|
|
|
next if aliases.include?(a)
|
|
|
|
|
2021-01-14 16:02:06 +00:00
|
|
|
remove_alias_symlink(opt/a, rack)
|
|
|
|
remove_alias_symlink(linkedkegs/a, rack)
|
2017-02-21 18:31:34 +00:00
|
|
|
end
|
2017-07-28 11:41:12 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def remove_opt_record
|
|
|
|
opt_record.unlink
|
2014-06-30 20:16:58 -05:00
|
|
|
opt_record.parent.rmdir_if_possible
|
|
|
|
end
|
|
|
|
|
2021-01-21 13:03:52 +00:00
|
|
|
def uninstall(raise_failures: false)
|
2018-06-06 13:27:59 +01:00
|
|
|
CacheStoreDatabase.use(:linkage) do |db|
|
|
|
|
break unless db.created?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2018-10-13 08:22:51 -07:00
|
|
|
LinkageCacheStore.new(path, db).delete!
|
2018-06-06 13:27:59 +01:00
|
|
|
end
|
|
|
|
|
2024-09-24 10:15:34 +01:00
|
|
|
FileUtils.rm_r(path)
|
2014-06-26 19:06:31 -05:00
|
|
|
path.parent.rmdir_if_possible
|
2014-06-30 20:16:58 -05:00
|
|
|
remove_opt_record if optlinked?
|
2021-01-14 16:02:06 +00:00
|
|
|
remove_linked_keg_record if linked?
|
2017-07-28 11:41:12 +01:00
|
|
|
remove_old_aliases
|
2023-04-27 04:09:28 +01:00
|
|
|
remove_oldname_opt_records
|
2019-05-24 16:46:54 +01:00
|
|
|
rescue Errno::EACCES, Errno::ENOTEMPTY
|
2021-01-21 13:03:52 +00:00
|
|
|
raise if raise_failures
|
|
|
|
|
2019-05-22 10:19:20 +01:00
|
|
|
odie <<~EOS
|
|
|
|
Could not remove #{name} keg! Do so manually:
|
|
|
|
sudo rm -rf #{path}
|
|
|
|
EOS
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
|
2021-01-15 20:15:40 +09:00
|
|
|
def unlink(verbose: false, dry_run: false)
|
2013-08-09 21:09:48 -05:00
|
|
|
ObserverPathnameExtension.reset_counts!
|
2012-07-20 12:01:37 -05:00
|
|
|
|
2013-08-09 11:29:19 -05:00
|
|
|
dirs = []
|
|
|
|
|
2024-10-05 13:46:24 -07:00
|
|
|
keg_directories = self.class.keg_link_directories.map { |d| path/d }
|
|
|
|
.select(&:exist?)
|
2020-07-09 14:21:09 +01:00
|
|
|
keg_directories.each do |dir|
|
2013-02-17 22:54:43 -06:00
|
|
|
dir.find do |src|
|
2014-06-26 19:06:31 -05:00
|
|
|
dst = HOMEBREW_PREFIX + src.relative_path_from(path)
|
2013-08-09 11:29:19 -05:00
|
|
|
dst.extend(ObserverPathnameExtension)
|
2012-10-04 08:52:48 -05:00
|
|
|
|
2013-08-09 11:29:19 -05:00
|
|
|
dirs << dst if dst.directory? && !dst.symlink?
|
|
|
|
|
2012-10-04 08:52:48 -05:00
|
|
|
# check whether the file to be unlinked is from the current keg first
|
2020-07-09 14:21:09 +01:00
|
|
|
next unless dst.symlink?
|
|
|
|
next if src != dst.resolved_path
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2021-01-15 20:15:40 +09:00
|
|
|
if dry_run
|
2016-09-17 15:17:27 +01:00
|
|
|
puts dst
|
2014-07-04 19:31:07 -05:00
|
|
|
Find.prune if src.directory?
|
2016-09-17 15:17:27 +01:00
|
|
|
next
|
2014-07-04 19:31:07 -05:00
|
|
|
end
|
2016-09-17 15:17:27 +01:00
|
|
|
|
2019-10-13 19:26:39 +01:00
|
|
|
dst.uninstall_info if dst.to_s.match?(INFOFILE_RX)
|
2016-09-17 15:17:27 +01:00
|
|
|
dst.unlink
|
|
|
|
Find.prune if src.directory?
|
2012-03-25 13:08:58 +01:00
|
|
|
end
|
2009-08-29 21:07:26 +01:00
|
|
|
end
|
2014-06-23 21:47:52 -05:00
|
|
|
|
2021-01-15 20:15:40 +09:00
|
|
|
unless dry_run
|
2021-01-14 16:02:06 +00:00
|
|
|
remove_old_aliases
|
2015-09-06 15:25:36 +08:00
|
|
|
remove_linked_keg_record if linked?
|
2024-10-05 13:46:24 -07:00
|
|
|
(dirs - self.class.must_exist_subdirectories).reverse_each(&:rmdir_if_possible)
|
2015-09-06 15:25:36 +08:00
|
|
|
end
|
2013-08-09 11:29:19 -05:00
|
|
|
|
2016-05-15 13:07:58 +02:00
|
|
|
ObserverPathnameExtension.n
|
2009-08-29 21:07:26 +01:00
|
|
|
end
|
|
|
|
|
2023-04-27 04:09:28 +01:00
|
|
|
def lock
|
2015-08-18 19:33:24 +08:00
|
|
|
FormulaLock.new(name).with_lock do
|
2023-04-27 04:09:28 +01:00
|
|
|
oldname_locks = oldname_opt_records.map do |record|
|
|
|
|
FormulaLock.new(record.basename.to_s)
|
2015-08-18 19:33:24 +08:00
|
|
|
end
|
2023-04-27 04:09:28 +01:00
|
|
|
oldname_locks.each(&:lock)
|
|
|
|
yield
|
|
|
|
ensure
|
|
|
|
oldname_locks&.each(&:unlock)
|
2015-08-18 19:33:24 +08:00
|
|
|
end
|
2013-01-23 00:26:25 -06:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def completion_installed?(shell)
|
2012-07-18 03:22:00 -05:00
|
|
|
dir = case shell
|
2017-06-01 16:06:51 +02:00
|
|
|
when :bash then path/"etc/bash_completion.d"
|
2024-02-13 18:46:58 +01:00
|
|
|
when :fish then path/"share/fish/vendor_completions.d"
|
2016-12-03 20:45:54 -06:00
|
|
|
when :zsh
|
2017-03-07 10:36:57 -08:00
|
|
|
dir = path/"share/zsh/site-functions"
|
|
|
|
dir if dir.directory? && dir.children.any? { |f| f.basename.to_s.start_with?("_") }
|
2025-03-02 18:17:38 -08:00
|
|
|
when :pwsh then path/"share/pwsh/completions"
|
2016-09-21 08:32:57 +02:00
|
|
|
end
|
2017-09-24 19:24:46 +01:00
|
|
|
dir&.directory? && !dir.children.empty?
|
2012-07-18 03:22:00 -05:00
|
|
|
end
|
|
|
|
|
2017-03-07 10:36:57 -08:00
|
|
|
def functions_installed?(shell)
|
|
|
|
case shell
|
|
|
|
when :fish
|
|
|
|
dir = path/"share/fish/vendor_functions.d"
|
|
|
|
dir.directory? && !dir.children.empty?
|
|
|
|
when :zsh
|
|
|
|
# Check for non completion functions (i.e. files not started with an underscore),
|
|
|
|
# since those can be checked separately
|
|
|
|
dir = path/"share/zsh/site-functions"
|
|
|
|
dir.directory? && dir.children.any? { |f| !f.basename.to_s.start_with?("_") }
|
|
|
|
end
|
2016-12-03 11:36:49 -06:00
|
|
|
end
|
|
|
|
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(T::Boolean) }
|
2012-11-25 15:06:41 +00:00
|
|
|
def plist_installed?
|
2016-08-05 22:01:32 +08:00
|
|
|
!Dir["#{path}/*.plist"].empty?
|
2012-11-25 15:06:41 +00:00
|
|
|
end
|
|
|
|
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(T::Array[Pathname]) }
|
2015-10-20 07:05:21 +02:00
|
|
|
def apps
|
|
|
|
app_prefix = optlinked? ? opt_record : path
|
|
|
|
Pathname.glob("#{app_prefix}/{,libexec/}*.app")
|
|
|
|
end
|
|
|
|
|
2015-07-05 14:45:26 -07:00
|
|
|
def elisp_installed?
|
2015-11-01 18:04:42 -08:00
|
|
|
return false unless (path/"share/emacs/site-lisp"/name).exist?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2020-09-11 10:29:21 +01:00
|
|
|
(path/"share/emacs/site-lisp"/name).children.any? { |f| ELISP_EXTENSIONS.include? f.extname }
|
2015-07-05 14:45:26 -07:00
|
|
|
end
|
|
|
|
|
2025-02-19 14:51:36 -05:00
|
|
|
sig { returns(PkgVersion) }
|
2012-07-10 22:08:06 -05:00
|
|
|
def version
|
2015-08-03 13:09:07 +01:00
|
|
|
require "pkg_version"
|
2014-06-26 19:06:31 -05:00
|
|
|
PkgVersion.parse(path.basename.to_s)
|
2014-03-27 17:05:17 -05:00
|
|
|
end
|
|
|
|
|
2024-03-29 23:07:38 +00:00
|
|
|
def version_scheme
|
|
|
|
@version_scheme ||= tab.version_scheme
|
|
|
|
end
|
|
|
|
|
2024-03-31 16:53:15 -07:00
|
|
|
# For ordering kegs by version with `.sort_by`, `.max_by`, etc.
|
|
|
|
# @see Formula.version_scheme
|
|
|
|
def scheme_and_version
|
|
|
|
[version_scheme, version]
|
|
|
|
end
|
|
|
|
|
2016-09-27 22:01:22 +01:00
|
|
|
def to_formula
|
2016-09-22 13:27:33 +01:00
|
|
|
Formulary.from_keg(self)
|
|
|
|
end
|
|
|
|
|
2023-04-27 04:09:28 +01:00
|
|
|
def oldname_opt_records
|
|
|
|
return @oldname_opt_records unless @oldname_opt_records.empty?
|
|
|
|
|
|
|
|
@oldname_opt_records = if (opt_dir = HOMEBREW_PREFIX/"opt").directory?
|
|
|
|
opt_dir.subdirs.select do |dir|
|
2015-08-16 17:16:44 +03:00
|
|
|
dir.symlink? && dir != opt_record && path.parent == dir.resolved_path.parent
|
|
|
|
end
|
2023-04-27 04:09:28 +01:00
|
|
|
else
|
|
|
|
[]
|
2015-08-16 17:16:44 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-15 20:15:40 +09:00
|
|
|
def link(verbose: false, dry_run: false, overwrite: false)
|
2016-09-17 15:17:27 +01:00
|
|
|
raise AlreadyLinkedError, self if linked_keg_record.directory?
|
2011-08-24 11:30:32 +01:00
|
|
|
|
2013-08-09 21:09:48 -05:00
|
|
|
ObserverPathnameExtension.reset_counts!
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
optlink(verbose:, dry_run:, overwrite:) unless dry_run
|
2016-07-17 01:59:54 +08: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
|
2024-03-07 16:20:20 +00:00
|
|
|
link_dir("etc", verbose:, dry_run:, overwrite:) { :mkpath }
|
|
|
|
link_dir("bin", verbose:, dry_run:, overwrite:) { :skip_dir }
|
|
|
|
link_dir("sbin", verbose:, dry_run:, overwrite:) { :skip_dir }
|
2024-03-28 17:26:58 -04:00
|
|
|
link_dir("include", verbose:, dry_run:, overwrite:) do |relative_path|
|
|
|
|
case relative_path.to_s
|
2024-04-11 10:24:45 -04:00
|
|
|
when /^postgresql@\d+/
|
2024-03-28 17:26:58 -04:00
|
|
|
:mkpath
|
|
|
|
else
|
|
|
|
:link
|
|
|
|
end
|
|
|
|
end
|
2012-01-04 20:50:19 -06:00
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
link_dir("share", verbose:, dry_run:, overwrite:) do |relative_path|
|
2014-12-26 11:58:09 -05:00
|
|
|
case relative_path.to_s
|
2014-02-22 09:20:09 -08:00
|
|
|
when INFOFILE_RX then :info
|
2020-11-13 10:07:02 -05:00
|
|
|
when "locale/locale.alias",
|
|
|
|
%r{^icons/.*/icon-theme\.cache$}
|
|
|
|
:skip_file
|
|
|
|
when LOCALEDIR_RX,
|
|
|
|
%r{^icons/}, # all icons subfolders should also mkpath
|
|
|
|
/^zsh/,
|
|
|
|
/^fish/,
|
|
|
|
%r{^lua/}, # Lua, Lua51, Lua53 all need the same handling.
|
|
|
|
%r{^guile/},
|
2024-04-11 10:24:45 -04:00
|
|
|
/^postgresql@\d+/,
|
2020-11-13 10:07:02 -05:00
|
|
|
*SHARE_PATHS
|
|
|
|
:mkpath
|
2020-11-13 17:21:51 +01:00
|
|
|
else
|
|
|
|
:link
|
2012-01-04 20:50:19 -06:00
|
|
|
end
|
|
|
|
end
|
2009-07-29 00:56:22 +01:00
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
link_dir("lib", verbose:, dry_run:, overwrite:) do |relative_path|
|
2014-12-26 11:58:09 -05:00
|
|
|
case relative_path.to_s
|
2020-11-13 17:21:51 +01:00
|
|
|
when "charset.alias"
|
|
|
|
:skip_file
|
2020-11-13 10:07:02 -05:00
|
|
|
when "pkgconfig", # pkg-config database gets explicitly created
|
|
|
|
"cmake", # cmake database gets explicitly created
|
|
|
|
"dtrace", # lib/language folders also get explicitly created
|
|
|
|
/^gdk-pixbuf/,
|
|
|
|
"ghc",
|
|
|
|
/^gio/,
|
2022-07-11 10:10:38 +08:00
|
|
|
/^lua/,
|
2020-11-13 10:07:02 -05:00
|
|
|
/^mecab/,
|
|
|
|
/^node/,
|
|
|
|
/^ocaml/,
|
|
|
|
/^perl5/,
|
|
|
|
"php",
|
2024-04-11 10:24:45 -04:00
|
|
|
/^postgresql@\d+/,
|
2021-07-03 23:33:09 +01:00
|
|
|
/^python[23]\.\d+/,
|
2020-11-13 10:07:02 -05:00
|
|
|
/^R/,
|
2020-11-13 17:21:51 +01:00
|
|
|
/^ruby/
|
2020-11-13 10:07:02 -05:00
|
|
|
:mkpath
|
2020-11-13 17:21:51 +01:00
|
|
|
else
|
|
|
|
# Everything else is symlinked to the cellar
|
|
|
|
:link
|
2009-09-17 21:26:17 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
link_dir("Frameworks", verbose:, dry_run:, overwrite:) do |relative_path|
|
2013-06-17 09:01:37 +02:00
|
|
|
# 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.
|
2019-10-13 19:26:39 +01:00
|
|
|
if relative_path.to_s.match?(%r{[^/]*\.framework(/Versions)?$})
|
2013-06-17 09:01:37 +02:00
|
|
|
:mkpath
|
|
|
|
else
|
|
|
|
:link
|
|
|
|
end
|
|
|
|
end
|
2024-03-07 16:20:20 +00:00
|
|
|
make_relative_symlink(linked_keg_record, path, verbose:, dry_run:, overwrite:) unless dry_run
|
2014-04-21 09:40:24 -05:00
|
|
|
rescue LinkError
|
2024-03-07 16:20:20 +00:00
|
|
|
unlink(verbose:)
|
2012-08-10 16:33:22 -04:00
|
|
|
raise
|
2014-04-21 09:40:24 -05:00
|
|
|
else
|
2016-05-15 13:07:58 +02:00
|
|
|
ObserverPathnameExtension.n
|
2012-08-10 16:33:22 -04:00
|
|
|
end
|
|
|
|
|
2022-07-30 11:08:52 +01:00
|
|
|
def prepare_debug_symbols; end
|
2022-07-26 00:00:45 +01:00
|
|
|
|
2022-12-12 16:47:56 +00:00
|
|
|
def consistent_reproducible_symlink_permissions!; end
|
|
|
|
|
2023-04-27 04:09:28 +01:00
|
|
|
def remove_oldname_opt_records
|
|
|
|
oldname_opt_records.reject! do |record|
|
|
|
|
return false if record.resolved_path != path
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2023-04-27 04:09:28 +01:00
|
|
|
record.unlink
|
|
|
|
record.parent.rmdir_if_possible
|
|
|
|
true
|
|
|
|
end
|
2015-08-16 17:16:44 +03:00
|
|
|
end
|
|
|
|
|
2024-04-28 03:23:21 +02:00
|
|
|
sig { returns(Tab) }
|
2018-03-25 12:47:57 +01:00
|
|
|
def tab
|
|
|
|
Tab.for_keg(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
def runtime_dependencies
|
2019-11-05 20:33:32 +00:00
|
|
|
Keg.cache[:runtime_dependencies] ||= {}
|
|
|
|
Keg.cache[:runtime_dependencies][path] ||= tab.runtime_dependencies
|
2018-03-25 12:47:57 +01:00
|
|
|
end
|
|
|
|
|
2016-09-30 03:22:09 -07:00
|
|
|
def aliases
|
2018-03-25 12:47:57 +01:00
|
|
|
tab.aliases || []
|
2016-09-30 03:22:09 -07:00
|
|
|
end
|
|
|
|
|
2021-01-15 20:15:40 +09:00
|
|
|
def optlink(verbose: false, dry_run: false, overwrite: false)
|
2014-06-30 21:53:48 -05:00
|
|
|
opt_record.delete if opt_record.symlink? || opt_record.exist?
|
2024-03-07 16:20:20 +00:00
|
|
|
make_relative_symlink(opt_record, path, verbose:, dry_run:, overwrite:)
|
2016-10-03 02:41:17 -07:00
|
|
|
aliases.each do |a|
|
|
|
|
alias_opt_record = opt_record.parent/a
|
|
|
|
alias_opt_record.delete if alias_opt_record.symlink? || alias_opt_record.exist?
|
2024-03-07 16:20:20 +00:00
|
|
|
make_relative_symlink(alias_opt_record, path, verbose:, dry_run:, overwrite:)
|
2016-09-30 03:22:09 -07:00
|
|
|
end
|
2015-08-16 17:16:44 +03:00
|
|
|
|
2023-04-27 04:09:28 +01:00
|
|
|
oldname_opt_records.each do |record|
|
|
|
|
record.delete
|
2024-03-07 16:20:20 +00:00
|
|
|
make_relative_symlink(record, path, verbose:, dry_run:, overwrite:)
|
2023-04-27 04:09:28 +01:00
|
|
|
end
|
2009-07-24 15:10:01 +01:00
|
|
|
end
|
|
|
|
|
2014-03-13 09:05:40 +00:00
|
|
|
def delete_pyc_files!
|
2023-03-21 21:42:51 -07:00
|
|
|
path.find { |pn| pn.delete if PYC_EXTENSIONS.include?(pn.extname) }
|
|
|
|
path.find { |pn| FileUtils.rm_rf pn if pn.basename.to_s == "__pycache__" }
|
2014-03-13 09:05:40 +00:00
|
|
|
end
|
|
|
|
|
2025-02-03 22:57:37 -05:00
|
|
|
def normalize_pod2man_outputs!
|
2025-02-28 03:30:27 -05:00
|
|
|
# Only process uncompressed manpages, which end in a digit
|
|
|
|
manpages = Dir[path/"share/man/*/*.[1-9]"]
|
2025-02-03 22:57:37 -05:00
|
|
|
generated_regex = /^\.\\"\s*Automatically generated by .*\n/
|
|
|
|
manpages.each do |f|
|
|
|
|
manpage = Pathname.new(f)
|
|
|
|
next unless manpage.file?
|
|
|
|
|
|
|
|
content = manpage.read
|
2025-03-10 03:06:46 -04:00
|
|
|
unless content.valid_encoding?
|
|
|
|
# Occasionally, a manpage might not be encoded as UTF-8. ISO-8859-1 is a
|
|
|
|
# common alternative that's worth trying in this case.
|
|
|
|
content = File.read(manpage, encoding: "ISO-8859-1")
|
|
|
|
|
|
|
|
# If the encoding is still invalid, we can't do anything about it.
|
|
|
|
next unless content.valid_encoding?
|
|
|
|
end
|
|
|
|
|
2025-02-03 22:57:37 -05:00
|
|
|
content = content.gsub(generated_regex, "")
|
|
|
|
content = content.lines.map do |line|
|
|
|
|
next line unless line.start_with?(".TH")
|
|
|
|
|
|
|
|
# Split the line by spaces, but preserve quoted strings
|
|
|
|
parts = line.split(/\s(?=(?:[^"]|"[^"]*")*$)/)
|
|
|
|
next line if parts.length != 6
|
|
|
|
|
|
|
|
# pod2man embeds the perl version used into the 5th field of the footer
|
|
|
|
T.must(parts[4]).gsub!(/^"perl v.*"$/, "\"\"")
|
|
|
|
"#{parts.join(" ")}\n"
|
|
|
|
end.join
|
|
|
|
|
|
|
|
manpage.atomic_write(content)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2025-04-01 15:12:12 +01:00
|
|
|
sig { returns(T::Array[String]) }
|
|
|
|
def keepme_refs
|
|
|
|
keepme = path/KEEPME_FILE
|
|
|
|
return [] if !keepme.exist? || !keepme.readable?
|
|
|
|
|
|
|
|
keepme.readlines.select { |ref| File.exist?(ref.strip) }
|
|
|
|
end
|
|
|
|
|
2021-07-03 19:23:19 +01:00
|
|
|
def binary_executable_or_library_files
|
2023-01-26 21:41:45 +09:00
|
|
|
[]
|
2021-07-03 19:23:19 +01:00
|
|
|
end
|
|
|
|
|
2022-04-08 18:09:18 -07:00
|
|
|
def codesign_patched_binary(file); end
|
|
|
|
|
2014-06-26 18:45:34 -05:00
|
|
|
private
|
2013-04-07 00:49:56 -05:00
|
|
|
|
2021-01-15 20:15:40 +09:00
|
|
|
def resolve_any_conflicts(dst, dry_run: false, verbose: false, overwrite: false)
|
2014-10-20 17:07:53 -05:00
|
|
|
return unless dst.symlink?
|
|
|
|
|
2014-07-12 19:56:58 -05:00
|
|
|
src = dst.resolved_path
|
2014-10-20 17:07:54 -05:00
|
|
|
|
2024-04-30 11:10:23 +02:00
|
|
|
# `src` itself may be a symlink, so check lstat to ensure we are dealing with
|
|
|
|
# a directory and not a symlink pointing to a directory (which needs to be
|
2014-07-12 20:15:57 -05:00
|
|
|
# treated as a file). In other words, we only want to resolve one symlink.
|
2014-10-20 17:07:54 -05:00
|
|
|
|
|
|
|
begin
|
|
|
|
stat = src.lstat
|
|
|
|
rescue Errno::ENOENT
|
|
|
|
# dst is a broken symlink, so remove it.
|
2021-01-15 20:15:40 +09:00
|
|
|
dst.unlink unless dry_run
|
2014-10-20 17:07:54 -05:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2016-09-23 22:02:23 +02:00
|
|
|
return unless stat.directory?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-09-23 22:02:23 +02:00
|
|
|
begin
|
|
|
|
keg = Keg.for(src)
|
|
|
|
rescue NotAKegError
|
2021-01-26 15:21:24 -05:00
|
|
|
puts "Won't resolve conflicts for symlink #{dst} as it doesn't resolve into the Cellar." if verbose
|
2016-09-23 22:02:23 +02:00
|
|
|
return
|
2009-10-23 16:03:38 +01:00
|
|
|
end
|
2016-09-23 22:02:23 +02:00
|
|
|
|
2021-01-15 20:15:40 +09:00
|
|
|
dst.unlink unless dry_run
|
|
|
|
keg.link_dir(src, dry_run: false, verbose: false, overwrite: false) { :mkpath }
|
2016-09-23 22:02:23 +02:00
|
|
|
true
|
2009-10-23 16:03:38 +01:00
|
|
|
end
|
|
|
|
|
2021-01-15 20:15:40 +09:00
|
|
|
def make_relative_symlink(dst, src, verbose: false, dry_run: false, overwrite: false)
|
2014-07-04 17:22:58 -05:00
|
|
|
if dst.symlink? && src == dst.resolved_path
|
2021-01-15 20:15:40 +09:00
|
|
|
puts "Skipping; link already exists: #{dst}" if 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
|
2021-01-15 20:15:40 +09:00
|
|
|
if dry_run && overwrite
|
2013-07-10 08:50:53 -07:00
|
|
|
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
|
2021-01-15 20:15:40 +09:00
|
|
|
if 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
|
|
|
|
2021-01-15 20:15:40 +09:00
|
|
|
dst.delete if overwrite && (dst.exist? || dst.symlink?)
|
2014-04-21 09:40:23 -05:00
|
|
|
dst.make_relative_symlink(src)
|
2014-09-09 14:24:18 -05:00
|
|
|
rescue Errno::EEXIST => e
|
2016-09-23 22:02:23 +02:00
|
|
|
raise ConflictError.new(self, src.relative_path_from(path), dst, e) if dst.exist?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-09-23 22:02:23 +02:00
|
|
|
if dst.symlink?
|
2014-04-21 09:40:23 -05:00
|
|
|
dst.unlink
|
|
|
|
retry
|
|
|
|
end
|
2014-09-09 14:24:18 -05:00
|
|
|
rescue Errno::EACCES => e
|
|
|
|
raise DirectoryNotWritableError.new(self, src.relative_path_from(path), dst, e)
|
|
|
|
rescue SystemCallError => e
|
|
|
|
raise LinkError.new(self, src.relative_path_from(path), dst, e)
|
2012-03-19 12:24:13 +00:00
|
|
|
end
|
|
|
|
|
2021-01-14 16:02:06 +00:00
|
|
|
def remove_alias_symlink(alias_symlink, alias_match_path)
|
|
|
|
if alias_symlink.symlink? && alias_symlink.exist?
|
2021-01-15 08:57:02 +00:00
|
|
|
alias_symlink.delete if alias_match_path.exist? && alias_symlink.realpath == alias_match_path.realpath
|
2021-01-14 16:02:06 +00:00
|
|
|
elsif alias_symlink.symlink? || alias_symlink.exist?
|
|
|
|
alias_symlink.delete
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-06-26 18:45:34 -05:00
|
|
|
protected
|
|
|
|
|
2014-06-26 19:06:31 -05:00
|
|
|
# symlinks the contents of path+relative_dir recursively into #{HOMEBREW_PREFIX}/relative_dir
|
2021-01-15 20:15:40 +09:00
|
|
|
def link_dir(relative_dir, verbose: false, dry_run: false, overwrite: false)
|
2017-06-01 16:06:51 +02:00
|
|
|
root = path/relative_dir
|
2010-04-04 13:45:02 -07:00
|
|
|
return unless root.exist?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2009-08-10 16:48:30 +01:00
|
|
|
root.find do |src|
|
|
|
|
next if src == root
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2014-06-26 19:06:31 -05:00
|
|
|
dst = HOMEBREW_PREFIX + src.relative_path_from(path)
|
2009-08-10 16:48:30 +01:00
|
|
|
dst.extend ObserverPathnameExtension
|
2009-07-24 15:10:01 +01:00
|
|
|
|
2014-07-12 19:56:58 -05:00
|
|
|
if src.symlink? || src.file?
|
2015-08-03 13:09:07 +01:00
|
|
|
Find.prune if File.basename(src) == ".DS_Store"
|
2017-01-06 16:35:41 +00:00
|
|
|
Find.prune if src.resolved_path == dst
|
2016-06-21 00:22:58 +01:00
|
|
|
# Don't link pyc or pyo files because Python overwrites these
|
|
|
|
# cached object files and next time brew wants to link, the
|
|
|
|
# file is in the way.
|
2020-09-11 10:29:21 +01:00
|
|
|
Find.prune if PYC_EXTENSIONS.include?(src.extname) && src.to_s.include?("/site-packages/")
|
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
|
2015-08-03 13:09:07 +01:00
|
|
|
next if File.basename(src) == "dir" # skip historical local 'dir' files
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
make_relative_symlink(dst, src, verbose:, dry_run:, overwrite:)
|
2012-02-26 19:28:46 -06:00
|
|
|
dst.install_info
|
|
|
|
else
|
2024-03-07 16:20:20 +00:00
|
|
|
make_relative_symlink dst, src, verbose:, dry_run:, overwrite:
|
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
|
2015-08-03 13:09:07 +01:00
|
|
|
next if dst.directory? && !dst.symlink?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
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
|
2015-08-03 13:09:07 +01:00
|
|
|
Find.prune if src.extname == ".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
|
2024-03-07 16:20:20 +00:00
|
|
|
dst.mkpath unless resolve_any_conflicts(dst, verbose:, dry_run:, overwrite:)
|
2009-10-23 16:03:38 +01:00
|
|
|
else
|
2024-03-07 16:20:20 +00:00
|
|
|
unless resolve_any_conflicts(dst, verbose:, dry_run:, overwrite:)
|
|
|
|
make_relative_symlink(dst, src, verbose:, dry_run:, overwrite:)
|
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
|
2018-10-04 22:55:00 -07:00
|
|
|
|
|
|
|
require "extend/os/keg"
|