keg_relocate: replace Perl shebangs

This commit is contained in:
Bo Anderson 2021-04-29 17:52:10 +01:00
parent 91ab5fe0ce
commit ae49b06600
No known key found for this signature in database
GPG Key ID: 3DB94E204E137D65
3 changed files with 102 additions and 45 deletions

View File

@ -11,9 +11,11 @@ class Keg
# Patching patchelf using itself fails with "Text file busy" or SIGBUS. # Patching patchelf using itself fails with "Text file busy" or SIGBUS.
return if name == "patchelf" return if name == "patchelf"
old_prefix, new_prefix = relocation.replacement_pair_for(:prefix)
elf_files.each do |file| elf_files.each do |file|
file.ensure_writable do file.ensure_writable do
change_rpath(file, relocation.old_prefix, relocation.new_prefix) change_rpath(file, old_prefix, new_prefix)
end end
end end
end end

View File

@ -19,18 +19,21 @@ class Keg
undef relocate_dynamic_linkage undef relocate_dynamic_linkage
def relocate_dynamic_linkage(relocation) def relocate_dynamic_linkage(relocation)
old_prefix, new_prefix = relocation.replacement_pair_for(:prefix)
old_cellar, new_cellar = relocation.replacement_pair_for(:cellar)
mach_o_files.each do |file| mach_o_files.each do |file|
file.ensure_writable do file.ensure_writable do
if file.dylib? if file.dylib?
id = dylib_id_for(file).sub(relocation.old_prefix, relocation.new_prefix) id = dylib_id_for(file).sub(old_prefix, new_prefix)
change_dylib_id(id, file) change_dylib_id(id, file)
end end
each_install_name_for(file) do |old_name| each_install_name_for(file) do |old_name|
if old_name.start_with? relocation.old_cellar if old_name.start_with? old_cellar
new_name = old_name.sub(relocation.old_cellar, relocation.new_cellar) new_name = old_name.sub(old_cellar, new_cellar)
elsif old_name.start_with? relocation.old_prefix elsif old_name.start_with? old_prefix
new_name = old_name.sub(relocation.old_prefix, relocation.new_prefix) new_name = old_name.sub(old_prefix, new_prefix)
end end
change_install_name(old_name, new_name, file) if new_name change_install_name(old_name, new_name, file) if new_name
@ -38,10 +41,10 @@ class Keg
if ENV["HOMEBREW_RELOCATE_RPATHS"] if ENV["HOMEBREW_RELOCATE_RPATHS"]
each_rpath_for(file) do |old_name| each_rpath_for(file) do |old_name|
new_name = if old_name.start_with? relocation.old_cellar new_name = if old_name.start_with? old_cellar
old_name.sub(relocation.old_cellar, relocation.new_cellar) old_name.sub(old_cellar, new_cellar)
elsif old_name.start_with? relocation.old_prefix elsif old_name.start_with? old_prefix
old_name.sub(relocation.old_prefix, relocation.new_prefix) old_name.sub(old_prefix, new_prefix)
end end
change_rpath(old_name, new_name, file) if new_name change_rpath(old_name, new_name, file) if new_name
@ -172,6 +175,25 @@ class Keg
mach_o_files mach_o_files
end end
def prepare_relocation_to_locations
relocation = generic_prepare_relocation_to_locations
brewed_perl = runtime_dependencies&.any? { |dep| dep["full_name"] == "perl" && dep["declared_directly"] }
perl_path = if brewed_perl
"#{HOMEBREW_PREFIX}/opt/perl/bin/perl"
else
perl_version = if tab["built_on"].present?
tab["built_on"]["preferred_perl"]
else
MacOS.preferred_perl_version
end
"/usr/bin/perl#{perl_version}"
end
relocation.add_replacement_pair(:perl, PERL_PLACEHOLDER, perl_path)
relocation
end
def recursive_fgrep_args def recursive_fgrep_args
# Don't recurse into symlinks; the man page says this is the default, but # Don't recurse into symlinks; the man page says this is the default, but
# it's wrong. -O is a BSD-grep-only option. # it's wrong. -O is a BSD-grep-only option.

