diff --git a/Library/Homebrew/cmd/bottle.rb b/Library/Homebrew/cmd/bottle.rb index cda1d776d9..6f703d4b35 100644 --- a/Library/Homebrew/cmd/bottle.rb +++ b/Library/Homebrew/cmd/bottle.rb @@ -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}" diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 9251fe487a..767db6e63c 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -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" diff --git a/Library/Homebrew/formula_support.rb b/Library/Homebrew/formula_support.rb index 4fa06c0e78..23472547ff 100644 --- a/Library/Homebrew/formula_support.rb +++ b/Library/Homebrew/formula_support.rb @@ -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 diff --git a/Library/Homebrew/keg_fix_install_names.rb b/Library/Homebrew/keg_fix_install_names.rb index dd647660a5..bdb98286f7 100644 --- a/Library/Homebrew/keg_fix_install_names.rb +++ b/Library/Homebrew/keg_fix_install_names.rb @@ -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 don’t break installed tools relative_path = Pathname.new(file).relative_path_from(self) diff --git a/Library/Homebrew/test/testball.rb b/Library/Homebrew/test/testball.rb index d3054810ae..f685f5e2c5 100644 --- a/Library/Homebrew/test/testball.rb +++ b/Library/Homebrew/test/testball.rb @@ -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