From f02d81ecbf636aea7bcb842ef6350afe947a4839 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Fri, 10 Aug 2012 16:33:22 -0400 Subject: [PATCH] Create active symlinks for installed formula Similar to the LinkedKegs record, we write a symlink for installed kegs to PREFIX/opt. Unlike the linked-keg record, unlinking doesn't remove the link, only uninstalling, and keg-only formula have a record too. The reason for this addition is so that formula that depend on keg-only formula can build against the opt directory and not the cellar keg. Thus surviving upgrades. To enforce this fix_install_names and built were adapted to use the opt path. Standard kegs also create an opt symlink so that caveats can now refer to the opt directory and thus provide steps that survive upgrades too. Thus the choice of /opt. It is short, neat and the right choice: POSIX dictates that opt is for stand-alone prefixes of software. --- Library/Homebrew/build.rb | 14 +++++++++----- Library/Homebrew/cmd/uninstall.rb | 9 +++++++++ Library/Homebrew/formula.rb | 2 ++ Library/Homebrew/formula_installer.rb | 12 ++++++++++-- Library/Homebrew/keg.rb | 20 +++++++++++++++++++- Library/Homebrew/keg_fix_install_names.rb | 9 +++++++-- 6 files changed, 56 insertions(+), 10 deletions(-) diff --git a/Library/Homebrew/build.rb b/Library/Homebrew/build.rb index 34c257dc84..dd3e024427 100755 --- a/Library/Homebrew/build.rb +++ b/Library/Homebrew/build.rb @@ -61,14 +61,18 @@ def install f f.recursive_deps.uniq.each do |dep| dep = Formula.factory dep if dep.keg_only? - ENV.prepend 'LDFLAGS', "-L#{dep.lib}" - ENV.prepend 'CPPFLAGS', "-I#{dep.include}" - ENV.prepend 'PATH', "#{dep.bin}", ':' + opt = HOMEBREW_PREFIX/:opt/dep.name - pcdir = dep.lib/'pkgconfig' + raise "#{opt} not present\nReinstall #{dep}." unless opt.directory? + + ENV.prepend 'LDFLAGS', "-L#{opt}/lib" + ENV.prepend 'CPPFLAGS', "-I#{opt}/include" + ENV.prepend 'PATH', "#{opt}/bin", ':' + + pcdir = opt/'lib/pkgconfig' ENV.prepend 'PKG_CONFIG_PATH', pcdir, ':' if pcdir.directory? - acdir = dep.share/'aclocal' + acdir = opt/'share/aclocal' ENV.prepend 'ACLOCAL_PATH', acdir, ':' if acdir.directory? end end diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index cc13a18c82..d719be96a2 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -10,6 +10,7 @@ module Homebrew extend self puts "Uninstalling #{keg}..." keg.unlink keg.uninstall + rm_opt_link keg.fname end else ARGV.named.each do |name| @@ -30,10 +31,18 @@ module Homebrew extend self end rack.rmtree end + + rm_opt_link name end end rescue MultipleVersionsInstalledError => e ofail e puts "Use `brew remove --force #{e.name}` to remove all versions." end + + def rm_opt_link name + optlink = HOMEBREW_PREFIX/:opt/name + optlink.unlink if optlink.symlink? + end + end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index cb6b44eb81..78209feb75 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -148,6 +148,8 @@ class Formula self.class.build end + def opt_prefix; HOMEBREW_PREFIX/:opt/name end + # Use the @active_spec to detect the download strategy. # Can be overriden to force a custom download strategy def download_strategy diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 08ddd192f0..1910348dbd 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -172,10 +172,18 @@ class FormulaInstaller def finish ohai 'Finishing up' if ARGV.verbose? - unless f.keg_only? + if f.keg_only? + begin + Keg.new(f.prefix).optlink + rescue Exception => e + onoe "Failed to create: #{f.opt_prefix}" + puts "Things that depend on #{f} will probably not build." + end + else link - check_PATH + check_PATH unless f.keg_only? end + fix_install_names ohai "Summary" if ARGV.verbose? or show_summary_heading diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index d7ad80aee2..3bae926057 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -42,7 +42,7 @@ class Keg < Pathname Find.prune if src.directory? end end - linked_keg_record.unlink if linked_keg_record.exist? + linked_keg_record.unlink if linked_keg_record.symlink? n end @@ -126,7 +126,25 @@ class Keg < Pathname linked_keg_record.make_relative_symlink(self) unless mode == :dryrun + optlink unless mode == :dryrun + return $n + $d + rescue Exception + opoo "Could not link #{fname}. Unlinking..." + unlink + raise + end + + def optlink + from = HOMEBREW_PREFIX/:opt/fname + if from.symlink? + from.delete + elsif from.directory? + from.rmdir + elsif from.exist? + from.delete + end + from.make_relative_symlink(self) end protected diff --git a/Library/Homebrew/keg_fix_install_names.rb b/Library/Homebrew/keg_fix_install_names.rb index 7851fec263..75e1def22e 100644 --- a/Library/Homebrew/keg_fix_install_names.rb +++ b/Library/Homebrew/keg_fix_install_names.rb @@ -56,8 +56,13 @@ class Keg end # the shortpath ensures that library upgrades don’t break installed tools - shortpath = HOMEBREW_PREFIX + Pathname.new(file).relative_path_from(self) - id = if shortpath.exist? then shortpath else file end + relative_path = Pathname.new(file).relative_path_from(self) + shortpath = HOMEBREW_PREFIX.join(relative_path) + id = if shortpath.exist? + shortpath + else + "#{HOMEBREW_PREFIX}/opt/#{fname}/#{relative_path}" + end yield id, install_names end