brew/Library/Homebrew/keg_relocate.rb

245 lines
7.1 KiB
Ruby
Raw Normal View History

2020-10-10 14:16:11 +02:00
# typed: false
# frozen_string_literal: true
2014-06-26 19:06:31 -05:00
class Keg
PREFIX_PLACEHOLDER = "@@HOMEBREW_PREFIX@@"
CELLAR_PLACEHOLDER = "@@HOMEBREW_CELLAR@@"
REPOSITORY_PLACEHOLDER = "@@HOMEBREW_REPOSITORY@@"
LIBRARY_PLACEHOLDER = "@@HOMEBREW_LIBRARY@@"
2021-04-29 17:52:10 +01:00
PERL_PLACEHOLDER = "@@HOMEBREW_PERL@@"
2021-04-29 17:52:10 +01:00
class Relocation
extend T::Sig
def initialize
@replacement_map = {}
end
def freeze
@replacement_map.freeze
super
end
sig { params(key: Symbol, old_value: T.any(String, Regexp), new_value: String).void }
def add_replacement_pair(key, old_value, new_value)
@replacement_map[key] = [old_value, new_value]
end
sig { params(key: Symbol).returns(T::Array[T.any(String, Regexp)]) }
def replacement_pair_for(key)
@replacement_map.fetch(key)
end
sig { params(text: String).void }
def replace_text(text)
replacements = @replacement_map.values.to_h
sorted_keys = replacements.keys.sort_by do |key|
key.is_a?(String) ? key.length : 999
end.reverse
any_changed = false
sorted_keys.each do |key|
changed = text.gsub!(key, replacements[key])
any_changed ||= changed
end
any_changed
end
end
def fix_dynamic_linkage
2015-09-11 16:32:14 +08:00
symlink_files.each do |file|
link = file.readlink
# Don't fix relative symlinks
next unless link.absolute?
2018-09-17 02:45:00 +02:00
2017-04-05 09:00:11 +01:00
link_starts_cellar = link.to_s.start_with?(HOMEBREW_CELLAR.to_s)
link_starts_prefix = link.to_s.start_with?(HOMEBREW_PREFIX.to_s)
next if !link_starts_cellar && !link_starts_prefix
2018-09-17 02:45:00 +02:00
2017-04-02 10:26:05 -07:00
new_src = link.relative_path_from(file.parent)
file.unlink
FileUtils.ln_s(new_src, file)
2015-09-11 16:32:14 +08:00
end
end
2016-09-23 18:13:48 +02:00
alias generic_fix_dynamic_linkage fix_dynamic_linkage
def relocate_dynamic_linkage(_relocation)
[]
end
2021-04-29 17:52:10 +01:00
def prepare_relocation_to_placeholders
relocation = Relocation.new
relocation.add_replacement_pair(:prefix, HOMEBREW_PREFIX.to_s, PREFIX_PLACEHOLDER)
relocation.add_replacement_pair(:cellar, HOMEBREW_CELLAR.to_s, CELLAR_PLACEHOLDER)
# when HOMEBREW_PREFIX == HOMEBREW_REPOSITORY we should use HOMEBREW_PREFIX for all relocations to avoid
# being unable to differentiate between them.
if HOMEBREW_PREFIX != HOMEBREW_REPOSITORY
relocation.add_replacement_pair(:repository, HOMEBREW_REPOSITORY.to_s, REPOSITORY_PLACEHOLDER)
end
relocation.add_replacement_pair(:library, HOMEBREW_LIBRARY.to_s, LIBRARY_PLACEHOLDER)
relocation.add_replacement_pair(:perl,
%r{\A#!(?:/usr/bin/perl\d\.\d+|#{HOMEBREW_PREFIX}/opt/perl/bin/perl)( |$)}o,
"#!#{PERL_PLACEHOLDER}\\1")
2021-04-29 17:52:10 +01:00
relocation
end
alias generic_prepare_relocation_to_placeholders prepare_relocation_to_placeholders
def replace_locations_with_placeholders
2021-04-29 17:52:10 +01:00
relocation = prepare_relocation_to_placeholders.freeze
relocate_dynamic_linkage(relocation)
replace_text_in_files(relocation)
end
2021-04-29 17:52:10 +01:00
def prepare_relocation_to_locations
relocation = Relocation.new
relocation.add_replacement_pair(:prefix, PREFIX_PLACEHOLDER, HOMEBREW_PREFIX.to_s)
relocation.add_replacement_pair(:cellar, CELLAR_PLACEHOLDER, HOMEBREW_CELLAR.to_s)
relocation.add_replacement_pair(:repository, REPOSITORY_PLACEHOLDER, HOMEBREW_REPOSITORY.to_s)
relocation.add_replacement_pair(:library, LIBRARY_PLACEHOLDER, HOMEBREW_LIBRARY.to_s)
relocation.add_replacement_pair(:perl, PERL_PLACEHOLDER, "#{HOMEBREW_PREFIX}/opt/perl/bin/perl")
relocation
end
alias generic_prepare_relocation_to_locations prepare_relocation_to_locations
def replace_placeholders_with_locations(files, skip_linkage: false)
2021-04-29 17:52:10 +01:00
relocation = prepare_relocation_to_locations.freeze
relocate_dynamic_linkage(relocation) unless skip_linkage
replace_text_in_files(relocation, files: files)
end
def replace_text_in_files(relocation, files: nil)
files ||= text_files | libtool_files
2015-07-23 16:29:37 +08:00
changed_files = []
files.map(&path.method(:join)).group_by { |f| f.stat.ino }.each_value do |first, *rest|
s = first.open("rb", &:read)
2021-04-29 17:52:10 +01:00
next unless relocation.replace_text(s)
2018-09-17 02:45:00 +02:00
changed_files += [first, *rest].map { |file| file.relative_path_from(path) }
begin
first.atomic_write(s)
rescue SystemCallError
first.ensure_writable do
first.open("wb") { |f| f.write(s) }
end
else
rest.each { |file| FileUtils.ln(first, file, force: true) }
end
end
changed_files
end
def detect_cxx_stdlibs(_options = {})
[]
end
2017-04-02 09:04:49 -07:00
def recursive_fgrep_args
# for GNU grep; overridden for BSD grep on OS X
"-lr"
end
alias generic_recursive_fgrep_args recursive_fgrep_args
def each_unique_file_matching(string)
Utils.popen_read("fgrep", recursive_fgrep_args, string, to_s) do |io|
hardlinks = Set.new
until io.eof?
file = Pathname.new(io.readline.chomp)
next if file.symlink?
2018-09-17 02:45:00 +02:00
yield file if hardlinks.add? file.stat.ino
end
end
end
2014-06-26 19:06:31 -05:00
def lib
2017-06-01 16:06:51 +02:00
path/"lib"
2014-06-26 19:06:31 -05:00
end
def libexec
path/"libexec"
end
def text_files
text_files = []
return text_files if !which("file") || !which("xargs")
# file has known issues with reading files on other locales. Has
# been fixed upstream for some time, but a sufficiently new enough
# file with that fix is only available in macOS Sierra.
# https://bugs.gw.com/view.php?id=292
with_custom_locale("C") do
files = Set.new path.find.reject { |pn|
next true if pn.symlink?
next true if pn.directory?
next false if pn.basename.to_s == "orig-prefix.txt" # for python virtualenvs
next true if pn == self/".brew/#{name}.rb"
next true if Metafiles::EXTENSIONS.include?(pn.extname)
2018-09-17 02:45:00 +02:00
if pn.text_executable?
text_files << pn
next true
end
false
}
output, _status = Open3.capture2("xargs -0 file --no-dereference --print0",
stdin_data: files.to_a.join("\0"))
# `file` output sometimes contains data from the file, which may include
# invalid UTF-8 entities, so tell Ruby this is just a bytestring
output.force_encoding(Encoding::ASCII_8BIT)
output.each_line do |line|
path, info = line.split("\0", 2)
2016-10-15 23:52:55 -07:00
# `file` sometimes prints more than one line of output per file;
# subsequent lines do not contain a null-byte separator, so `info`
# will be `nil` for those lines
next unless info
next unless info.include?("text")
2018-09-17 02:45:00 +02:00
path = Pathname.new(path)
next unless files.include?(path)
2018-09-17 02:45:00 +02:00
text_files << path
end
end
text_files
end
2015-07-23 16:29:37 +08:00
def libtool_files
libtool_files = []
path.find do |pn|
2020-12-01 17:04:59 +00:00
next if pn.symlink? || pn.directory? || Keg::LIBTOOL_EXTENSIONS.exclude?(pn.extname)
2018-09-17 02:45:00 +02:00
2015-07-23 16:29:37 +08:00
libtool_files << pn
end
2015-07-23 16:29:37 +08:00
libtool_files
end
2015-09-11 16:32:14 +08:00
def symlink_files
symlink_files = []
path.find do |pn|
symlink_files << pn if pn.symlink?
end
symlink_files
end
def self.file_linked_libraries(_file, _string)
[]
end
def self.relocation_formulae
[]
end
def self.bottle_dependencies
relocation_formulae
end
end
require "extend/os/keg_relocate"