2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-07-09 13:52:05 +01:00
|
|
|
class Keg
|
2018-04-07 20:28:56 +01:00
|
|
|
class << self
|
|
|
|
undef file_linked_libraries
|
|
|
|
|
|
|
|
def file_linked_libraries(file, string)
|
|
|
|
# Check dynamic library linkage. Importantly, do not perform for static
|
|
|
|
# libraries, which will falsely report "linkage" to themselves.
|
|
|
|
if file.mach_o_executable? || file.dylib? || file.mach_o_bundle?
|
|
|
|
file.dynamically_linked_libraries.select { |lib| lib.include? string }
|
|
|
|
else
|
|
|
|
[]
|
2016-07-09 13:52:05 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-04-07 20:28:56 +01:00
|
|
|
undef relocate_dynamic_linkage, detect_cxx_stdlibs
|
|
|
|
|
2016-10-23 23:48:07 -04:00
|
|
|
def relocate_dynamic_linkage(relocation)
|
2016-07-09 13:52:05 +01:00
|
|
|
mach_o_files.each do |file|
|
|
|
|
file.ensure_writable do
|
|
|
|
if file.dylib?
|
2016-10-23 23:48:07 -04:00
|
|
|
id = dylib_id_for(file).sub(relocation.old_prefix, relocation.new_prefix)
|
2017-09-27 02:08:23 -07:00
|
|
|
change_dylib_id(id, file)
|
2016-07-09 13:52:05 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
each_install_name_for(file) do |old_name|
|
2016-10-23 23:48:07 -04:00
|
|
|
if old_name.start_with? relocation.old_cellar
|
|
|
|
new_name = old_name.sub(relocation.old_cellar, relocation.new_cellar)
|
|
|
|
elsif old_name.start_with? relocation.old_prefix
|
|
|
|
new_name = old_name.sub(relocation.old_prefix, relocation.new_prefix)
|
2016-07-09 13:52:05 +01:00
|
|
|
end
|
|
|
|
|
2017-09-27 02:08:23 -07:00
|
|
|
change_install_name(old_name, new_name, file) if new_name
|
2016-07-09 13:52:05 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detects the C++ dynamic libraries in place, scanning the dynamic links
|
|
|
|
# of the files within the keg.
|
|
|
|
# Note that this doesn't attempt to distinguish between libstdc++ versions,
|
|
|
|
# for instance between Apple libstdc++ and GNU libstdc++
|
|
|
|
def detect_cxx_stdlibs(options = {})
|
|
|
|
skip_executables = options.fetch(:skip_executables, false)
|
|
|
|
results = Set.new
|
|
|
|
|
|
|
|
mach_o_files.each do |file|
|
|
|
|
next if file.mach_o_executable? && skip_executables
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-07-09 13:52:05 +01:00
|
|
|
dylibs = file.dynamically_linked_libraries
|
|
|
|
results << :libcxx unless dylibs.grep(/libc\+\+.+\.dylib/).empty?
|
|
|
|
results << :libstdcxx unless dylibs.grep(/libstdc\+\+.+\.dylib/).empty?
|
|
|
|
end
|
|
|
|
|
|
|
|
results.to_a
|
|
|
|
end
|
|
|
|
|
2018-04-07 20:28:56 +01:00
|
|
|
def fix_dynamic_linkage
|
|
|
|
mach_o_files.each do |file|
|
|
|
|
file.ensure_writable do
|
|
|
|
change_dylib_id(dylib_id_for(file), file) if file.dylib?
|
|
|
|
|
|
|
|
each_install_name_for(file) do |bad_name|
|
|
|
|
# Don't fix absolute paths unless they are rooted in the build directory
|
|
|
|
next if bad_name.start_with?("/") &&
|
|
|
|
!bad_name.start_with?(HOMEBREW_TEMP.to_s) &&
|
|
|
|
!bad_name.start_with?(HOMEBREW_TEMP.realpath.to_s)
|
|
|
|
|
|
|
|
new_name = fixed_name(file, bad_name)
|
|
|
|
change_install_name(bad_name, new_name, file) unless new_name == bad_name
|
|
|
|
end
|
2018-12-27 13:30:16 -05:00
|
|
|
|
|
|
|
# If none of the install names reference RPATH(s), then we can safely
|
|
|
|
# remove all RPATHs from the file.
|
2019-03-19 07:40:27 -07:00
|
|
|
if ENV["HOMEBREW_RELOCATE_METAVARS"] &&
|
|
|
|
file.dynamically_linked_libraries.none? { |lib| lib.start_with?("@rpath") }
|
2018-12-27 13:30:16 -05:00
|
|
|
# Note: This could probably be made more efficient by reverse-sorting
|
|
|
|
# the RPATHs by offset and calling MachOFile#delete_command
|
|
|
|
# with repopulate: false.
|
|
|
|
file.rpaths.each { |r| file.delete_rpath(r) }
|
|
|
|
end
|
2018-04-07 20:28:56 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
generic_fix_dynamic_linkage
|
|
|
|
end
|
|
|
|
|
2018-12-19 19:55:21 -05:00
|
|
|
def expand_rpath(file, bad_name)
|
|
|
|
suffix = bad_name.sub(/^@rpath/, "")
|
|
|
|
|
|
|
|
file.rpaths.each do |rpath|
|
2018-12-19 20:23:02 -05:00
|
|
|
return rpath/suffix if (rpath/suffix).exist?
|
2018-12-19 19:55:21 -05:00
|
|
|
end
|
|
|
|
|
2018-12-19 20:12:28 -05:00
|
|
|
opoo "Could not find library #{bad_name} for #{file}"
|
2018-12-19 19:55:21 -05:00
|
|
|
bad_name
|
|
|
|
end
|
|
|
|
|
2016-07-09 13:52:05 +01:00
|
|
|
# If file is a dylib or bundle itself, look for the dylib named by
|
|
|
|
# bad_name relative to the lib directory, so that we can skip the more
|
|
|
|
# expensive recursive search if possible.
|
|
|
|
def fixed_name(file, bad_name)
|
|
|
|
if bad_name.start_with? PREFIX_PLACEHOLDER
|
2017-04-27 20:17:06 +02:00
|
|
|
bad_name.sub(PREFIX_PLACEHOLDER, HOMEBREW_PREFIX)
|
2016-07-09 13:52:05 +01:00
|
|
|
elsif bad_name.start_with? CELLAR_PLACEHOLDER
|
2017-04-27 20:17:06 +02:00
|
|
|
bad_name.sub(CELLAR_PLACEHOLDER, HOMEBREW_CELLAR)
|
2016-07-09 13:52:05 +01:00
|
|
|
elsif (file.dylib? || file.mach_o_bundle?) && (file.parent + bad_name).exist?
|
2019-03-11 20:58:04 -04:00
|
|
|
"@loader_path/#{bad_name}"
|
2016-07-09 13:52:05 +01:00
|
|
|
elsif file.mach_o_executable? && (lib + bad_name).exist?
|
|
|
|
"#{lib}/#{bad_name}"
|
2019-03-10 21:29:59 -04:00
|
|
|
elsif bad_name.start_with?("@rpath") && ENV["HOMEBREW_RELOCATE_METAVARS"]
|
|
|
|
expand_rpath file, bad_name
|
2016-07-09 13:52:05 +01:00
|
|
|
elsif (abs_name = find_dylib(bad_name)) && abs_name.exist?
|
|
|
|
abs_name.to_s
|
|
|
|
else
|
|
|
|
opoo "Could not fix #{bad_name} in #{file}"
|
|
|
|
bad_name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def each_install_name_for(file, &block)
|
|
|
|
dylibs = file.dynamically_linked_libraries
|
2019-03-11 20:58:04 -04:00
|
|
|
dylibs.reject! { |fn| fn =~ /^@(loader|executable)_path/ }
|
2019-03-19 07:40:27 -07:00
|
|
|
dylibs.reject! { |fn| fn =~ /^@rpath/ } unless ENV["HOMEBREW_RELOCATE_METAVARS"]
|
2016-07-09 13:52:05 +01:00
|
|
|
dylibs.each(&block)
|
|
|
|
end
|
|
|
|
|
|
|
|
def dylib_id_for(file)
|
|
|
|
# The new dylib ID should have the same basename as the old dylib ID, not
|
|
|
|
# the basename of the file itself.
|
|
|
|
basename = File.basename(file.dylib_id)
|
|
|
|
relative_dirname = file.dirname.relative_path_from(path)
|
2017-06-01 16:06:51 +02:00
|
|
|
(opt_record/relative_dirname/basename).to_s
|
2016-07-09 13:52:05 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
# Matches framework references like `XXX.framework/Versions/YYY/XXX` and
|
|
|
|
# `XXX.framework/XXX`, both with or without a slash-delimited prefix.
|
2018-11-02 17:18:07 +00:00
|
|
|
FRAMEWORK_RX = %r{(?:^|/)(([^/]+)\.framework/(?:Versions/[^/]+/)?\2)$}.freeze
|
2016-07-09 13:52:05 +01:00
|
|
|
|
|
|
|
def find_dylib_suffix_from(bad_name)
|
|
|
|
if (framework = bad_name.match(FRAMEWORK_RX))
|
|
|
|
framework[1]
|
|
|
|
else
|
|
|
|
File.basename(bad_name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def find_dylib(bad_name)
|
|
|
|
return unless lib.directory?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-07-09 13:52:05 +01:00
|
|
|
suffix = "/#{find_dylib_suffix_from(bad_name)}"
|
|
|
|
lib.find { |pn| break pn if pn.to_s.end_with?(suffix) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def mach_o_files
|
|
|
|
hardlinks = Set.new
|
|
|
|
mach_o_files = []
|
|
|
|
path.find do |pn|
|
|
|
|
next if pn.symlink? || pn.directory?
|
|
|
|
next unless pn.dylib? || pn.mach_o_bundle? || pn.mach_o_executable?
|
|
|
|
# if we've already processed a file, ignore its hardlinks (which have the same dev ID and inode)
|
|
|
|
# this prevents relocations from being performed on a binary more than once
|
|
|
|
next unless hardlinks.add? [pn.stat.dev, pn.stat.ino]
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-07-09 13:52:05 +01:00
|
|
|
mach_o_files << pn
|
|
|
|
end
|
|
|
|
|
|
|
|
mach_o_files
|
|
|
|
end
|
|
|
|
|
2017-04-02 09:04:49 -07:00
|
|
|
def recursive_fgrep_args
|
|
|
|
# Don't recurse into symlinks; the man page says this is the default, but
|
|
|
|
# it's wrong. -O is a BSD-grep-only option.
|
|
|
|
"-lrO"
|
|
|
|
end
|
2016-07-09 13:52:05 +01:00
|
|
|
end
|