Relocate bottles using install_name_tool.

This has two parts:

1. Bottles are temporarily relocated on bottling and tested if that is
sufficient for them to contain no longer reference the prefix or
cellar. If so, they are marked as relocatable.
2. On installation if bottles are marked as relocatable they will be
relocated using install_name_tool to the current prefix and cellar.

Closes Homebrew/homebrew#18374.
This commit is contained in:
Mike McQuaid 2013-03-11 18:56:26 +00:00
parent 258d70028f
commit 0f9910d352
5 changed files with 98 additions and 19 deletions

View File

@ -1,8 +1,13 @@
require 'formula'
require 'bottles'
require 'tab'
require 'keg'
module Homebrew extend self
def keg_contains string, keg
quiet_system 'fgrep', '--recursive', '--quiet', '--max-count=1', string, keg
end
def bottle_formula f
unless f.installed?
return ofail "Formula not installed: #{f.name}"
@ -18,23 +23,48 @@ module Homebrew extend self
bottle_path = Pathname.pwd/filename
sha1 = nil
prefix = HOMEBREW_PREFIX.to_s
tmp_prefix = '/tmp'
cellar = HOMEBREW_CELLAR.to_s
tmp_cellar = '/tmp/Cellar'
HOMEBREW_CELLAR.cd do
ohai "Bottling #{f.name} #{f.version}..."
bottle_relocatable = !quiet_system(
'grep', '--recursive', '--quiet', '--max-count=1',
HOMEBREW_CELLAR, "#{f.name}/#{f.version}")
cellar = nil
if bottle_relocatable
cellar = ':any'
elsif HOMEBREW_CELLAR.to_s != '/usr/local/Cellar'
cellar = "'#{HOMEBREW_CELLAR}'"
end
# Use gzip, faster to compress than bzip2, faster to uncompress than bzip2
# or an uncompressed tarball (and more bandwidth friendly).
safe_system 'tar', 'czf', bottle_path, "#{f.name}/#{f.version}"
sha1 = bottle_path.sha1
relocatable = false
keg = Keg.new f.prefix
keg.lock do
# Relocate bottle library references before testing for built-in
# references to the Cellar e.g. Qt's QMake annoyingly does this.
keg.relocate_install_names prefix, tmp_prefix, cellar, tmp_cellar
relocatable = !keg_contains(HOMEBREW_PREFIX, keg)
relocatable = !keg_contains(HOMEBREW_CELLAR, keg) if relocatable
# And do the same thing in reverse to change the library references
# back to how they were.
keg.relocate_install_names tmp_prefix, prefix, tmp_cellar, cellar
end
prefix = cellar = nil
if relocatable
cellar = ':any'
else
if HOMEBREW_PREFIX.to_s != '/usr/local'
prefix = "'#{HOMEBREW_PREFIX}"
end
if HOMEBREW_CELLAR.to_s != '/usr/local/Cellar'
cellar = "'#{HOMEBREW_CELLAR}'"
end
end
puts "./#{filename}"
puts "bottle do"
puts " prefix #{prefix}" if prefix
puts " cellar #{cellar}" if cellar
puts " revision #{bottle_revision}" if bottle_revision > 0
puts " sha1 '#{sha1}' => :#{MacOS.cat}"

View File

@ -96,13 +96,13 @@ class FormulaInstaller
begin
if pour_bottle?
pour
poured_bottle = true
@poured_bottle = true
end
rescue
opoo "Bottle installation failed: building from source."
end
unless poured_bottle
unless @poured_bottle
build
clean
end
@ -353,6 +353,17 @@ class FormulaInstaller
def fix_install_names
Keg.new(f.prefix).fix_install_names
if @poured_bottle
old_prefix = f.bottle.prefix
new_prefix = HOMEBREW_PREFIX.to_s
old_cellar = f.bottle.cellar
new_cellar = HOMEBREW_CELLAR.to_s
if old_prefix != new_prefix or old_cellar != new_cellar
Keg.new(f.prefix).relocate_install_names \
old_prefix, new_prefix, old_cellar, new_cellar
end
end
rescue Exception => e
onoe "Failed to fix install names"
puts "The formula built, but you may encounter issues using it or linking other"

