brew/Library/Homebrew/dependencies.rb
Charlie Sharpsteen e873411694 XCodeDependency: Clarify that Xcode.app is needed
Clarify the error message arising from XCodeDependency so that users know
XCode.app is needed and that just installing the CLT does not count as
"installing XCode".
2012-08-24 19:38:03 -07:00

341 lines
8.6 KiB
Ruby

## This file defines dependencies and requirements.
##
## A dependency is a formula that another formula needs to install.
## A requirement is something other than a formula that another formula
## needs to be present. This includes external language modules,
## command-line tools in the path, or any arbitrary predicate.
##
## The `depends_on` method in the formula DSL is used to declare
## dependencies and requirements.
# This class is used by `depends_on` in the formula DSL to turn dependency
# specifications into the proper kinds of dependencies and requirements.
class DependencyCollector
# Define the languages that we can handle as external dependencies.
LANGUAGE_MODULES = [
:chicken, :jruby, :lua, :node, :perl, :python, :rbx, :ruby
].freeze
attr_reader :deps, :requirements
def initialize
@deps = Dependencies.new
@requirements = Set.new
end
def add spec
tag = nil
spec, tag = spec.shift if spec.is_a? Hash
dep = parse_spec(spec, tag)
# Some symbol specs are conditional, and resolve to nil if there is no
# dependency needed for the current platform.
return if dep.nil?
# Add dep to the correct bucket
(dep.is_a?(Requirement) ? @requirements : @deps) << dep
end
private
def parse_spec spec, tag
case spec
when Symbol
parse_symbol_spec(spec, tag)
when String
if LANGUAGE_MODULES.include? tag
LanguageModuleDependency.new(tag, spec)
else
Dependency.new(spec, tag)
end
when Formula
Dependency.new(spec.name, tag)
when Dependency, Requirement
spec
else
raise "Unsupported type #{spec.class} for #{spec}"
end
end
def parse_symbol_spec spec, tag
case spec
when :autoconf, :automake, :bsdmake, :libtool
# Xcode no longer provides autotools or some other build tools
Dependency.new(spec.to_s) unless MacOS::Xcode.provides_autotools?
when :libpng, :freetype, :pixman, :fontconfig, :cairo
if MacOS.version >= :mountain_lion
Dependency.new(spec.to_s)
else
X11Dependency.new(tag)
end
when :x11
X11Dependency.new(tag)
when :xcode
XCodeDependency.new
else
raise "Unsupported special dependency #{spec}"
end
end
end
# A list of formula dependencies.
class Dependencies < Array
def include? dependency_name
self.any?{|d| d.name == dependency_name}
end
end
# A dependency on another Homebrew formula.
class Dependency
attr_reader :name, :tags
def initialize name, tags=nil
@name = name
@tags = case tags
when Array then tags.each {|s| s.to_s}
when nil then []
else [tags.to_s]
end
end
def to_s
@name
end
def ==(other_dep)
@name == other_dep.to_s
end
def <=>(other_dep)
@name <=> other_dep.to_s
end
def options
@tags.select{|p|p.start_with? '--'}
end
end
# A base class for non-formula requirements needed by formulae.
# A "fatal" requirement is one that will fail the build if it is not present.
# By default, Requirements are non-fatal.
class Requirement
# Should return true if this requirement is met.
def satisfied?; false; end
# Should return true if not meeting this requirement should fail the build.
def fatal?; false; end
# The message to show when the requirement is not met.
def message; ""; end
# Requirements can modify the current build environment by overriding this.
# See X11Dependency
def modify_build_environment; nil end
def eql?(other)
other.is_a? self.class and hash == other.hash
end
def hash
message.hash
end
end
# A dependency on a language-specific module.
class LanguageModuleDependency < Requirement
def initialize language, module_name, import_name=nil
@language = language
@module_name = module_name
@import_name = import_name || module_name
end
def fatal?; true; end
def satisfied?
quiet_system(*the_test)
end
def message; <<-EOS.undent
Unsatisfied dependency: #{@module_name}
Homebrew does not provide #{@language.to_s.capitalize} dependencies; install with:
#{command_line} #{@module_name}
EOS
end
def the_test
case @language
when :chicken then %W{/usr/bin/env csi -e (use #{@import_name})}
when :jruby then %W{/usr/bin/env jruby -rubygems -e require\ '#{@import_name}'}
when :lua then %W{/usr/bin/env luarocks show #{@import_name}}
when :node then %W{/usr/bin/env node -e require('#{@import_name}');}
when :perl then %W{/usr/bin/env perl -e use\ #{@import_name}}
when :python then %W{/usr/bin/env python -c import\ #{@import_name}}
when :ruby then %W{/usr/bin/env ruby -rubygems -e require\ '#{@import_name}'}
when :rbx then %W{/usr/bin/env rbx -rubygems -e require\ '#{@import_name}'}
end
end
def command_line
case @language
when :chicken then "chicken-install"
when :jruby then "jruby -S gem install"
when :lua then "luarocks install"
when :node then "npm install"
when :perl then "cpan -i"
when :python then "easy_install"
when :rbx then "rbx gem install"
when :ruby then "gem install"
end
end
end
# This requirement is used to require an X11 implementation,
# optionally with a minimum version number.
class X11Dependency < Requirement
def initialize min_version=nil
@min_version = min_version
end
def fatal?; true; end
def satisfied?
MacOS::XQuartz.installed? and (@min_version.nil? or @min_version <= MacOS::XQuartz.version)
end
def message; <<-EOS.undent
Unsatisfied dependency: XQuartz #{@min_version}
Please install the latest version of XQuartz:
https://xquartz.macosforge.org
EOS
end
def modify_build_environment
ENV.x11
end
def hash
"X11".hash
end
end
# There are multiple implementations of MPI-2 available.
# http://www.mpi-forum.org/
# This requirement is used to find an appropriate one.
class MPIDependency < Requirement
attr_reader :lang_list
def initialize *lang_list
@lang_list = lang_list
@non_functional = []
@unknown_langs = []
end
def fatal?; true; end
def mpi_wrapper_works? compiler
compiler = which compiler
return false if compiler.nil? or not compiler.executable?
# Some wrappers are non-functional and will return a non-zero exit code
# when invoked for version info.
#
# NOTE: A better test may be to do a small test compilation a la autotools.
quiet_system compiler, '--version'
end
def satisfied?
@lang_list.each do |lang|
case lang
when :cc, :cxx, :f90, :f77
compiler = 'mpi' + lang.to_s
@non_functional << compiler unless mpi_wrapper_works? compiler
else
@unknown_langs << lang.to_s
end
end
@unknown_langs.empty? and @non_functional.empty?
end
def modify_build_environment
# Set environment variables to help configure scripts find MPI compilers.
# Variable names taken from:
# http://www.gnu.org/software/autoconf-archive/ax_mpi.html
lang_list.each do |lang|
compiler = 'mpi' + lang.to_s
mpi_path = which compiler
# Fortran 90 environment var has a different name
compiler = 'MPIFC' if lang == :f90
ENV[compiler.upcase] = mpi_path
end
end
def message
if not @unknown_langs.empty?
<<-EOS.undent
There is no MPI compiler wrapper for:
#{@unknown_langs.join ', '}
The following values are valid arguments to `MPIDependency.new`:
:cc, :cxx, :f90, :f77
EOS
else
<<-EOS.undent
Homebrew could not locate working copies of the following MPI compiler
wrappers:
#{@non_functional.join ', '}
If you have a MPI installation, please ensure the bin folder is on your
PATH and that all the wrappers are functional. Otherwise, a MPI
installation can be obtained from homebrew by *picking one* of the
following formulae:
open-mpi, mpich2
EOS
end
end
end
# This requirement added by the `conflicts_with` DSL method.
class ConflictRequirement < Requirement
attr_reader :formula
def initialize formula, message
@formula = formula
@message = message
end
def message; @message; end
def satisfied?
keg = Formula.factory(@formula).prefix
not keg.exist? && Keg.new(keg).linked?
end
# The user can chose to force installation even in the face of conflicts.
def fatal?
not ARGV.force?
end
end
class XCodeDependency < Requirement
def fatal?; true; end
def satisfied?
MacOS::Xcode.installed?
end
def message; <<-EOS.undent
A full installation of XCode.app is required to compile this software.
Installing just the Command Line Tools is not sufficent.
EOS
end
end