View File

@ -6,12 +6,44 @@ class Keg
CELLAR_PLACEHOLDER = "@@HOMEBREW_CELLAR@@" CELLAR_PLACEHOLDER = "@@HOMEBREW_CELLAR@@"
REPOSITORY_PLACEHOLDER = "@@HOMEBREW_REPOSITORY@@" REPOSITORY_PLACEHOLDER = "@@HOMEBREW_REPOSITORY@@"
LIBRARY_PLACEHOLDER = "@@HOMEBREW_LIBRARY@@" LIBRARY_PLACEHOLDER = "@@HOMEBREW_LIBRARY@@"
PERL_PLACEHOLDER = "@@HOMEBREW_PERL@@"
Relocation = Struct.new(:old_prefix, :old_cellar, :old_repository, :old_library, class Relocation
:new_prefix, :new_cellar, :new_repository, :new_library) do extend T::Sig
# Use keyword args instead of positional args for initialization.
def initialize(**kwargs) def initialize
super(*members.map { |k| kwargs[k] }) @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
end end
@ -36,32 +68,42 @@ class Keg
[] []
end end
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}")
relocation
end
alias generic_prepare_relocation_to_placeholders prepare_relocation_to_placeholders
def replace_locations_with_placeholders def replace_locations_with_placeholders
relocation = Relocation.new( relocation = prepare_relocation_to_placeholders.freeze
old_prefix: HOMEBREW_PREFIX.to_s,
old_cellar: HOMEBREW_CELLAR.to_s,
new_prefix: PREFIX_PLACEHOLDER,
new_cellar: CELLAR_PLACEHOLDER,
old_repository: HOMEBREW_REPOSITORY.to_s,
new_repository: REPOSITORY_PLACEHOLDER,
old_library: HOMEBREW_LIBRARY.to_s,
new_library: LIBRARY_PLACEHOLDER,
)
relocate_dynamic_linkage(relocation) relocate_dynamic_linkage(relocation)
replace_text_in_files(relocation) replace_text_in_files(relocation)
end end
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) def replace_placeholders_with_locations(files, skip_linkage: false)
relocation = Relocation.new( relocation = prepare_relocation_to_locations.freeze
old_prefix: PREFIX_PLACEHOLDER,
old_cellar: CELLAR_PLACEHOLDER,
old_repository: REPOSITORY_PLACEHOLDER,
old_library: LIBRARY_PLACEHOLDER,
new_prefix: HOMEBREW_PREFIX.to_s,
new_cellar: HOMEBREW_CELLAR.to_s,
new_repository: HOMEBREW_REPOSITORY.to_s,
new_library: HOMEBREW_LIBRARY.to_s,
)
relocate_dynamic_linkage(relocation) unless skip_linkage relocate_dynamic_linkage(relocation) unless skip_linkage
replace_text_in_files(relocation, files: files) replace_text_in_files(relocation, files: files)
end end
@ -73,16 +115,7 @@ class Keg
files.map(&path.method(:join)).group_by { |f| f.stat.ino }.each_value do |first, *rest| files.map(&path.method(:join)).group_by { |f| f.stat.ino }.each_value do |first, *rest|
s = first.open("rb", &:read) s = first.open("rb", &:read)
replacements = { next unless relocation.replace_text(s)
relocation.old_prefix => relocation.new_prefix,
relocation.old_cellar => relocation.new_cellar,
relocation.old_library => relocation.new_library,
}.compact
# when HOMEBREW_PREFIX == HOMEBREW_REPOSITORY we should use HOMEBREW_PREFIX for all relocations to avoid
# being unable to differentiate between them.
replacements[relocation.old_repository] = relocation.new_repository if HOMEBREW_PREFIX != HOMEBREW_REPOSITORY
changed = s.gsub!(Regexp.union(replacements.keys.sort_by(&:length).reverse), replacements)
next unless changed
changed_files += [first, *rest].map { |file| file.relative_path_from(path) } changed_files += [first, *rest].map { |file| file.relative_path_from(path) }