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".
341 lines
8.6 KiB
Ruby
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
|