require "formula" require "keg" require "tab" require "tap_migrations" 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) if tap.nil? super <<-EOS.undent #{formula.name} from #{formula.tap} is given, but old name #{formula.oldname} wasn't installed from taps or core formulae You can try `brew migrate --force #{formula.oldname}`. EOS else user, repo = tap.split("/") repo.sub!("homebrew-", "") name = "fully-qualified #{user}/#{repo}/#{formula.oldname}" name = formula.oldname if tap == "Homebrew/homebrew" super <<-EOS.undent #{formula.name} from #{formula.tap} is given, but old name #{formula.oldname} was installed from #{tap} Please try to use #{name} to refer the formula EOS end end end attr_reader :formula attr_reader :oldname, :oldpath, :old_pin_record, :old_opt_record attr_reader :old_linked_keg_record, :oldkeg, :old_tabs, :old_tap attr_reader :newname, :newpath, :new_pin_record attr_reader :old_pin_link_record # Path to new linked keg attr_reader :new_keg def initialize(formula) @oldname = formula.oldname @newname = formula.name raise MigratorNoOldnameError.new(formula) unless oldname @formula = formula @oldpath = HOMEBREW_CELLAR/formula.oldname raise MigratorNoOldpathError.new(formula) unless oldpath.exist? @old_tabs = oldpath.subdirs.each.map { |d| Tab.for_keg(Keg.new(d)) } @old_tap = old_tabs.first.tap raise MigratorDifferentTapsError.new(formula, old_tap) unless from_same_taps? @newpath = HOMEBREW_CELLAR/formula.name if @oldkeg = get_linked_oldkeg @old_linked_keg_record = oldkeg.linked_keg_record if oldkeg.linked? @old_opt_record = oldkeg.opt_record if oldkeg.optlinked? @new_keg = HOMEBREW_CELLAR/"#{newname}/#{File.basename(oldkeg)}" 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.source["tap"] = formula.tap tab.write end end def from_same_taps? if old_tap == nil && formula.core_formula? && ARGV.force? true elsif 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 (tap is Homebrew/homebrew if installed formula migrates to a 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 TAP_MIGRATIONS && (rec = TAP_MIGRATIONS[formula.oldname]) \ && rec == formula.tap.sub("homebrew-", "") && old_tap == "Homebrew/homebrew" fix_tabs true elsif formula.tap false end end def get_linked_oldkeg kegs = oldpath.subdirs.map { |d| Keg.new(d) } kegs.detect(&:linked?) || kegs.detect(&:optlinked?) end def pinned? @pinned end def oldkeg_linked? !!oldkeg end def migrate if newpath.exist? onoe "#{newpath} 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}" unlink_oldname move_to_new_directory repin link_newname if oldkeg_linked? link_oldname_opt link_oldname_cellar 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 } end end # move everything from Cellar/oldname to Cellar/newname def move_to_new_directory puts "Moving to: #{newpath}" FileUtils.mv(oldpath, newpath) end def repin if pinned? # old_pin_record is a relative symlink and when we try to to read it # from