aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Nagel2012-03-18 13:58:13 -0500
committerJack Nagel2012-04-01 12:39:59 -0500
commitde444ead0b8898ea2989d6a8d5984c4c31318a64 (patch)
treed2cad5d7b20789f8a990b4293a58da7b36acbe5f
parentbfbfdf03eb2bcbb73082af0325f85761ea87719e (diff)
downloadbrew-de444ead0b8898ea2989d6a8d5984c4c31318a64.tar.bz2
New fails_with infrastructure
- Formulae can now declare failures on any compiler. - FailsWithLLVM and associated formula elements have been moved to compat. Signed-off-by: Jack Nagel <jacknagel@gmail.com>
-rwxr-xr-xLibrary/Homebrew/build.rb6
-rw-r--r--Library/Homebrew/compat/compatibility.rb59
-rw-r--r--Library/Homebrew/compilers.rb138
-rw-r--r--Library/Homebrew/formula.rb30
-rw-r--r--Library/Homebrew/formula_support.rb58
-rw-r--r--Library/Homebrew/test/test_formula.rb69
-rw-r--r--Library/Homebrew/test/testball.rb82
-rw-r--r--Library/Homebrew/test/testing_env.rb1
8 files changed, 370 insertions, 73 deletions
diff --git a/Library/Homebrew/build.rb b/Library/Homebrew/build.rb
index 8f1fe3924..319f58df9 100755
--- a/Library/Homebrew/build.rb
+++ b/Library/Homebrew/build.rb
@@ -58,6 +58,12 @@ def install f
end
end
+ if f.fails_with? ENV.compiler
+ cs = CompilerSelector.new f
+ cs.select_compiler
+ cs.advise
+ end
+
f.brew do
if ARGV.flag? '--interactive'
ohai "Entering interactive mode"
diff --git a/Library/Homebrew/compat/compatibility.rb b/Library/Homebrew/compat/compatibility.rb
index 3d67de077..acb03a75f 100644
--- a/Library/Homebrew/compat/compatibility.rb
+++ b/Library/Homebrew/compat/compatibility.rb
@@ -76,6 +76,16 @@ class Formula
def fails_with_llvm msg=nil, data=nil
FailsWithLLVM.new(msg, data).handle_failure
end
+
+ def fails_with_llvm?
+ fails_with? :llvm
+ end
+
+ def self.fails_with_llvm msg=nil, data=nil
+ fails_with_llvm_reason = FailsWithLLVM.new(msg, data)
+ @cc_failures ||= CompilerFailures.new
+ @cc_failures << fails_with_llvm_reason
+ end
end
class UnidentifiedFormula < Formula
@@ -94,3 +104,52 @@ module HomebrewEnvExtension extend self
compiler == :llvm
end
end
+
+class FailsWithLLVM
+ attr_reader :compiler, :build, :cause
+
+ def initialize msg=nil, data=nil
+ if msg.nil? or msg.kind_of? Hash
+ @cause = "(No specific reason was given)"
+ data = msg
+ else
+ @cause = msg
+ end
+ @build = (data.delete :build rescue nil).to_i
+ @compiler = :llvm
+ end
+
+ def handle_failure
+ return unless ENV.compiler == :llvm
+
+ # version 2336 is the latest version as of Xcode 4.2, so it is the
+ # latest version we have tested against so we will switch to GCC and
+ # bump this integer when Xcode 4.3 is released. TODO do that!
+ if build.to_i >= 2336
+ if MacOS.xcode_version < "4.2"
+ opoo "Formula will not build with LLVM, using GCC"
+ ENV.gcc
+ else
+ opoo "Formula will not build with LLVM, trying Clang"
+ ENV.clang
+ end
+ return
+ end
+ opoo "Building with LLVM, but this formula is reported to not work with LLVM:"
+ puts
+ puts cause
+ puts
+ puts <<-EOS.undent
+ We are continuing anyway so if the build succeeds, please open a ticket with
+ the following information: #{MacOS.llvm_build_version}-#{MACOS_VERSION}. So
+ that we can update the formula accordingly. Thanks!
+ EOS
+ puts
+ if MacOS.xcode_version < "4.2"
+ puts "If it doesn't work you can: brew install --use-gcc"
+ else
+ puts "If it doesn't work you can try: brew install --use-clang"
+ end
+ puts
+ end
+end
diff --git a/Library/Homebrew/compilers.rb b/Library/Homebrew/compilers.rb
new file mode 100644
index 000000000..ff56d34a0
--- /dev/null
+++ b/Library/Homebrew/compilers.rb
@@ -0,0 +1,138 @@
+class Compilers < Array
+ def include? cc
+ cc = cc.name if cc.is_a? Compiler
+ self.any? { |c| c.name == cc }
+ end
+end
+
+
+class CompilerFailures < Array
+ def include? cc
+ cc = Compiler.new(cc) unless cc.is_a? Compiler
+ self.any? { |failure| failure.compiler == cc.name }
+ end
+
+ def <<(failure)
+ super(failure) unless self.include? failure.compiler
+ end
+end
+
+
+class Compiler
+ attr_reader :name, :build
+
+ def initialize name
+ @name = name
+ @build = case name
+ when :clang then MacOS.clang_build_version.to_i
+ when :llvm then MacOS.llvm_build_version.to_i
+ when :gcc then MacOS.gcc_42_build_version.to_i
+ end
+ end
+
+ def ==(other)
+ @name.to_sym == other.to_sym
+ end
+end
+
+
+class CompilerFailure
+ attr_reader :compiler
+
+ def initialize compiler, &block
+ @compiler = compiler
+ instance_eval(&block) if block_given?
+ end
+
+ def build val=nil
+ val.nil? ? @build.to_i : @build = val.to_i
+ end
+
+ def cause val=nil
+ val.nil? ? @cause : @cause = val
+ end
+end
+
+
+# CompilerSelector is used to process a formula's CompilerFailures.
+# If no viable compilers are available, ENV.compiler is left as-is.
+class CompilerSelector
+ NAMES = { :clang => "Clang", :gcc => "GCC", :llvm => "LLVM" }
+
+ def initialize f
+ @f = f
+ @old_compiler = ENV.compiler
+ @compilers = Compilers.new
+ @compilers << Compiler.new(:clang) if MacOS.clang_build_version
+ @compilers << Compiler.new(:llvm) if MacOS.llvm_build_version
+ @compilers << Compiler.new(:gcc) if MacOS.gcc_42_build_version
+ end
+
+ def select_compiler
+ # @compilers is our list of available compilers. If @f declares a
+ # failure with compiler foo, then we remove foo from the list if
+ # the failing build is >= the currently installed version of foo.
+ @compilers.reject! do |cc|
+ failure = @f.fails_with? cc
+ next unless failure
+ failure.build >= cc.build
+ end
+
+ return if @compilers.empty? or @compilers.include? ENV.compiler
+
+ ENV.send case ENV.compiler
+ when :clang
+ if @compilers.include? :llvm then :llvm
+ elsif @compilers.include? :gcc then :gcc
+ else ENV.compiler
+ end
+ when :llvm
+ if @compilers.include? :clang and MacOS.clang_build_version >= 211 then :clang
+ elsif @compilers.include? :gcc then :gcc
+ elsif @compilers.include? :clang then :clang
+ else ENV.compiler
+ end
+ when :gcc
+ if @compilers.include? :clang and MacOS.clang_build_version >= 211 then :clang
+ elsif @compilers.include? :llvm then :llvm
+ elsif @compilers.include? :clang then :clang
+ else ENV.compiler
+ end
+ end
+ end
+
+ def advise
+ failure = @f.fails_with? @old_compiler
+ return unless failure
+
+ # If we're still using the original ENV.compiler, then the formula did not
+ # declare a specific failing build, so we continue and print some advice.
+ # Otherwise, tell the user that we're switching compilers.
+ if @old_compiler == ENV.compiler
+ cc = Compiler.new(ENV.compiler)
+ subject = "#{@f.name}-#{@f.version}: builds with #{NAMES[cc.name]}-#{cc.build}-#{MACOS_VERSION}"
+ warning = "Using #{NAMES[cc.name]}, but this formula is reported to fail with #{NAMES[cc.name]}."
+ warning += "\n\n#{failure.cause.strip}\n" unless failure.cause.nil?
+ warning += <<-EOS.undent
+
+ We are continuing anyway so if the build succeeds, please open a ticket with
+ the subject
+
+ #{subject}
+
+ so that we can update the formula accordingly. Thanks!
+ EOS
+
+ viable = @compilers.reject { |cc| @f.fails_with? cc }
+ unless viable.empty?
+ warning += "\nIf it fails you can use "
+ options = viable.map { |cc| "--use-#{cc.name}" }
+ warning += "#{options*' or '} to try a different compiler."
+ end
+
+ opoo warning
+ else
+ opoo "Formula will not build with #{NAMES[@old_compiler]}, trying #{NAMES[ENV.compiler]}"
+ end
+ end
+end
diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb
index c8ef58e50..eb1d7dcbe 100644
--- a/Library/Homebrew/formula.rb
+++ b/Library/Homebrew/formula.rb
@@ -5,6 +5,7 @@ require 'hardware'
require 'bottles'
require 'extend/fileutils'
require 'patches'
+require 'compilers'
# Derive and define at least @url, see Library/Formula for examples
class Formula
@@ -156,14 +157,12 @@ class Formula
self.class.keg_only_reason || false
end
- def fails_with_llvm?
- llvm = self.class.fails_with_llvm_reason
- if llvm
- if llvm.build and MacOS.llvm_build_version.to_i > llvm.build.to_i
- false
- else
- llvm
- end
+ def fails_with? cc
+ return false if self.class.cc_failures.nil?
+ cc = Compiler.new(cc) unless cc.is_a? Compiler
+ return self.class.cc_failures.find do |failure|
+ next unless failure.compiler == cc.name
+ failure.build.zero? or failure.build >= cc.build
end
end
@@ -182,8 +181,6 @@ class Formula
validate_variable :name
validate_variable :version
- fails_with_llvm?.handle_failure if fails_with_llvm?
-
stage do
begin
patch
@@ -571,8 +568,8 @@ private
end
attr_rw :version, :homepage, :mirrors, :specs
- attr_rw :keg_only_reason, :fails_with_llvm_reason, :skip_clean_all
- attr_rw :bottle_url, :bottle_sha1
+ attr_rw :keg_only_reason, :skip_clean_all, :bottle_url, :bottle_sha1
+ attr_rw :cc_failures
attr_rw(*CHECKSUM_TYPES)
def head val=nil, specs=nil
@@ -675,8 +672,13 @@ private
@keg_only_reason = KegOnlyReason.new(reason, explanation.to_s.chomp)
end
- def fails_with_llvm msg=nil, data=nil
- @fails_with_llvm_reason = FailsWithLLVM.new(msg, data)
+ def fails_with compiler, &block
+ @cc_failures ||= CompilerFailures.new
+ @cc_failures << if block_given?
+ CompilerFailure.new(compiler, &block)
+ else
+ CompilerFailure.new(compiler)
+ end
end
end
end
diff --git a/Library/Homebrew/formula_support.rb b/Library/Homebrew/formula_support.rb
index df98f3cb4..3bda65dcd 100644
--- a/Library/Homebrew/formula_support.rb
+++ b/Library/Homebrew/formula_support.rb
@@ -69,61 +69,3 @@ EOS
end
end
end
-
-
-# Used to annotate formulae that won't build correctly with LLVM.
-class FailsWithLLVM
- attr_reader :msg, :data, :build
-
- def initialize msg=nil, data=nil
- if msg.nil? or msg.kind_of? Hash
- @msg = "(No specific reason was given)"
- data = msg
- else
- @msg = msg
- end
- @data = data
- @build = data.delete :build rescue nil
- end
-
- def reason
- s = @msg
- s += "Tested with LLVM build #{@build}" unless @build == nil
- s += "\n"
- return s
- end
-
- def handle_failure
- return unless ENV.compiler == :llvm
-
- # version 2336 is the latest version as of Xcode 4.2, so it is the
- # latest version we have tested against so we will switch to GCC and
- # bump this integer when Xcode 4.3 is released. TODO do that!
- if build.to_i >= 2336
- if MacOS.xcode_version < "4.2"
- opoo "Formula will not build with LLVM, using GCC"
- ENV.gcc
- else
- opoo "Formula will not build with LLVM, trying Clang"
- ENV.clang
- end
- return
- end
- opoo "Building with LLVM, but this formula is reported to not work with LLVM:"
- puts
- puts reason
- puts
- puts <<-EOS.undent
- We are continuing anyway so if the build succeeds, please open a ticket with
- the following information: #{MacOS.llvm_build_version}-#{MACOS_VERSION}. So
- that we can update the formula accordingly. Thanks!
- EOS
- puts
- if MacOS.xcode_version < "4.2"
- puts "If it doesn't work you can: brew install --use-gcc"
- else
- puts "If it doesn't work you can try: brew install --use-clang"
- end
- puts
- end
-end
diff --git a/Library/Homebrew/test/test_formula.rb b/Library/Homebrew/test/test_formula.rb
index a819e5751..0735d55d6 100644
--- a/Library/Homebrew/test/test_formula.rb
+++ b/Library/Homebrew/test/test_formula.rb
@@ -3,8 +3,12 @@ require 'testing_env'
require 'extend/ARGV' # needs to be after test/unit to avoid conflict with OptionsParser
ARGV.extend(HomebrewArgvExtension)
+require 'extend/ENV'
+ENV.extend(HomebrewEnvExtension)
+
require 'test/testball'
-require 'utils'
+
+require 'hardware'
class AbstractDownloadStrategy
attr_reader :url
@@ -62,4 +66,67 @@ class FormulaTests < Test::Unit::TestCase
assert_equal f.url, "file:///#{TEST_FOLDER}/bad_url/testball-0.1.tbz"
assert_equal downloader.url, "file:///#{TEST_FOLDER}/tarballs/testball-0.1.tbz"
end
+
+ def test_compiler_selection
+ %W{HOMEBREW_USE_CLANG HOMEBEW_USE_LLVM HOMEBREW_USE_GCC}.each { |e| ENV.delete(e) }
+
+ f = TestAllCompilerFailures.new
+ assert f.fails_with? :clang
+ assert f.fails_with? :llvm
+ assert f.fails_with? :gcc
+ cs = CompilerSelector.new(f)
+ cs.select_compiler
+ assert_equal MacOS.default_compiler, ENV.compiler
+
+ f = TestNoCompilerFailures.new
+ assert !(f.fails_with? :clang)
+ assert !(f.fails_with? :llvm)
+ assert !(f.fails_with? :gcc)
+ cs = CompilerSelector.new(f)
+ cs.select_compiler
+ assert_equal MacOS.default_compiler, ENV.compiler
+
+ f = TestLLVMFailure.new
+ assert !(f.fails_with? :clang)
+ assert f.fails_with? :llvm
+ assert !(f.fails_with? :gcc)
+ cs = CompilerSelector.new(f)
+ cs.select_compiler
+ assert ENV.compiler, case MacOS.clang_build_version
+ when 0..210 then :gcc
+ else :clang
+ end
+
+ f = TestMixedCompilerFailures.new
+ assert f.fails_with? :clang
+ assert !(f.fails_with? :llvm)
+ assert f.fails_with? :gcc
+ cs = CompilerSelector.new(f)
+ cs.select_compiler
+ assert_equal :llvm, ENV.compiler
+
+ f = TestMoreMixedCompilerFailures.new
+ assert !(f.fails_with? :clang)
+ assert f.fails_with? :llvm
+ assert f.fails_with? :gcc
+ cs = CompilerSelector.new(f)
+ cs.select_compiler
+ assert_equal :clang, ENV.compiler
+
+ f = TestEvenMoreMixedCompilerFailures.new
+ assert f.fails_with? :clang
+ assert f.fails_with? :llvm
+ assert !(f.fails_with? :gcc)
+ cs = CompilerSelector.new(f)
+ cs.select_compiler
+ assert_equal :clang, ENV.compiler
+
+ f = TestBlockWithoutBuildCompilerFailure.new
+ assert f.fails_with? :clang
+ assert !(f.fails_with? :llvm)
+ assert !(f.fails_with? :gcc)
+ cs = CompilerSelector.new(f)
+ cs.select_compiler
+ assert_equal MacOS.default_compiler, ENV.compiler
+ end
end
diff --git a/Library/Homebrew/test/testball.rb b/Library/Homebrew/test/testball.rb
index aae18115c..a77d7709c 100644
--- a/Library/Homebrew/test/testball.rb
+++ b/Library/Homebrew/test/testball.rb
@@ -39,3 +39,85 @@ class ConfigureFails <Formula
system "./configure"
end
end
+
+class TestAllCompilerFailures < Formula
+ def initialize name=nil
+ @url="file:///#{TEST_FOLDER}/tarballs/testball-0.1.tbz"
+ @homepage = 'http://example.com/'
+ super "compilerfailures"
+ end
+
+ fails_with :clang
+ fails_with :llvm
+ fails_with :gcc
+end
+
+class TestNoCompilerFailures < Formula
+ def initialize name=nil
+ @url="file:///#{TEST_FOLDER}/tarballs/testball-0.1.tbz"
+ @homepage = 'http://example.com/'
+ super "nocompilerfailures"
+ end
+
+ fails_with(:clang) { build 42 }
+ fails_with(:llvm) { build 42 }
+ fails_with(:gcc) { build 42 }
+end
+
+class TestLLVMFailure < Formula
+ def initialize name=nil
+ @url="file:///#{TEST_FOLDER}/tarballs/testball-0.1.tbz"
+ @homepage = 'http://example.com/'
+ super "llvmfailure"
+ end
+
+ fails_with :llvm
+end
+
+class TestMixedCompilerFailures < Formula
+ def initialize name=nil
+ @url="file:///#{TEST_FOLDER}/tarballs/testball-0.1.tbz"
+ @homepage = 'http://example.com/'
+ super "mixedcompilerfailures"
+ end
+
+ fails_with(:clang) { build MacOS.clang_build_version }
+ fails_with(:llvm) { build 42 }
+ fails_with(:gcc) { build 5666 }
+end
+
+class TestMoreMixedCompilerFailures < Formula
+ def initialize name=nil
+ @url="file:///#{TEST_FOLDER}/tarballs/testball-0.1.tbz"
+ @homepage = 'http://example.com/'
+ super "moremixedcompilerfailures"
+ end
+
+ fails_with(:clang) { build 42 }
+ fails_with(:llvm) { build 2336 }
+ fails_with(:gcc) { build 5666 }
+end
+
+class TestEvenMoreMixedCompilerFailures < Formula
+ def initialize name=nil
+ @url="file:///#{TEST_FOLDER}/tarballs/testball-0.1.tbz"
+ @homepage = 'http://example.com/'
+ super "evenmoremixedcompilerfailures"
+ end
+
+ fails_with :clang
+ fails_with(:llvm) { build 2336 }
+ fails_with(:gcc) { build 5648 }
+end
+
+class TestBlockWithoutBuildCompilerFailure < Formula
+ def initialize name=nil
+ @url="file:///#{TEST_FOLDER}/tarballs/testball-0.1.tbz"
+ @homepage = 'http://example.com/'
+ super "blockwithoutbuildcompilerfailure"
+ end
+
+ fails_with :clang do
+ cause "failure"
+ end
+end
diff --git a/Library/Homebrew/test/testing_env.rb b/Library/Homebrew/test/testing_env.rb
index c0556651c..1e183287a 100644
--- a/Library/Homebrew/test/testing_env.rb
+++ b/Library/Homebrew/test/testing_env.rb
@@ -9,6 +9,7 @@ ABS__FILE__=File.expand_path(__FILE__)
$:.push(File.expand_path(__FILE__+'/../..'))
require 'extend/pathname'
require 'exceptions'
+require 'utils'
# these are defined in global.rb, but we don't want to break our actual
# homebrew tree, and we do want to test everything :)