Replace CompilerQueue with predetermined priority lists

This commit is contained in:
Jack Nagel 2014-09-18 15:50:54 -05:00
parent 288c7974dc
commit 04dae13ae7
4 changed files with 69 additions and 113 deletions

View File

@ -68,74 +68,65 @@ class CompilerFailure
} }
end end
class CompilerQueue
def initialize
@array = []
end
def <<(o)
@array << o
self
end
def pop
@array.delete(@array.max { |a, b| a.priority <=> b.priority })
end
def empty?
@array.empty?
end
end
class CompilerSelector class CompilerSelector
Compiler = Struct.new(:name, :version, :priority) include CompilerConstants
attr_reader :formula Compiler = Struct.new(:name, :version)
def initialize(formula, versions=MacOS) COMPILER_PRIORITY = {
:clang => [:clang, :gcc, :llvm, :gnu, :gcc_4_0],
:gcc => [:gcc, :llvm, :gnu, :clang, :gcc_4_0],
:llvm => [:llvm, :gcc, :gnu, :clang, :gcc_4_0],
:gcc_4_0 => [:gcc_4_0, :gcc, :llvm, :gnu, :clang],
}
def self.select_for(formula)
compilers = COMPILER_PRIORITY.fetch(MacOS.default_compiler)
new(formula, MacOS, compilers).compiler
end
attr_reader :formula, :failures, :versions, :compilers
def initialize(formula, versions, compilers)
@formula = formula @formula = formula
@failures = formula.compiler_failures @failures = formula.compiler_failures
@versions = versions @versions = versions
@compilers = CompilerQueue.new @compilers = compilers
%w{clang llvm gcc gcc_4_0}.map(&:to_sym).each do |cc|
version = @versions.send("#{cc}_build_version")
unless version.nil?
@compilers << Compiler.new(cc, version, priority_for(cc))
end
end
# non-Apple GCC 4.x
CompilerConstants::GNU_GCC_VERSIONS.each do |v|
name = "gcc-4.#{v}"
version = @versions.non_apple_gcc_version(name)
unless version.nil?
# priority is based on version, with newest preferred first
@compilers << Compiler.new(name, version, 1.0 + v/10.0)
end
end
end end
# Attempts to select an appropriate alternate compiler, but
# if none can be found raises CompilerError instead
def compiler def compiler
while cc = @compilers.pop find_compiler { |c| return c.name unless fails_with?(c) }
return cc.name unless fails_with?(cc)
end
raise CompilerSelectionError.new(formula) raise CompilerSelectionError.new(formula)
end end
private private
def fails_with?(compiler) def find_compiler
@failures.any? { |failure| failure === compiler } compilers.each do |compiler|
case compiler
when :gnu
GNU_GCC_VERSIONS.reverse_each do |v|
name = "gcc-4.#{v}"
version = compiler_version(name)
yield Compiler.new(name, version) if version
end
else
version = compiler_version(compiler)
yield Compiler.new(compiler, version) if version
end
end
end end
def priority_for(cc) def fails_with?(compiler)
case cc failures.any? { |failure| failure === compiler }
when :clang then @versions.clang_build_version >= 318 ? 3 : 0.5 end
when :gcc then 2.5
when :llvm then 2 def compiler_version(name)
when :gcc_4_0 then 0.25 case name
when GNU_GCC_REGEXP
versions.non_apple_gcc_version(name)
else
versions.send("#{name}_build_version")
end end
end end
end end

View File

@ -131,7 +131,7 @@ module SharedEnvExtension
# an alternate compiler, altering the value of environment variables. # an alternate compiler, altering the value of environment variables.
# If no valid compiler is found, raises an exception. # If no valid compiler is found, raises an exception.
def validate_cc!(formula) def validate_cc!(formula)
send CompilerSelector.new(formula).compiler send CompilerSelector.select_for(formula)
end end
# Snow Leopard defines an NCURSES value the opposite of most distros # Snow Leopard defines an NCURSES value the opposite of most distros

View File

@ -1,38 +0,0 @@
require 'testing_env'
require 'compilers'
class CompilerQueueTests < Homebrew::TestCase
FakeCompiler = Struct.new(:name, :priority)
def setup
@q = CompilerQueue.new
end
def test_shovel_returns_self
assert_same @q, @q << Object.new
end
def test_empty
assert_empty @q
end
def test_queues_items
a = FakeCompiler.new(:foo, 0)
b = FakeCompiler.new(:bar, 0)
@q << a << b
assert_equal a, @q.pop
assert_equal b, @q.pop
assert_nil @q.pop
end
def test_pops_items_by_priority
a = FakeCompiler.new(:foo, 0)
b = FakeCompiler.new(:bar, 0.5)
c = FakeCompiler.new(:baz, 1)
@q << a << b << c
assert_equal c, @q.pop
assert_equal b, @q.pop
assert_equal a, @q.pop
assert_nil @q.pop
end
end

