FormulaInstaller: allow formulae to pass options to deps

Formulae can now pass build options to dependencies. The following
syntax is supported:

  depends_on 'foo' => 'with-bar'
  depends_on 'foo' => ['with-bar', 'with-baz']

If a dependency is already installed but lacks the required build
options, an exception is raised. Eventually we may be able to just stash
the existing keg and reinstall it with the combined set of used_options
and passed options, but enabling that is left for another day.
This commit is contained in:
Jack Nagel 2013-01-23 00:26:28 -06:00
parent 951872c4be
commit 046d802d09
5 changed files with 46 additions and 22 deletions

View File

@ -3,7 +3,7 @@ require 'options'
# This class holds the build-time options defined for a Formula, # This class holds the build-time options defined for a Formula,
# and provides named access to those options during install. # and provides named access to those options during install.
class BuildOptions class BuildOptions
attr_writer :args attr_accessor :args
include Enumerable include Enumerable
def initialize args def initialize args

View File

@ -138,7 +138,7 @@ module Dependable
end end
def options def options
tags.reject { |tag| RESERVED_TAGS.include? tag }.map { |tag| '--'+tag.to_s } Options.new((tags - RESERVED_TAGS).map { |o| Option.new(o) })
end end
end end
@ -171,13 +171,24 @@ class Dependency
end end
def to_formula def to_formula
Formula.factory(name) f = Formula.factory(name)
# Add this dependency's options to the formula's build args
f.build.args = f.build.args.concat(options)
f
end
def installed?
to_formula.installed?
end end
def requested? def requested?
ARGV.formulae.include?(to_formula) rescue false ARGV.formulae.include?(to_formula) rescue false
end end
def universal!
tags << 'universal' if to_formula.build.has_option? 'universal'
end
# Expand the dependencies of f recursively, optionally yielding # Expand the dependencies of f recursively, optionally yielding
# [f, dep] to allow callers to apply arbitrary filters to the list. # [f, dep] to allow callers to apply arbitrary filters to the list.
# The default filter, which is used when a block is not supplied, # The default filter, which is used when a block is not supplied,

View File

@ -8,6 +8,7 @@ require 'caveats'
class FormulaInstaller class FormulaInstaller
attr :f attr :f
attr :tab, true attr :tab, true
attr :options, true
attr :show_summary_heading, true attr :show_summary_heading, true
attr :ignore_deps, true attr :ignore_deps, true
attr :show_header, true attr :show_header, true
@ -116,20 +117,6 @@ class FormulaInstaller
end end
end end
def dep_needed? dep
dep_f = dep.to_formula
if dep_f.installed?
# If the dep is already installed, skip it.
false
elsif install_bottle and dep.build?
# We skip build-time deps when installing bottles.
false
else
# Otherwise, we need to install it.
true
end
end
def effective_deps def effective_deps
@deps ||= begin @deps ||= begin
deps = Set.new deps = Set.new
@ -138,15 +125,34 @@ class FormulaInstaller
# any influential flags (--HEAD, --devel, etc.) the user has passed # any influential flags (--HEAD, --devel, etc.) the user has passed
# when we check the installed status. # when we check the installed status.
requested_deps = f.recursive_dependencies.select do |dep| requested_deps = f.recursive_dependencies.select do |dep|
dep_f = dep.to_formula dep.requested? && !dep.installed?
dep_f.requested? and not dep_f.installed?
end end
# Otherwise, we filter these influential flags so that they do not # Otherwise, we filter these influential flags so that they do not
# affect installation prefixes and other properties when we decide # affect installation prefixes and other properties when we decide
# whether or not the dep is needed. # whether or not the dep is needed.
necessary_deps = ARGV.filter_for_dependencies do necessary_deps = ARGV.filter_for_dependencies do
f.recursive_dependencies.select { |d| dep_needed? d } f.recursive_dependencies do |dependent, dep|
if dep.optional? || dep.recommended?
Dependency.prune unless dependent.build.with?(dep.name)
elsif dep.build?
Dependency.prune if pour_bottle?
end
dep.universal! if f.build.universal?
dep_f = dep.to_formula
dep_tab = Tab.for_formula(dep)
missing = dep.options - dep_tab.used_options
if dep.installed?
if missing.empty?
Dependency.prune
else
raise "#{f} dependency #{dep} not installed with:\n #{missing*', '}"
end
end
end
end end
deps.merge(requested_deps) deps.merge(requested_deps)
@ -171,12 +177,14 @@ class FormulaInstaller
def install_dependency dep def install_dependency dep
dep_tab = Tab.for_formula(dep) dep_tab = Tab.for_formula(dep)
dep_options = dep.options
dep = dep.to_formula dep = dep.to_formula
outdated_keg = Keg.new(dep.linked_keg.realpath) rescue nil outdated_keg = Keg.new(dep.linked_keg.realpath) rescue nil
fi = FormulaInstaller.new(dep) fi = FormulaInstaller.new(dep)
fi.tab = dep_tab fi.tab = dep_tab
fi.options = dep_options
fi.ignore_deps = true fi.ignore_deps = true
fi.show_header = false fi.show_header = false
oh1 "Installing #{f} dependency: #{Tty.green}#{dep}#{Tty.reset}" oh1 "Installing #{f} dependency: #{Tty.green}#{dep}#{Tty.reset}"
@ -258,7 +266,10 @@ class FormulaInstaller
# FIXME: enforce the download of the non-bottled package # FIXME: enforce the download of the non-bottled package
# in the spawned Ruby process. # in the spawned Ruby process.
args << '--build-from-source' args << '--build-from-source'
args.uniq! # Just in case some dupes were added # Add any options that were passed into this FormulaInstaller instance.
# Usually this represents options being passed by a dependent formula.
args.concat options
args.uniq!
fork do fork do
begin begin

View File

@ -1,5 +1,6 @@
require 'testing_env' require 'testing_env'
require 'dependencies' require 'dependencies'
require 'options'
class DependableTests < Test::Unit::TestCase class DependableTests < Test::Unit::TestCase
def setup def setup
@ -8,7 +9,7 @@ class DependableTests < Test::Unit::TestCase
end end
def test_options def test_options
assert_equal %w{--foo --bar}.sort, @dep.options.sort assert_equal %w{--foo --bar}.sort, @dep.options.as_flags.sort
end end
def test_interrogation def test_interrogation

View File

@ -1,5 +1,6 @@
require 'testing_env' require 'testing_env'
require 'dependencies' require 'dependencies'
require 'options'
require 'extend/set' require 'extend/set'
module DependencyCollectorTestExtension module DependencyCollectorTestExtension