require "formula" require "formula_lock" require "keg" require "tab" class Migrator class MigrationNeededError < RuntimeError def initialize(formula) super <<-EOS.undent #{formula.oldname} was renamed to #{formula.name} and needs to be migrated. Please run `brew migrate #{formula.oldname}` EOS end end class MigratorNoOldnameError < RuntimeError def initialize(formula) super "#{formula.name} doesn't replace any formula." end end class MigratorNoOldpathError < RuntimeError def initialize(formula) super "#{HOMEBREW_CELLAR/formula.oldname} doesn't exist." end end class MigratorDifferentTapsError < RuntimeError def initialize(formula, tap) msg = if tap.core_tap? "Please try to use #{formula.oldname} to refer the formula.\n" elsif tap "Please try to use fully-qualified #{tap}/#{formula.oldname} to refer the formula.\n" end super <<-EOS.undent #{formula.name} from #{formula.tap} is given, but old name #{formula.oldname} was installed from #{tap ? tap : "path or url"}. #{msg}To force migrate use `brew migrate --force #{formula.oldname}`. EOS end end # instance of new name formula attr_reader :formula # old name of the formula attr_reader :oldname # path to oldname's cellar attr_reader :old_cellar # path to oldname pin attr_reader :old_pin_record # path to oldname opt attr_reader :old_opt_record # oldname linked keg attr_reader :old_linked_keg # path to oldname's linked keg attr_reader :old_linked_keg_record # tabs from oldname kegs attr_reader :old_tabs # tap of the old name attr_reader :old_tap # resolved path to oldname pin attr_reader :old_pin_link_record # new name of the formula attr_reader :newname # path to newname cellar according to new name attr_reader :new_cellar # path to newname pin attr_reader :new_pin_record # path to newname keg that will be linked if old_linked_keg isn't nil attr_reader :new_linked_keg_record def initialize(formula) @oldname = formula.oldname @newname = formula.name raise MigratorNoOldnameError.new(formula) unless oldname @formula = formula @old_cellar = HOMEBREW_CELLAR/formula.oldname raise MigratorNoOldpathError.new(formula) unless old_cellar.exist? @old_tabs = old_cellar.subdirs.map { |d| Tab.for_keg(Keg.new(d)) } @old_tap = old_tabs.first.tap if !ARGV.force? && !from_same_taps? raise MigratorDifferentTapsError.new(formula, old_tap) end @new_cellar = HOMEBREW_CELLAR/formula.name if @old_linked_keg = get_linked_old_linked_keg @old_linked_keg_record = old_linked_keg.linked_keg_record if old_linked_keg.linked? @old_opt_record = old_linked_keg.opt_record if old_linked_keg.optlinked? @new_linked_keg_record = HOMEBREW_CELLAR/"#{newname}/#{File.basename(old_linked_keg)}" end @old_pin_record = HOMEBREW_LIBRARY/"PinnedKegs"/oldname @new_pin_record = HOMEBREW_LIBRARY/"PinnedKegs"/newname @pinned = old_pin_record.symlink? @old_pin_link_record = old_pin_record.readlink if @pinned end # Fix INSTALL_RECEIPTS for tap-migrated formula. def fix_tabs old_tabs.each do |tab| tab.tap = formula.tap tab.write end end def from_same_taps? if formula.tap == old_tap true # Homebrew didn't use to update tabs while performing tap-migrations, # so there can be INSTALL_RECEIPT's containing wrong information about tap, # so we check if there is an entry about oldname migrated to tap and if # newname's tap is the same as tap to which oldname migrated, then we # can perform migrations and the taps for oldname and newname are the same. elsif formula.tap && old_tap && formula.tap == old_tap.tap_migrations[formula.oldname] fix_tabs true else false end end def get_linked_old_linked_keg kegs = old_cellar.subdirs.map { |d| Keg.new(d) } kegs.detect(&:linked?) || kegs.detect(&:optlinked?) end def pinned? @pinned end def migrate if new_cellar.exist? onoe "#{new_cellar} already exists; remove it manually and run brew migrate #{oldname}." return end begin oh1 "Migrating #{Tty.green}#{oldname}#{Tty.white} to #{Tty.green}#{newname}#{Tty.reset}" lock unlink_oldname move_to_new_directory repin link_oldname_cellar link_oldname_opt link_newname unless old_linked_keg.nil? update_tabs rescue Interrupt ignore_interrupts { backup_oldname } rescue Exception => e onoe "Error occured while migrating." puts e puts e.backtrace if ARGV.debug? puts "Backuping..." ignore_interrupts { backup_oldname } ensure unlock end end # move everything from Cellar/oldname to Cellar/newname def move_to_new_directory puts "Moving to: #{new_cellar}" FileUtils.mv(old_cellar, new_cellar) end def repin if pinned? # old_pin_record is a relative symlink and when we try to to read it # from