aboutsummaryrefslogtreecommitdiffstats
path: root/Library
diff options
context:
space:
mode:
authorMike McQuaid2016-07-09 13:52:05 +0100
committerGitHub2016-07-09 13:52:05 +0100
commitf1c414188540206370ebc85bf3b8c66c7c410e2e (patch)
tree01c0a116de4ba393c19e2f2e805969a87e496de6 /Library
parentdf7e36b86cf4fab3cf24a6d0888121d94ca73b8a (diff)
downloadbrew-f1c414188540206370ebc85bf3b8c66c7c410e2e.tar.bz2
keg_relocate: port to generic OS. (#453)
Diffstat (limited to 'Library')
-rw-r--r--Library/Homebrew/cmd/bottle.rb17
-rw-r--r--Library/Homebrew/extend/os/keg_relocate.rb5
-rw-r--r--Library/Homebrew/extend/os/mac/keg_relocate.rb135
-rw-r--r--Library/Homebrew/formula_installer.rb10
-rw-r--r--Library/Homebrew/keg_relocate.rb127
5 files changed, 161 insertions, 133 deletions
diff --git a/Library/Homebrew/cmd/bottle.rb b/Library/Homebrew/cmd/bottle.rb
index 1975d7ccf..50ce94709 100644
--- a/Library/Homebrew/cmd/bottle.rb
+++ b/Library/Homebrew/cmd/bottle.rb
@@ -57,15 +57,8 @@ module Homebrew
# skip document file.
next if Metafiles::EXTENSIONS.include? file.extname
- # Check dynamic library linkage. Importantly, do not run otool on static
- # libraries, which will falsely report "linkage" to themselves.
- if file.mach_o_executable? || file.dylib? || file.mach_o_bundle?
- linked_libraries = file.dynamically_linked_libraries
- linked_libraries = linked_libraries.select { |lib| lib.include? string }
- result ||= linked_libraries.any?
- else
- linked_libraries = []
- end
+ linked_libraries = Keg.file_linked_libraries(file, string)
+ result ||= linked_libraries.any?
if ARGV.verbose?
print_filename(string, file) if linked_libraries.any?
@@ -195,9 +188,9 @@ module Homebrew
begin
unless ARGV.include? "--skip-relocation"
- keg.relocate_install_names prefix, Keg::PREFIX_PLACEHOLDER,
+ keg.relocate_dynamic_linkage prefix, Keg::PREFIX_PLACEHOLDER,
cellar, Keg::CELLAR_PLACEHOLDER
- keg.relocate_text_files prefix, Keg::PREFIX_PLACEHOLDER,
+ keg.relocate_dynamic_files prefix, Keg::PREFIX_PLACEHOLDER,
cellar, Keg::CELLAR_PLACEHOLDER
end
@@ -264,7 +257,7 @@ module Homebrew
ignore_interrupts do
original_tab.write if original_tab
unless ARGV.include? "--skip-relocation"
- keg.relocate_install_names Keg::PREFIX_PLACEHOLDER, prefix,
+ keg.relocate_dynamic_linkage Keg::PREFIX_PLACEHOLDER, prefix,
Keg::CELLAR_PLACEHOLDER, cellar
keg.relocate_text_files Keg::PREFIX_PLACEHOLDER, prefix,
Keg::CELLAR_PLACEHOLDER, cellar
diff --git a/Library/Homebrew/extend/os/keg_relocate.rb b/Library/Homebrew/extend/os/keg_relocate.rb
new file mode 100644
index 000000000..2fde45d93
--- /dev/null
+++ b/Library/Homebrew/extend/os/keg_relocate.rb
@@ -0,0 +1,5 @@
+require "keg_relocate"
+
+if OS.mac?
+ require "extend/os/mac/keg_relocate"
+end
diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb
new file mode 100644
index 000000000..80f687516
--- /dev/null
+++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb
@@ -0,0 +1,135 @@
+class Keg
+ 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)
+
+ new_name = fixed_name(file, bad_name)
+ change_install_name(bad_name, new_name, file) unless new_name == bad_name
+ end
+ end
+ end
+
+ generic_fix_dynamic_linkage
+ end
+
+ def relocate_dynamic_linkage(old_prefix, new_prefix, old_cellar, new_cellar)
+ mach_o_files.each do |file|
+ file.ensure_writable do
+ if file.dylib?
+ id = dylib_id_for(file).sub(old_prefix, new_prefix)
+ change_dylib_id(id, file)
+ end
+
+ each_install_name_for(file) do |old_name|
+ if old_name.start_with? old_cellar
+ new_name = old_name.sub(old_cellar, new_cellar)
+ elsif old_name.start_with? old_prefix
+ new_name = old_name.sub(old_prefix, new_prefix)
+ end
+
+ change_install_name(old_name, new_name, file) if new_name
+ 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
+ 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
+
+ # 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
+ bad_name.sub(PREFIX_PLACEHOLDER, HOMEBREW_PREFIX.to_s)
+ elsif bad_name.start_with? CELLAR_PLACEHOLDER
+ bad_name.sub(CELLAR_PLACEHOLDER, HOMEBREW_CELLAR.to_s)
+ elsif (file.dylib? || file.mach_o_bundle?) && (file.parent + bad_name).exist?
+ "@loader_path/#{bad_name}"
+ elsif file.mach_o_executable? && (lib + bad_name).exist?
+ "#{lib}/#{bad_name}"
+ 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
+ dylibs.reject! { |fn| fn =~ /^@(loader_|executable_|r)path/ }
+ 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)
+ opt_record.join(relative_dirname, basename).to_s
+ end
+
+ # Matches framework references like `XXX.framework/Versions/YYY/XXX` and
+ # `XXX.framework/XXX`, both with or without a slash-delimited prefix.
+ FRAMEWORK_RX = %r{(?:^|/)(([^/]+)\.framework/(?:Versions/[^/]+/)?\2)$}
+
+ 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?
+ 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]
+ mach_o_files << pn
+ end
+
+ mach_o_files
+ end
+
+ def self.file_linked_libraries(file, string)
+ # Check dynamic library linkage. Importantly, do not run otool on static
+ # libraries, which will falsely report "linkage" to themselves.
+ if file.mach_o_executable? || file.dylib? || file.mach_o_bund
+ file.dynamically_linked_libraries.select { |lib| lib.include? string }
+ else
+ []
+ end
+ end
+end
diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb
index e44c78087..375a529bb 100644
--- a/Library/Homebrew/formula_installer.rb
+++ b/Library/Homebrew/formula_installer.rb
@@ -470,7 +470,7 @@ class FormulaInstaller
link(keg)
unless @poured_bottle && formula.bottle_specification.skip_relocation?
- fix_install_names(keg)
+ fix_dynamic_linkage(keg)
end
if formula.post_install_defined?
@@ -687,10 +687,10 @@ class FormulaInstaller
Homebrew.failed = true
end
- def fix_install_names(keg)
- keg.fix_install_names
+ def fix_dynamic_linkage(keg)
+ keg.fix_dynamic_linkage
rescue Exception => e
- onoe "Failed to fix install names"
+ onoe "Failed to fix install linkage"
puts "The formula built, but you may encounter issues using it or linking other"
puts "formula against it."
ohai e, e.backtrace if debug?
@@ -736,7 +736,7 @@ class FormulaInstaller
keg = Keg.new(formula.prefix)
unless formula.bottle_specification.skip_relocation?
- keg.relocate_install_names Keg::PREFIX_PLACEHOLDER, HOMEBREW_PREFIX.to_s,
+ keg.relocate_dynamic_linkage Keg::PREFIX_PLACEHOLDER, HOMEBREW_PREFIX.to_s,
Keg::CELLAR_PLACEHOLDER, HOMEBREW_CELLAR.to_s
end
keg.relocate_text_files Keg::PREFIX_PLACEHOLDER, HOMEBREW_PREFIX.to_s,
diff --git a/Library/Homebrew/keg_relocate.rb b/Library/Homebrew/keg_relocate.rb
index be41841f1..317320168 100644
--- a/Library/Homebrew/keg_relocate.rb
+++ b/Library/Homebrew/keg_relocate.rb
@@ -2,21 +2,7 @@ class Keg
PREFIX_PLACEHOLDER = "@@HOMEBREW_PREFIX@@".freeze
CELLAR_PLACEHOLDER = "@@HOMEBREW_CELLAR@@".freeze
- def fix_install_names
- 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)
-
- new_name = fixed_name(file, bad_name)
- change_install_name(bad_name, new_name, file) unless new_name == bad_name
- end
- end
- end
-
+ def fix_dynamic_linkage
symlink_files.each do |file|
link = file.readlink
# Don't fix relative symlinks
@@ -26,26 +12,10 @@ class Keg
end
end
end
+ alias generic_fix_dynamic_linkage fix_dynamic_linkage
- def relocate_install_names(old_prefix, new_prefix, old_cellar, new_cellar)
- mach_o_files.each do |file|
- file.ensure_writable do
- if file.dylib?
- id = dylib_id_for(file).sub(old_prefix, new_prefix)
- change_dylib_id(id, file)
- end
-
- each_install_name_for(file) do |old_name|
- if old_name.start_with? old_cellar
- new_name = old_name.sub(old_cellar, new_cellar)
- elsif old_name.start_with? old_prefix
- new_name = old_name.sub(old_prefix, new_prefix)
- end
-
- change_install_name(old_name, new_name, file) if new_name
- end
- end
- end
+ def relocate_dynamic_linkage(old_prefix, new_prefix, old_cellar, new_cellar)
+ []
end
def relocate_text_files(old_prefix, new_prefix, old_cellar, new_cellar)
@@ -70,22 +40,8 @@ class Keg
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
- 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
def each_unique_file_matching(string)
@@ -100,77 +56,10 @@ class Keg
end
end
- # 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
- bad_name.sub(PREFIX_PLACEHOLDER, HOMEBREW_PREFIX.to_s)
- elsif bad_name.start_with? CELLAR_PLACEHOLDER
- bad_name.sub(CELLAR_PLACEHOLDER, HOMEBREW_CELLAR.to_s)
- elsif (file.dylib? || file.mach_o_bundle?) && (file.parent + bad_name).exist?
- "@loader_path/#{bad_name}"
- elsif file.mach_o_executable? && (lib + bad_name).exist?
- "#{lib}/#{bad_name}"
- 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 lib
path.join("lib")
end
- def each_install_name_for(file, &block)
- dylibs = file.dynamically_linked_libraries
- dylibs.reject! { |fn| fn =~ /^@(loader_|executable_|r)path/ }
- 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)
- opt_record.join(relative_dirname, basename).to_s
- end
-
- # Matches framework references like `XXX.framework/Versions/YYY/XXX` and
- # `XXX.framework/XXX`, both with or without a slash-delimited prefix.
- FRAMEWORK_RX = %r{(?:^|/)(([^/]+)\.framework/(?:Versions/[^/]+/)?\2)$}.freeze
-
- 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?
- 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]
- mach_o_files << pn
- end
-
- mach_o_files
- end
-
def text_files
text_files = []
path.find do |pn|
@@ -203,4 +92,10 @@ class Keg
symlink_files
end
+
+ def self.file_linked_libraries(file, string)
+ []
+ end
end
+
+require "extend/os/keg_relocate"