diff --git a/Library/Homebrew/cmd/link.rb b/Library/Homebrew/cmd/link.rb index a85690ecb6..6371c41de6 100644 --- a/Library/Homebrew/cmd/link.rb +++ b/Library/Homebrew/cmd/link.rb @@ -35,8 +35,10 @@ module Homebrew extend self next end - print "Linking #{keg}... " do - puts "#{keg.link(mode)} symlinks created" + keg.lock do + print "Linking #{keg}... " do + puts "#{keg.link(mode)} symlinks created" + end end end end diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index d719be96a2..19ad6eeb5d 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -7,10 +7,12 @@ module Homebrew extend self if not ARGV.force? ARGV.kegs.each do |keg| - puts "Uninstalling #{keg}..." - keg.unlink - keg.uninstall - rm_opt_link keg.fname + keg.lock do + puts "Uninstalling #{keg}..." + keg.unlink + keg.uninstall + rm_opt_link keg.fname + end end else ARGV.named.each do |name| diff --git a/Library/Homebrew/cmd/unlink.rb b/Library/Homebrew/cmd/unlink.rb index 12b0377815..37ef8f1da2 100644 --- a/Library/Homebrew/cmd/unlink.rb +++ b/Library/Homebrew/cmd/unlink.rb @@ -3,8 +3,10 @@ module Homebrew extend self raise KegUnspecifiedError if ARGV.named.empty? ARGV.kegs.each do |keg| - print "Unlinking #{keg}... " - puts "#{keg.unlink} links removed" + keg.lock do + print "Unlinking #{keg}... " + puts "#{keg.unlink} links removed" + end end end end diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index ae2dd20c41..edd5cf1d1f 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -45,6 +45,18 @@ class FormulaUnavailableError < RuntimeError end end +class OperationInProgressError < RuntimeError + def initialize name + message = <<-EOS.undent + Operation already in progress for #{name} + Another active Homebrew process is already using #{name}. + Please wait for it to finish or terminate it to continue. + EOS + + super message + end +end + module Homebrew class InstallationError < RuntimeError attr :formula diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 2e072afff9..e2fde2ef92 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -228,6 +228,21 @@ class Formula end end + def lock + lockpath = HOMEBREW_CACHE_FORMULA/"#{@name}.brewing" + @lockfile = lockpath.open(File::RDWR | File::CREAT) + unless @lockfile.flock(File::LOCK_EX | File::LOCK_NB) + raise OperationInProgressError, @name + end + end + + def unlock + unless @lockfile.nil? + @lockfile.flock(File::LOCK_UN) + @lockfile.close + end + end + def == b name == b.name end diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 373440ba88..b24641e558 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -22,6 +22,7 @@ class FormulaInstaller @@attempted ||= Set.new + lock check_install_sanity end @@ -226,6 +227,8 @@ class FormulaInstaller print "#{f.prefix}: #{f.prefix.abv}" print ", built in #{pretty_duration build_time}" if build_time puts + + unlock if hold_locks? end def build_time @@ -463,6 +466,29 @@ class FormulaInstaller check_jars check_non_libraries end + + private + + def hold_locks? + @hold_locks || false + end + + def lock + if (@@locked ||= []).empty? + f.recursive_deps.each { |d| @@locked << d } unless ignore_deps + @@locked.unshift(f) + @@locked.each(&:lock) + @hold_locks = true + end + end + + def unlock + if hold_locks? + @@locked.each(&:unlock) + @@locked.clear + @hold_locks = false + end + end end diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index 3a4d012622..4949d24dc3 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -61,6 +61,18 @@ class Keg < Pathname parent.basename.to_s end + def lock + path = HOMEBREW_CACHE_FORMULA/"#{fname}.brewing" + file = path.open(File::RDWR | File::CREAT) + unless file.flock(File::LOCK_EX | File::LOCK_NB) + raise OperationInProgressError, fname + end + yield + ensure + file.flock(File::LOCK_UN) + file.close + end + def linked_keg_record @linked_keg_record ||= HOMEBREW_REPOSITORY/"Library/LinkedKegs"/fname end