#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -W0 #TODO make it work with homebrew/dupes/gcc #TODO? If we find -mmacosx-version-min=10.8, change sdkroot? warn visibly if no such SDK? #TODO fix pkg-config files, should point to /usr/local or /usr/local/opt #TODO for easier to understand code, don't monkey-patch ENV, just set via a hash from a helper class #TODO create mechanism to specify build effects like %w{-O0 -O4 vanilla-arg-parsing sdk=10.6} etc. #TODO DSL for lame-env (and rename to typical-env or something better) #TODO consider always setting CC to cc and instead having HOMEBREW_CC to force cc choice in end toolchain # in verbose mode print out things like "gcc called, but redirecting to clang" if that happens #TODO `brew sh`: https://github.com/mxcl/homebrew/issues/14381#issuecomment-8017538 require "#{File.dirname __FILE__}/../libsuperenv" require 'set' def cccfg? flags flags.split('').all?{|c| ENV['HOMEBREW_CCCFG'].include? c } if ENV['HOMEBREW_CCCFG'] end def servile? # we are servile when we are called from configure etc. # * we give the callee the tools it asks for # * we leave ARGV alone # when callee is make we optimize and force HOMEBREW_CC not cccfg? 'O' end def nclt? $sdkroot != nil end def cmake_prefixes @prefixes ||= ENV['CMAKE_PREFIX_PATH'].split(':').reject do |path| case path when '/usr/local' then !nclt? when '/usr', '/', "#$sdkroot/usr" then true end end end class Cmd def initialize path, args @arg0 = path.basename.freeze @args = args.freeze end def mode if @arg0 == 'cpp' or @arg0 == 'ld' @arg0.to_sym elsif @args.include? '-c' :cc elsif @args.include? '-E' :cpp else :ccld end end def tool @tool ||= if servile? or @arg0 == 'ld' @arg0 elsif @arg0.include? '++' if ENV['HOMEBREW_CC'].chuzzle =~ /gcc/ 'g++' else 'clang++' end else ENV['HOMEBREW_CC'].chuzzle or 'clang' end end def args args = if servile? or tool == 'ld' @args.dup else refurbished_args end if tool != 'ld' args.unshift("--sysroot=#$sdkroot") else args.unshift($sdkroot).unshift("-syslibroot") end if nclt? case mode when :cpp %w{-E} + cppflags + args when :ld ldflags + args when :cc cflags + cppflags + args when :ccld cflags + cppflags + ldflags + args end.compact end def refurbished_args iset = Set.new(cmake_prefixes.map{|prefix| "#{prefix}/include" }) lset = Set.new args = [] whittler = @args.each loop do case arg = whittler.next when '-arch', /^-Xarch_/ whittler.next when /^-g\d?/, /^-gstabs\d+/, '-gstabs+', /^-ggdb\d?/, '-gdwarf-2', /^-march=.+/, /^-mtune=.+/, '-m64', '-m32', /^-O[0-9zs]/, '-fast', %r{^-[IL]/opt/local}, %r{^-[IL]/sw}, # no macports/fink %r{^-[IL]/usr/X11}, %r{^-[IL]/opt/X11}, # we add X11 ourselves '-pedantic', '-pedantic-errors' when /^-W.*/ args << arg if arg =~ /^-Wl,/ when '-macosx_version_min', '-dylib_install_name' args << "-Wl,#{arg},#{whittler.next}" when '-dylib' args << "-Wl,#{arg}" when /^-I(.+)/ # it is okay to add a space after the -I; so let's support it path = $1.chuzzle || whittler.next args << "-I#{path}" if iset.add?(path.cleanpath) when /^-l(.+)/ lib = $1.chuzzle || whittler.next args << "-l#{lib}" if lset.add?(lib) else args << arg end end make_fuss(args) args end def cflags if cccfg? 'Ob' %w{-mtune=generic -Oz} elsif cccfg? 'O' u = '-arch i386 -arch x86_64' if cccfg? 'u' c = case tool when 'clang', 'clang++' then '-march=native' end %w{-pipe -w -Os} << u << c else [] end end def ldflags cmake_prefixes.map{|prefix| "#{prefix}/lib" }.to_flags('-L') end def cppflags all = cmake_prefixes.map{|prefix| "#{prefix}/include" } opt = all.select{|prefix| prefix =~ %r{^#$brewfix/opt} } sys = all - opt + ENV['CMAKE_INCLUDE_PATH'].split(':') # we want our keg-only includes to be found before system includes so that # they override the system options. sys.to_flags('-isystem') + opt.to_flags('-I') end def make_fuss args dels = @args - args if ENV['VERBOSE'] adds = args - @args puts "brew: Superenv removed: #{dels*' '}" unless dels.empty? puts "brew: Superenv added: #{adds*' '}" unless adds.empty? else %w{CPPFLAGS LDFLAGS CXXFLAGS CFLAGS}.each do |flag| next unless ENV[flag] flags = dels.select{|del| ENV[flag].include? del }.join(' ') puts "brew: superenv removed `#{flags}' from #{flag}" unless flags.empty? end end end end ####################################################################### sanity abort "The build-tool has reset ENV. --env=std required." unless ENV['HOMEBREW_BREW_FILE'] ######################################################################### main cmd = Cmd.new($0, ARGV) exec "xcrun", cmd.tool, *cmd.args