View File

@ -85,6 +85,7 @@ class Bottle < SoftwareSpec
def initialize
super
@revision = 0
@prefix = '/usr/local'
@cellar = '/usr/local/Cellar'
@cat_without_underscores = false
end
@ -117,6 +118,10 @@ class Bottle < SoftwareSpec
val.nil? ? @root_url : @root_url = val
end
def prefix val=nil
val.nil? ? @prefix : @prefix = val
end
def cellar val=nil
val.nil? ? @cellar : @cellar = val
end

View File

@ -2,7 +2,7 @@ class Keg
def fix_install_names
return unless MACOS
mach_o_files.each do |file|
bad_install_names_for file do |id, bad_names|
install_names_for file do |id, bad_names|
file.ensure_writable do
system MacOS.locate("install_name_tool"), "-id", id, file if file.dylib?
@ -31,13 +31,50 @@ class Keg
end
end
def relocate_install_names old_prefix, new_prefix, old_cellar, new_cellar
mach_o_files.each do |file|
install_names_for(file, relocate_reject_proc(old_prefix)) do |id, old_prefix_names|
file.ensure_writable do
new_prefix_id = id.to_s.gsub old_prefix, new_prefix
system MacOS.locate("install_name_tool"), "-id", new_prefix_id, file if file.dylib?
old_prefix_names.each do |old_prefix_name|
new_prefix_name = old_prefix_name.to_s.gsub old_prefix, new_prefix
system MacOS.locate("install_name_tool"), "-change", old_prefix_name, new_prefix_name, file
end
end
end
install_names_for(file, relocate_reject_proc(old_cellar)) do |id, old_cellar_names|
file.ensure_writable do
old_cellar_names.each do |old_cellar_name|
new_cellar_name = old_cellar_name.to_s.gsub old_cellar, new_cellar
system MacOS.locate("install_name_tool"), "-change", old_cellar_name, new_cellar_name, file
end
end
end
end
end
private
OTOOL_RX = /\t(.*) \(compatibility version (\d+\.)*\d+, current version (\d+\.)*\d+\)/
def lib; join 'lib' end
def bad_install_names_for file
def default_reject_proc
Proc.new do |fn|
# Don't fix absolute paths unless they are rooted in the build directory
tmp = ENV['HOMEBREW_TEMP'] ? Regexp.escape(ENV['HOMEBREW_TEMP']) : '/tmp'
fn[0,1] == '/' and not %r[^#{tmp}] === fn
end
end
def relocate_reject_proc(path)
Proc.new { |fn| not fn.start_with?(path) }
end
def install_names_for file, reject_proc=default_reject_proc
ENV['HOMEBREW_MACH_O_FILE'] = file.to_s # solves all shell escaping problems
install_names = `#{MacOS.locate("otool")} -L "$HOMEBREW_MACH_O_FILE"`.split "\n"
@ -49,12 +86,7 @@ class Keg
install_names.compact!
install_names.reject!{ |fn| fn =~ /^@(loader_|executable_|r)path/ }
# Don't fix absolute paths unless they are rooted in the build directory
install_names.reject! do |fn|
tmp = ENV['HOMEBREW_TEMP'] ? Regexp.escape(ENV['HOMEBREW_TEMP']) : '/tmp'
fn[0,1] == '/' and not %r[^#{tmp}] === fn
end
install_names.reject!{ |fn| reject_proc.call(fn) }
# the shortpath ensures that library upgrades dont break installed tools
relative_path = Pathname.new(file).relative_path_from(self)

View File

@ -221,6 +221,7 @@ class AllCatsBottleSpecTestBall < Formula
sha1 '482e737739d946b7c8cbaf127d9ee9c148b999f5'
bottle do
prefix '/private/tmp/testbrew/prefix'
cellar '/private/tmp/testbrew/cellar'
sha1 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' => :snow_leopard_32
sha1 'faceb00cfaceb00cfaceb00cfaceb00cfaceb00c' => :snow_leopard