View File

@ -14,17 +14,18 @@ class CompilerSelectorTests < Homebrew::TestCase
attr_accessor :gcc_4_0_build_version, :gcc_build_version, attr_accessor :gcc_4_0_build_version, :gcc_build_version,
:llvm_build_version, :clang_build_version :llvm_build_version, :clang_build_version
def initialize(versions={}) def initialize
{ @gcc_4_0_build_version = nil
:gcc_4_0_build_version => nil, @gcc_build_version = 5666
:gcc_build_version => 5666, @llvm_build_version = 2336
:llvm_build_version => 2336, @clang_build_version = 425
:clang_build_version => 425,
}.merge(versions).each { |k, v| instance_variable_set("@#{k}", v) }
end end
def non_apple_gcc_version(name) def non_apple_gcc_version(name)
name == "gcc-4.8" ? "4.8.1" : nil case name
when "gcc-4.8" then "4.8.1"
when "gcc-4.7" then "4.7.1"
end
end end
end end
@ -32,14 +33,17 @@ class CompilerSelectorTests < Homebrew::TestCase
@f = Double.new @f = Double.new
@cc = :clang @cc = :clang
@versions = CompilerVersions.new @versions = CompilerVersions.new
@selector = CompilerSelector.new(
@f, @versions, [:clang, :gcc, :llvm, :gnu]
)
end end
def actual_cc def actual_cc
CompilerSelector.new(@f, @versions).compiler @selector.compiler
end end
def test_all_compiler_failures def test_all_compiler_failures
@f << :clang << :llvm << :gcc << { :gcc => "4.8" } @f << :clang << :llvm << :gcc << { :gcc => "4.8" } << { :gcc => "4.7" }
assert_raises(CompilerSelectionError) { actual_cc } assert_raises(CompilerSelectionError) { actual_cc }
end end
@ -68,17 +72,17 @@ class CompilerSelectorTests < Homebrew::TestCase
end end
def test_mixed_failures_1 def test_mixed_failures_1
@f << :clang << :gcc
assert_equal :llvm, actual_cc
end
def test_mixed_failures_2
@f << :clang << :llvm @f << :clang << :llvm
assert_equal :gcc, actual_cc assert_equal :gcc, actual_cc
end end
def test_mixed_failures_2
@f << :gcc << :clang << { :gcc => "4.8" }
assert_equal :llvm, actual_cc
end
def test_mixed_failures_3 def test_mixed_failures_3
@f << :llvm << :gcc @f << :gcc << :llvm
assert_equal :clang, actual_cc assert_equal :clang, actual_cc
end end
@ -87,10 +91,9 @@ class CompilerSelectorTests < Homebrew::TestCase
assert_equal :gcc, actual_cc assert_equal :gcc, actual_cc
end end
def test_older_clang_precedence def test_mixed_failures_5
@versions.clang_build_version = 211 @f << :clang << :gcc << :llvm << { :gcc => "4.8" }
@f << :gcc << { :gcc => "4.8" } assert_equal "gcc-4.7", actual_cc
assert_equal :llvm, actual_cc
end end
def test_llvm_precedence def test_llvm_precedence
@ -100,13 +103,13 @@ class CompilerSelectorTests < Homebrew::TestCase
def test_missing_gcc def test_missing_gcc
@versions.gcc_build_version = nil @versions.gcc_build_version = nil
@f << :clang << :llvm << { :gcc => "4.8" } @f << :clang << :llvm << { :gcc => "4.8" } << { :gcc => "4.7" }
assert_raises(CompilerSelectionError) { actual_cc } assert_raises(CompilerSelectionError) { actual_cc }
end end
def test_missing_llvm_and_gcc def test_missing_llvm_and_gcc
@versions.gcc_build_version = @versions.llvm_build_version = nil @versions.gcc_build_version = @versions.llvm_build_version = nil
@f << :clang << { :gcc => "4.8" } @f << :clang << { :gcc => "4.8" } << { :gcc => "4.7" }
assert_raises(CompilerSelectionError) { actual_cc } assert_raises(CompilerSelectionError) { actual_cc }
end end
end end