aboutsummaryrefslogtreecommitdiffstats
path: root/Library
diff options
context:
space:
mode:
authorAlyssa Ross2016-09-15 16:01:18 +0100
committerAlyssa Ross2016-09-18 19:58:09 +0100
commit2a683f2569614850f79534a8547fd96cc52c7850 (patch)
treecd89a2f42b59d337cda690ed2550c28772b36344 /Library
parent3d559fa79641735193636cbf6240c082e6ca171c (diff)
downloadbrew-2a683f2569614850f79534a8547fd96cc52c7850.tar.bz2
upgrade, outdated: follow alias changes
Diffstat (limited to 'Library')
-rw-r--r--Library/Homebrew/cmd/outdated.rb21
-rw-r--r--Library/Homebrew/cmd/upgrade.rb28
-rw-r--r--Library/Homebrew/extend/ARGV.rb16
-rw-r--r--Library/Homebrew/formula.rb138
-rw-r--r--Library/Homebrew/formulary.rb39
-rw-r--r--Library/Homebrew/test/test_formulary.rb2
6 files changed, 188 insertions, 56 deletions
diff --git a/Library/Homebrew/cmd/outdated.rb b/Library/Homebrew/cmd/outdated.rb
index 7afa41df4..f9739cd7a 100644
--- a/Library/Homebrew/cmd/outdated.rb
+++ b/Library/Homebrew/cmd/outdated.rb
@@ -43,15 +43,26 @@ module Homebrew
outdated_formulae.each do |f|
if verbose
- outdated_versions = f.outdated_versions(fetch_head: fetch_head)
- current_version = if f.head? && outdated_versions.any? { |v| v.to_s == f.pkg_version.to_s }
+ outdated_kegs = f.outdated_kegs(fetch_head: fetch_head)
+
+ current_version = if f.alias_changed?
+ latest = f.latest_formula
+ "#{latest.name} (#{latest.pkg_version})"
+ elsif f.head? && outdated_kegs.any? { |k| k.version.to_s == f.pkg_version.to_s }
+ # There is a newer HEAD but the version number has not changed.
"latest HEAD"
else
f.pkg_version.to_s
end
- puts "#{f.full_name} (#{outdated_versions.join(", ")}) < #{current_version}"
+
+ outdated_versions = outdated_kegs.
+ group_by(&:name).
+ sort_by(&:first).
+ map { |name, kegs| "#{name} (#{kegs.map(&:version) * ", "})" } * ", "
+
+ puts "#{outdated_versions} < #{current_version}"
else
- puts f.full_name
+ puts f.full_installed_specified_name
end
end
end
@@ -62,7 +73,7 @@ module Homebrew
outdated_formulae = formulae.select { |f| f.outdated?(fetch_head: fetch_head) }
outdated = outdated_formulae.each do |f|
- outdated_versions = f.outdated_versions(fetch_head: fetch_head)
+ outdated_versions = f.outdated_kegs(fetch_head: fetch_head).map(&:version)
current_version = if f.head? && outdated_versions.any? { |v| v.to_s == f.pkg_version.to_s }
"HEAD"
else
diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb
index 6968fbda8..c56a73384 100644
--- a/Library/Homebrew/cmd/upgrade.rb
+++ b/Library/Homebrew/cmd/upgrade.rb
@@ -37,10 +37,10 @@ module Homebrew
(ARGV.resolved_formulae - outdated).each do |f|
versions = f.installed_kegs.map(&:version)
if versions.empty?
- onoe "#{f.full_name} not installed"
+ onoe "#{f.full_specified_name} not installed"
else
version = versions.max
- onoe "#{f.full_name} #{version} already installed"
+ onoe "#{f.full_specified_name} #{version} already installed"
end
end
exit 1 if outdated.empty?
@@ -51,19 +51,21 @@ module Homebrew
outdated -= pinned
end
- if outdated.empty?
+ formulae_to_install = outdated.map(&:latest_formula)
+
+ if formulae_to_install.empty?
oh1 "No packages to upgrade"
else
- oh1 "Upgrading #{outdated.length} outdated package#{plural(outdated.length)}, with result:"
- puts outdated.map { |f| "#{f.full_name} #{f.pkg_version}" } * ", "
+ oh1 "Upgrading #{formulae_to_install.length} outdated package#{plural(formulae_to_install.length)}, with result:"
+ puts formulae_to_install.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", "
end
unless upgrade_pinned? || pinned.empty?
oh1 "Not upgrading #{pinned.length} pinned package#{plural(pinned.length)}:"
- puts pinned.map { |f| "#{f.full_name} #{f.pkg_version}" } * ", "
+ puts pinned.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", "
end
- outdated.each do |f|
+ formulae_to_install.each do |f|
upgrade_formula(f)
next unless ARGV.include?("--cleanup")
next unless f.installed?
@@ -76,7 +78,11 @@ module Homebrew
end
def upgrade_formula(f)
- outdated_keg = Keg.new(f.linked_keg.resolved_path) if f.linked_keg.directory?
+ formulae_maybe_with_kegs = [f] + f.old_installed_formulae
+ outdated_kegs = formulae_maybe_with_kegs.
+ map(&:linked_keg).
+ select(&:directory?).
+ map { |k| Keg.new(k.resolved_path) }
fi = FormulaInstaller.new(f)
fi.options = f.build.used_options
@@ -87,12 +93,12 @@ module Homebrew
fi.debug = ARGV.debug?
fi.prelude
- oh1 "Upgrading #{f.full_name}"
+ oh1 "Upgrading #{f.full_specified_name}"
# first we unlink the currently active keg for this formula otherwise it is
# possible for the existing build to interfere with the build we are about to
# do! Seriously, it happens!
- outdated_keg.unlink if outdated_keg
+ outdated_kegs.each(&:unlink)
fi.install
fi.finish
@@ -117,7 +123,7 @@ module Homebrew
ensure
# restore previous installation state if build failed
begin
- outdated_keg.link if outdated_keg && !f.installed?
+ outdated_kegs.each(&:link) if !f.installed?
rescue
nil
end
diff --git a/Library/Homebrew/extend/ARGV.rb b/Library/Homebrew/extend/ARGV.rb
index 63e46a1e2..d9f599877 100644
--- a/Library/Homebrew/extend/ARGV.rb
+++ b/Library/Homebrew/extend/ARGV.rb
@@ -37,11 +37,23 @@ module HomebrewArgvExtension
f.version.update_commit(k.version.version.commit) if k.version.head?
end
end
- f
else
rack = Formulary.to_rack(name)
- Formulary.from_rack(rack, spec(nil))
+ alias_path = Formulary.factory(name).alias_path
+ f = Formulary.from_rack(rack, spec(nil), alias_path: alias_path)
end
+
+ # If this formula was installed with an alias that has since changed,
+ # then it was specified explicitly in ARGV. (Using the alias would
+ # instead have found the new formula.)
+ #
+ # Because of this, the user is referring to this specific formula,
+ # not any formula targetted by the same alias, so in this context
+ # the formula shouldn't be considered outdated if the alias used to
+ # install it has changed.
+ f.follow_installed_alias = false
+
+ f
end
end
diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb
index f2da6fbc8..41ddda307 100644
--- a/Library/Homebrew/formula.rb
+++ b/Library/Homebrew/formula.rb
@@ -60,16 +60,24 @@ class Formula
# e.g. `this-formula`
attr_reader :name
- # The name that was used to identify this {Formula}.
- # Could be the name of the {Formula}, or an alias.
- # e.g. `another-name-for-this-formula`
+ # The path to the alias that was used to identify this {Formula}.
+ # e.g. `/usr/local/Library/Taps/homebrew/homebrew-core/Aliases/another-name-for-this-formula`
attr_reader :alias_path
+ # The name of the alias that was used to identify this {Formula}.
+ # e.g. `another-name-for-this-formula`
+ attr_reader :alias_name
+
# The fully-qualified name of this {Formula}.
# For core formula it's the same as {#name}.
# e.g. `homebrew/tap-name/this-formula`
attr_reader :full_name
+ # The fully-qualified alias referring to this {Formula}.
+ # For core formula it's the same as {#alias_name}.
+ # e.g. `homebrew/tap-name/another-name-for-this-formula`
+ attr_reader :full_alias_name
+
# The full path to this {Formula}.
# e.g. `/usr/local/Library/Taps/homebrew/homebrew-core/Formula/this-formula.rb`
attr_reader :path
@@ -149,25 +157,31 @@ class Formula
# @return [BuildOptions]
attr_accessor :build
+ # A {Boolean} indicating whether this formula should be considered outdated
+ # if the target of the alias it was installed with has since changed.
+ # Defaults to true.
+ # @return [Boolean]
+ attr_accessor :follow_installed_alias
+ alias follow_installed_alias? follow_installed_alias
+
# @private
def initialize(name, path, spec, alias_path: nil)
@name = name
@path = path
@alias_path = alias_path
+ @alias_name = File.basename(alias_path) if alias_path
@revision = self.class.revision || 0
@version_scheme = self.class.version_scheme || 0
- if path == Formulary.core_path(name)
- @tap = CoreTap.instance
- @full_name = name
+ @tap = if path == Formulary.core_path(name)
+ CoreTap.instance
elsif path.to_s =~ HOMEBREW_TAP_PATH_REGEX
- @tap = Tap.fetch($1, $2)
- @full_name = "#{@tap}/#{name}"
- else
- @tap = nil
- @full_name = name
+ Tap.fetch($1, $2)
end
+ @full_name = get_full_name(name)
+ @full_alias_name = get_full_name(@alias_name)
+
set_spec :stable
set_spec :devel
set_spec :head
@@ -183,6 +197,7 @@ class Formula
validate_attributes!
@build = active_spec.build
@pin = FormulaPin.new(self)
+ @follow_installed_alias = true
end
# @private
@@ -197,6 +212,16 @@ class Formula
private
+ # Allow full name logic to be re-used between names, aliases,
+ # and installed aliases.
+ def get_full_name(name)
+ if name.nil? || @tap.nil? || @tap.core_tap?
+ name
+ else
+ "#{@tap}/#{name}"
+ end
+ end
+
def set_spec(name)
spec = self.class.send(name)
if spec.url
@@ -236,11 +261,39 @@ class Formula
path if path =~ %r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases}
end
+ def installed_alias_name
+ File.basename(installed_alias_path) if installed_alias_path
+ end
+
+ def full_installed_alias_name
+ get_full_name(installed_alias_name)
+ end
+
# The path that was specified to find this formula.
def specified_path
alias_path || path
end
+ # The name specified to find this formula.
+ def specified_name
+ alias_name || name
+ end
+
+ # The name (including tap) specified to find this formula.
+ def full_specified_name
+ full_alias_name || full_name
+ end
+
+ # The name specified to install this formula.
+ def installed_specified_name
+ installed_alias_name || name
+ end
+
+ # The name (including tap) specified to install this formula.
+ def full_installed_specified_name
+ full_installed_alias_name || full_name
+ end
+
# Is the currently active {SoftwareSpec} a {#stable} build?
# @private
def stable?
@@ -1067,39 +1120,77 @@ class Formula
end
# @private
- def outdated_versions(options = {})
- @outdated_versions ||= Hash.new do |cache, key|
+ def outdated_kegs(options = {})
+ @outdated_kegs ||= Hash.new do |cache, key|
raise Migrator::MigrationNeededError, self if migration_needed?
- cache[key] = _outdated_versions(key)
+ cache[key] = _outdated_kegs(key)
end
- @outdated_versions[options]
+ @outdated_kegs[options]
end
- def _outdated_versions(options = {})
- all_versions = []
+ def _outdated_kegs(options = {})
+ all_kegs = []
installed_kegs.each do |keg|
+ all_kegs << keg
version = keg.version
- all_versions << version
next if version.head?
tab = Tab.for_keg(keg)
next if version_scheme > tab.version_scheme
next if version_scheme == tab.version_scheme && pkg_version > version
- return []
+ next if follow_installed_alias? && installed_alias_target_changed?
+
+ return [] # this keg is the current version of the formula, so it's not outdated
end
+ # Even if this formula hasn't been installed, there may be installations
+ # of other formulae which used to be targets of the alias currently
+ # targetting this formula. These should be counted as outdated versions.
+ all_kegs.concat old_installed_formulae.flat_map(&:installed_kegs)
+
head_version = latest_head_version
if head_version && !head_version_outdated?(head_version, options)
[]
else
- all_versions.sort
+ all_kegs.sort_by(&:version)
end
end
+ def current_installed_alias_target
+ Formulary.factory(installed_alias_path) if installed_alias_path
+ end
+
+ # Has the target of the alias used to install this formula changed?
+ # Returns false if the formula wasn't installed with an alias.
+ def installed_alias_target_changed?
+ ![self, nil].include?(current_installed_alias_target)
+ end
+
+ # Is this formula the target of an alias used to install an old formula?
+ def supersedes_an_installed_formula?
+ old_installed_formulae.any?
+ end
+
+ # Has the alias used to install the formula changed, or are different
+ # formulae already installed with this alias?
+ def alias_changed?
+ installed_alias_target_changed? || supersedes_an_installed_formula?
+ end
+
+ # If the alias has changed value, return the new formula.
+ # Otherwise, return self.
+ def latest_formula
+ installed_alias_target_changed? ? current_installed_alias_target : self
+ end
+
+ def old_installed_formulae
+ alias_path ? self.class.installed_with_alias_path(alias_path) : []
+ end
+
# @private
def outdated?(options = {})
- !outdated_versions(options).empty?
+ !outdated_kegs(options).empty?
rescue Migrator::MigrationNeededError
true
end
@@ -1254,6 +1345,11 @@ class Formula
end.compact
end
+ def self.installed_with_alias_path(alias_path)
+ return [] if alias_path.nil?
+ installed.select { |f| f.installed_alias_path == alias_path }
+ end
+
# an array of all alias files of core {Formula}
# @private
def self.core_alias_files
diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb
index 1cd9dce2a..4c20c2225 100644
--- a/Library/Homebrew/formulary.rb
+++ b/Library/Homebrew/formulary.rb
@@ -77,8 +77,11 @@ class Formulary
end
# Gets the formula instance.
- def get_formula(spec)
- klass.new(name, path, spec, alias_path: alias_path)
+ #
+ # `alias_path` can be overridden here in case an alias was used to refer to
+ # a formula that was loaded in another way.
+ def get_formula(spec, alias_path: nil)
+ klass.new(name, path, spec, alias_path: alias_path || self.alias_path)
end
def klass
@@ -103,7 +106,7 @@ class Formulary
super name, Formulary.path(full_name)
end
- def get_formula(spec)
+ def get_formula(spec, alias_path: nil)
formula = super
formula.local_bottle_path = @bottle_filename
formula_version = formula.pkg_version
@@ -120,7 +123,7 @@ class Formulary
path = alias_path.resolved_path
name = path.basename(".rb").to_s
super name, path
- @alias_path = alias_path
+ @alias_path = alias_path.to_s
end
end
@@ -175,7 +178,7 @@ class Formulary
super name, path
end
- def get_formula(spec)
+ def get_formula(spec, alias_path: nil)
super
rescue FormulaUnavailableError => e
raise TapFormulaUnavailableError.new(tap, name), "", e.backtrace
@@ -187,7 +190,7 @@ class Formulary
super name, Formulary.core_path(name)
end
- def get_formula(_spec)
+ def get_formula(_spec, alias_path: nil)
raise FormulaUnavailableError, name
end
end
@@ -215,38 +218,42 @@ class Formulary
# * a formula pathname
# * a formula URL
# * a local bottle reference
- def self.factory(ref, spec = :stable)
- loader_for(ref).get_formula(spec)
+ def self.factory(ref, spec = :stable, alias_path: nil)
+ loader_for(ref).get_formula(spec, alias_path: alias_path)
end
# Return a Formula instance for the given rack.
# It will auto resolve formula's spec when requested spec is nil
- def self.from_rack(rack, spec = nil)
+ #
+ # The :alias_path option will be used if the formula is found not to be
+ # installed, and discarded if it is installed because the alias_path used
+ # to install the formula will be set instead.
+ def self.from_rack(rack, spec = nil, alias_path: nil)
kegs = rack.directory? ? rack.subdirs.map { |d| Keg.new(d) } : []
keg = kegs.detect(&:linked?) || kegs.detect(&:optlinked?) || kegs.max_by(&:version)
if keg
- from_keg(keg, spec)
+ from_keg(keg, spec, :alias_path => alias_path)
else
- factory(rack.basename.to_s, spec || :stable)
+ factory(rack.basename.to_s, spec || :stable, :alias_path => alias_path)
end
end
# Return a Formula instance for the given keg.
# It will auto resolve formula's spec when requested spec is nil
- def self.from_keg(keg, spec = nil)
+ def self.from_keg(keg, spec = nil, alias_path: nil)
tab = Tab.for_keg(keg)
tap = tab.tap
spec ||= tab.spec
f = if tap.nil?
- factory(keg.rack.basename.to_s, spec)
+ factory(keg.rack.basename.to_s, spec, :alias_path => alias_path)
else
begin
- factory("#{tap}/#{keg.rack.basename}", spec)
+ factory("#{tap}/#{keg.rack.basename}", spec, :alias_path => alias_path)
rescue FormulaUnavailableError
# formula may be migrated to different tap. Try to search in core and all taps.
- factory(keg.rack.basename.to_s, spec)
+ factory(keg.rack.basename.to_s, spec, :alias_path => alias_path)
end
end
f.build = tab
@@ -346,7 +353,7 @@ class Formulary
end
def self.core_path(name)
- CoreTap.instance.formula_dir/"#{name.downcase}.rb"
+ CoreTap.instance.formula_dir/"#{name.to_s.downcase}.rb"
end
def self.tap_paths(name, taps = Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/"])
diff --git a/Library/Homebrew/test/test_formulary.rb b/Library/Homebrew/test/test_formulary.rb
index 8b3417dad..c545ff84d 100644
--- a/Library/Homebrew/test/test_formulary.rb
+++ b/Library/Homebrew/test/test_formulary.rb
@@ -89,7 +89,7 @@ class FormularyFactoryTest < Homebrew::TestCase
FileUtils.ln_s @path, alias_path
result = Formulary.factory("foo")
assert_kind_of Formula, result
- assert_equal alias_path, result.alias_path
+ assert_equal alias_path.to_s, result.alias_path
ensure
alias_dir.rmtree
end