This repository has been archived by the owner. It is now read-only.

FormulaInstaller and dependency improvements #14456

Closed
wants to merge 33 commits into
from
Commits
Jump to file or symbol
Failed to load files and symbols.
+786 −272
Split
@@ -15,7 +15,7 @@ def install_bottle? f
and f.downloader.local_bottle_path
not ARGV.build_from_source? \
and MacOS.bottles_supported? \
- and ARGV.used_options(f).empty? \
+ and f.build.used_options.empty? \
and bottle_current?(f)
end
@@ -65,12 +65,12 @@ def post_superenv_hacks f
def pre_superenv_hacks f
# Allow a formula to opt-in to the std environment.
ARGV.unshift '--env=std' if (f.env.std? or
- f.recursive_deps.detect{|d| d.name == 'scons' }) and
+ f.recursive_dependencies.detect{|d| d.name == 'scons' }) and
not ARGV.include? '--env=super'
end
def install f
- deps = f.recursive_deps
+ deps = f.recursive_dependencies.map(&:to_formula)
keg_only_deps = deps.select(&:keg_only?)
pre_superenv_hacks(f)
@@ -83,7 +83,7 @@ def install f
if superenv?
ENV.deps = keg_only_deps.map(&:to_s)
- ENV.all_deps = f.recursive_deps.map(&:to_s)
+ ENV.all_deps = f.recursive_dependencies.map(&:to_s)
ENV.x11 = f.recursive_requirements.detect{|rq| rq.class == X11Dependency }
ENV.setup_build_environment
post_superenv_hacks(f)
@@ -0,0 +1,88 @@
+require 'options'
+
+# This class holds the build-time options defined for a Formula,
+# and provides named access to those options during install.
+class BuildOptions
+ attr_accessor :args
+ include Enumerable
+
+ def initialize args
+ @args = Options.coerce(args)
+ @options = Options.new
+ end
+
+ def add name, description=nil
+ description ||= case name.to_s
+ when "universal" then "Build a universal binary"
+ when "32-bit" then "Build 32-bit only"
+ end.to_s
+
+ @options << Option.new(name, description)
+ end
+
+ def has_option? name
+ any? { |opt| opt.name == name }
+ end
+
+ def empty?
+ @options.empty?
+ end
+
+ def each(*args, &block)
+ @options.each(*args, &block)
+ end
+
+ def as_flags
+ @options.as_flags
+ end
+
+ def include? name
+ args.include? '--' + name
+ end
+
+ def with? name
+ if has_option? "with-#{name}"
+ include? "with-#{name}"
+ elsif has_option? "without-#{name}"
+ not include? "without-#{name}"
+ else
+ false
+ end
+ end
+
+ def without? name
+ not with? name
+ end
+
+ def head?
+ args.include? '--HEAD'
+ end
+
+ def devel?
+ args.include? '--devel'
+ end
+
+ def stable?
+ not (head? or devel?)
+ end
+
+ # True if the user requested a universal build.
+ def universal?
+ args.include?('--universal') && has_option?('universal')
+ end
+
+ # Request a 32-bit only build.
+ # This is needed for some use-cases though we prefer to build Universal
+ # when a 32-bit version is needed.
+ def build_32_bit?
+ args.include?('--32-bit') && has_option?('32-bit')
+ end
+
+ def used_options
+ Options.new(@options & @args)
+ end
+
+ def unused_options
+ Options.new(@options - @args)
+ end
+end
@@ -129,18 +129,24 @@ def audit_deps
# Check for things we don't like to depend on.
# We allow non-Homebrew installs whenever possible.
- f.deps.each do |d|
+ f.deps.each do |dep|
begin
- dep_f = Formula.factory d
+ dep_f = dep.to_formula
rescue
- problem "Can't find dependency \"#{d}\"."
+ problem "Can't find dependency #{dep.inspect}."
end
- case d.name
+ dep.options.reject do |opt|
+ dep_f.build.has_option?(opt.name)
+ end.each do |opt|
+ problem "Dependency #{dep} does not define option #{opt.name.inspect}"
+ end
+
+ case dep.name
when "git", "python", "ruby", "emacs", "mysql", "postgresql", "mercurial"
problem <<-EOS.undent
- Don't use #{d} as a dependency. We allow non-Homebrew
- #{d} installations.
+ Don't use #{dep} as a dependency. We allow non-Homebrew
+ #{dep} installations.
EOS
when 'gfortran'
problem "Use ENV.fortran during install instead of depends_on 'gfortran'"
@@ -23,7 +23,7 @@ def deps
else
raise FormulaUnspecifiedError if ARGV.named.empty?
all_deps = ARGV.formulae.map do |f|
- ARGV.one? ? f.deps : f.recursive_deps
+ ARGV.one? ? f.deps : f.recursive_dependencies
end.intersection.map(&:name)
all_deps.sort! unless ARGV.include? "-n"
puts all_deps
@@ -872,7 +872,7 @@ def check_tmpdir
def check_missing_deps
return unless HOMEBREW_CELLAR.exist?
s = Set.new
- Homebrew.missing_deps(Homebrew.installed_brews).each do |_, deps|
+ Homebrew.missing_deps(Formula.installed).each do |_, deps|
s.merge deps
end
@@ -10,7 +10,7 @@ def fetch
bucket = []
ARGV.formulae.each do |f|
bucket << f
- bucket << f.recursive_deps
+ bucket << f.recursive_dependencies.map(&:to_formula)
end
bucket = bucket.flatten.uniq
@@ -35,8 +35,10 @@ def link
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
@@ -1,21 +1,21 @@
require 'formula'
+require 'tab'
module Homebrew extend self
- def installed_brews
- formulae = []
- HOMEBREW_CELLAR.subdirs.each do |rack|
- f = Formula.factory rack.basename.to_s rescue nil
- formulae << f if f and f.rack.exist? and f.rack.subdirs.length > 0
- end
- formulae
- end
-
def missing_deps ff
missing = {}
ff.each do |f|
- missing_deps = f.recursive_deps.uniq.reject do |dep|
- dep.rack.exist? and dep.rack.subdirs.length > 0
+ missing_deps = f.recursive_dependencies do |dependent, dep|
+ if dep.optional? || dep.recommended?
+ tab = Tab.for_formula(dependent)
+ Dependency.prune unless tab.with?(dep.name)
+ elsif dep.build?
+ Dependency.prune
end
+ end
+
+ missing_deps.map!(&:to_formula)
+ missing_deps.reject! { |d| d.rack.exist? && d.rack.subdirs.length > 0 }
unless missing_deps.empty?
yield f.name, missing_deps if block_given?
@@ -29,7 +29,7 @@ def missing
return unless HOMEBREW_CELLAR.exist?
ff = if ARGV.named.empty?
- installed_brews
+ Formula.installed
else
ARGV.formulae
end
@@ -7,10 +7,12 @@ def uninstall
if not ARGV.force?
ARGV.kegs.each do |keg|
- puts "Uninstalling #{keg}..."
- keg.unlink
- keg.uninstall
- rm_opt_link keg.fname
+ keg.lock do

