Tab: track C++ stdlib in use
There are subtle incompatibilities between Apple's libstdc++ and the libstdc++ used by the various GNU GCC formulae. In addition, we'll likely also be supporting libc++ in the future, and that's also incompatible with the other stdlibs. Tracking it in the tab lets us make sure that dependencies are all built against the same stdlib to avoid subtle breakage.
This commit is contained in:
parent
3ac74331a8
commit
b71682bdc7
@ -170,7 +170,8 @@ class Build
|
||||
|
||||
begin
|
||||
f.install
|
||||
Tab.create(f, Options.coerce(ARGV.options_only)).write
|
||||
Tab.create(f, ENV.compiler,
|
||||
Options.coerce(ARGV.options_only)).write
|
||||
rescue Exception => e
|
||||
if ARGV.debug?
|
||||
debrew e, f
|
||||
|
||||
44
Library/Homebrew/cxxstdlib.rb
Normal file
44
Library/Homebrew/cxxstdlib.rb
Normal file
@ -0,0 +1,44 @@
|
||||
class CxxStdlib
|
||||
attr_accessor :type, :compiler
|
||||
|
||||
def initialize(type, compiler)
|
||||
if ![:libstdcxx, :libcxx].include? type
|
||||
raise ArgumentError, "Invalid C++ stdlib type: #{type}"
|
||||
end
|
||||
|
||||
@type = type.to_sym
|
||||
@compiler = compiler.to_sym
|
||||
end
|
||||
|
||||
def apple_compiler?
|
||||
not compiler.to_s =~ SharedEnvExtension::GNU_GCC_REGEXP
|
||||
end
|
||||
|
||||
def compatible_with?(other)
|
||||
# libstdc++ and libc++ aren't ever intercompatible
|
||||
return false unless type == other.type
|
||||
|
||||
# libstdc++ is compatible across Apple compilers, but
|
||||
# not between Apple and GNU compilers, or between GNU compiler versions
|
||||
return false if apple_compiler? && !other.apple_compiler?
|
||||
if compiler.to_s =~ SharedEnvExtension::GNU_GCC_REGEXP
|
||||
return false unless other.compiler.to_s =~ SharedEnvExtension::GNU_GCC_REGEXP
|
||||
return false unless compiler.to_s[4..6] == other.compiler.to_s[4..6]
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def check_dependencies(formula, deps)
|
||||
deps.each do |dep|
|
||||
dep_stdlib = Tab.for_formula(dep.to_formula).cxxstdlib
|
||||
if !compatible_with? dep_stdlib
|
||||
raise IncompatibleCxxStdlibs.new(formula, dep, dep_stdlib, self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def type_string
|
||||
type.to_s.gsub(/cxx$/, 'c++')
|
||||
end
|
||||
end
|
||||
@ -1,3 +1,4 @@
|
||||
require 'cxxstdlib'
|
||||
require 'ostruct'
|
||||
require 'options'
|
||||
require 'utils/json'
|
||||
@ -9,7 +10,7 @@ require 'utils/json'
|
||||
class Tab < OpenStruct
|
||||
FILENAME = 'INSTALL_RECEIPT.json'
|
||||
|
||||
def self.create f, args
|
||||
def self.create f, stdlib, compiler, args
|
||||
f.build.args = args
|
||||
|
||||
sha = HOMEBREW_REPOSITORY.cd do
|
||||
@ -23,7 +24,9 @@ class Tab < OpenStruct
|
||||
:poured_from_bottle => false,
|
||||
:tapped_from => f.tap,
|
||||
:time => Time.now.to_i, # to_s would be better but Ruby has no from_s function :P
|
||||
:HEAD => sha
|
||||
:HEAD => sha,
|
||||
:compiler => compiler,
|
||||
:stdlib => stdlib
|
||||
end
|
||||
|
||||
def self.from_file path
|
||||
@ -59,7 +62,9 @@ class Tab < OpenStruct
|
||||
:poured_from_bottle => false,
|
||||
:tapped_from => "",
|
||||
:time => nil,
|
||||
:HEAD => nil
|
||||
:HEAD => nil,
|
||||
:stdlib => :libstdcxx,
|
||||
:compiler => :clang
|
||||
end
|
||||
|
||||
def with? name
|
||||
@ -92,6 +97,13 @@ class Tab < OpenStruct
|
||||
used_options + unused_options
|
||||
end
|
||||
|
||||
def cxxstdlib
|
||||
# Older tabs won't have these values, so provide sensible defaults
|
||||
lib = stdlib || :libstdcxx
|
||||
cc = compiler || MacOS.default_compiler
|
||||
CxxStdlib.new(lib.to_sym, cc.to_sym)
|
||||
end
|
||||
|
||||
def to_json
|
||||
Utils::JSON.dump({
|
||||
:used_options => used_options.map(&:to_s),
|
||||
@ -100,7 +112,9 @@ class Tab < OpenStruct
|
||||
:poured_from_bottle => poured_from_bottle,
|
||||
:tapped_from => tapped_from,
|
||||
:time => time,
|
||||
:HEAD => send("HEAD")})
|
||||
:HEAD => send("HEAD"),
|
||||
:stdlib => (stdlib.to_s if stdlib),
|
||||
:compiler => (compiler.to_s if compiler)})
|
||||
end
|
||||
|
||||
def write
|
||||
|
||||
62
Library/Homebrew/test/test_stdlib.rb
Normal file
62
Library/Homebrew/test/test_stdlib.rb
Normal file
@ -0,0 +1,62 @@
|
||||
require 'testing_env'
|
||||
require 'test/testball'
|
||||
require 'formula'
|
||||
require 'cxxstdlib'
|
||||
require 'tab'
|
||||
|
||||
class CxxStdlibTests < Test::Unit::TestCase
|
||||
def setup
|
||||
@clang = CxxStdlib.new(:libstdcxx, :clang)
|
||||
@gcc = CxxStdlib.new(:libstdcxx, :gcc)
|
||||
@llvm = CxxStdlib.new(:libstdcxx, :llvm)
|
||||
@gcc4 = CxxStdlib.new(:libstdcxx, :gcc_4_0)
|
||||
@gcc48 = CxxStdlib.new(:libstdcxx, 'gcc-4.8')
|
||||
@gcc49 = CxxStdlib.new(:libstdcxx, 'gcc-4.9')
|
||||
@lcxx = CxxStdlib.new(:libcxx, :clang)
|
||||
end
|
||||
|
||||
def test_apple_libstdcxx_intercompatibility
|
||||
assert @clang.compatible_with?(@gcc)
|
||||
assert @clang.compatible_with?(@llvm)
|
||||
assert @clang.compatible_with?(@gcc4)
|
||||
end
|
||||
|
||||
def test_compatibility_same_compilers_and_type
|
||||
assert @gcc48.compatible_with?(@gcc48)
|
||||
assert @clang.compatible_with?(@clang)
|
||||
end
|
||||
|
||||
def test_apple_gnu_libstdcxx_incompatibility
|
||||
assert !@clang.compatible_with?(@gcc48)
|
||||
assert !@gcc48.compatible_with?(@clang)
|
||||
end
|
||||
|
||||
def test_gnu_cross_version_incompatibility
|
||||
assert !@clang.compatible_with?(@gcc48)
|
||||
assert !@gcc48.compatible_with?(@clang)
|
||||
end
|
||||
|
||||
def test_libstdcxx_libcxx_incompatibility
|
||||
assert !@clang.compatible_with?(@lcxx)
|
||||
assert !@lcxx.compatible_with?(@clang)
|
||||
end
|
||||
|
||||
def test_apple_compiler_reporting
|
||||
assert @clang.apple_compiler?
|
||||
assert @gcc.apple_compiler?
|
||||
assert @llvm.apple_compiler?
|
||||
assert @gcc4.apple_compiler?
|
||||
assert !@gcc48.apple_compiler?
|
||||
end
|
||||
|
||||
def test_type_string_formatting
|
||||
assert_equal @clang.type_string, 'libstdc++'
|
||||
assert_equal @lcxx.type_string, 'libc++'
|
||||
end
|
||||
|
||||
def test_constructing_from_tab
|
||||
stdlib = Tab.dummy_tab.cxxstdlib
|
||||
assert_equal stdlib.compiler, :clang
|
||||
assert_equal stdlib.type, :libstdcxx
|
||||
end
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user