This comment has been minimized.

Show comment Hide comment
@mxcl

mxcl Aug 31, 2012

Member

Feels great with this syntax.

@mxcl

mxcl Aug 31, 2012

Member

Feels great with this syntax.

+ puts "Uninstalling #{keg}..."
+ keg.unlink
+ keg.uninstall
+ rm_opt_link keg.fname
+ end
end
else
ARGV.named.each do |name|
@@ -3,8 +3,10 @@ def unlink
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
@@ -32,17 +32,6 @@ def upgrade
exit 1 if outdated.empty?
end
- # Expand the outdated list to include outdated dependencies then sort and
- # reduce such that dependencies are installed first and installation is not
- # attempted twice. Sorting is implicit the way `recursive_deps` returns
- # root dependencies at the head of the list and `uniq` keeps the first
- # element it encounters and discards the rest.
- ARGV.filter_for_dependencies do
- outdated.map!{ |f| f.recursive_deps.reject{ |d| d.installed? } << f }
- outdated.flatten!
- outdated.uniq!
- end unless ARGV.ignore_deps?
-
if outdated.length > 1
oh1 "Upgrading #{outdated.length} outdated package#{outdated.length.plural_s}, with result:"
puts outdated.map{ |f| "#{f.name} #{f.version}" } * ", "
@@ -54,15 +43,12 @@ def upgrade
end
def upgrade_formula f
- # Generate using `for_keg` since the formula object points to a newer version
- # that doesn't exist yet. Use `opt_prefix` to guard against keg-only installs.
- # Also, guard against old installs that may not have an `opt_prefix` symlink.
- tab = (f.opt_prefix.exist? ? Tab.for_keg(f.opt_prefix) : Tab.dummy_tab(f))
+ tab = Tab.for_formula(f)
outdated_keg = Keg.new(f.linked_keg.realpath) rescue nil
- installer = FormulaInstaller.new(f, tab)
+ installer = FormulaInstaller.new(f)
+ installer.tab = tab
installer.show_header = false
- installer.install_bottle = (install_bottle?(f) and tab.used_options.empty?)
oh1 "Upgrading #{f.name}"
@@ -12,20 +12,17 @@ def uses
uses = Formula.select do |f|
ARGV.formulae.all? do |ff|
if ARGV.flag? '--recursive'
- f.recursive_deps.include? ff
+ f.recursive_dependencies.any? { |dep| dep.name == ff.name }
else
- f.deps.include? ff
+ f.deps.any? { |dep| dep.name == ff.name }
end
end
end
if ARGV.include? "--installed"
- uses = uses.select do |f|
- keg = HOMEBREW_CELLAR/f
- keg.directory? and not keg.subdirs.empty?
- end
+ uses = uses.select { |f| Formula.installed.include? f }
end
- puts_columns uses.map{|f| f.to_s}.sort
+ puts_columns uses.map(&:to_s).sort
end
end
@@ -99,6 +99,21 @@ def bottle_sha1 val=nil
val.nil? ? @bottle_sha1 : @bottle_sha1 = val
end
end
+
+ # These methods return lists of Formula objects.
+ # They are eprecated in favor of Dependency::expand_dependencies
+ # and Formula#recursive_dependencies, which return lists of
+ # Dependency objects instead.
+ def self.expand_deps f
+ f.deps.map do |dep|
+ f_dep = Formula.factory dep.to_s
+ expand_deps(f_dep) << f_dep
+ end
+ end
+
+ def recursive_deps
+ Formula.expand_deps(self).flatten.uniq
+ end
end
class UnidentifiedFormula < Formula
Oops, something went wrong.