#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -W0 require "#{File.dirname __FILE__}/../libsuperenv" require 'set' require 'stringio' class Logger def initialize @io = StringIO.new end def puts(*args) @io.puts(*args) end def log! return unless ENV.key? 'HOMEBREW_CC_LOG_PATH' path = "#{ENV['HOMEBREW_CC_LOG_PATH']}.cc" puts File.open(path, File::WRONLY | File::APPEND | File::CREAT) { |f| f.write(@io.string) } end end LOGGER = Logger.new def cccfg? flags flags.split('').all?{|c| ENV['HOMEBREW_CCCFG'].include? c } if ENV['HOMEBREW_CCCFG'] end def nclt? $sdkroot != nil end def syspath if nclt? %W{#$sdkroot/usr #$sdkroot/usr/local} else %W{/usr /usr/local} 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' if @arg0 =~ /(?:c|g|clang)\+\+/ :cxx else :cc end elsif @args.include? '-E' :ccE else if @arg0 =~ /(?:c|g|clang)\+\+/ :cxxld else :ccld end end end def tool @tool ||= case @arg0 when 'ld' then 'ld' when 'cpp' then 'cpp' when /\w\+\+(-\d\.\d)?$/ case ENV['HOMEBREW_CC'] when /clang/ 'clang++' when /llvm-gcc/ 'llvm-g++-4.2' when /gcc(-\d\.\d)?$/ 'g++' + $1.to_s end else # Note that this is a universal fallback, so that we'll always invoke # HOMEBREW_CC regardless of what name under which the tool was invoked. ENV['HOMEBREW_CC'] end end def args args = if not cccfg? 'O' or tool == 'ld' @args.dup else refurbished_args end if tool != 'ld' args << "--sysroot=#$sdkroot" else args << "-syslibroot" << $sdkroot end if nclt? allflags = case mode when :ccld cflags + args + cppflags + ldflags when :cxxld cxxflags + args + cppflags + ldflags when :cc cflags + args + cppflags when :cxx cxxflags + args + cppflags when :ccE args + cppflags when :cpp args + cppflags when :ld ldflags + args end.compact make_fuss(allflags) allflags end def refurbished_args lset = Set.new(libpath + syslibpath) iset = Set.new(cpath.flatten) args = [] whittler = @args.each loop do case arg = whittler.next when '-arch', /^-Xarch_/ whittler.next when '-m32' # If ENV.m32 was set, we allow the "-m32" flag, but we don't add anything args << '-m32' if cccfg? '3' when /^-g\d?/, /^-gstabs\d+/, '-gstabs+', /^-ggdb\d?/, '-gdwarf-2', /^-march=.+/, /^-mtune=.+/, /^-mcpu=.+/, '-m64', /^-O[0-9zs]?$/, '-fast', '-no-cpp-precomp', '-pedantic', '-pedantic-errors' when '-fopenmp', '-lgomp' # clang doesn't support OpenMP args << arg if not tool =~ /^clang/ when /^-W.*/ args << arg if arg =~ /^-W[alp],/ or arg =~ /^-Wno-/ when '-macosx_version_min', '-dylib_install_name' args << "-Wl,#{arg},#{whittler.next}" when /^-isysroot/ # We set the sysroot 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(.+)/ path = $1.chuzzle || whittler.next doit = case path.cleanpath when %r{^#$brewfix} # maybe homebrew is installed to /sw or /opt/brew true when %r{^/opt}, %r{^/sw}, %r{/usr/X11} false else true end args << "-L#{path}" if doit and lset.add?(path.cleanpath) else args << arg end end args end def cflags args = [] return args unless cccfg? 'O' or configure? args << '-pipe' args << '-w' unless configure? args.concat(optflags) args.concat(archflags) args << "-std=#{@arg0}" if @arg0 =~ /c[89]9/ args end def cxxflags args = cflags args << '-std=c++11' if cccfg? 'x' args << '-stdlib=libc++' if cccfg? 'g' args << '-stdlib=libstdc++' if cccfg? 'h' args end def optflags args = [] args << "-#{ENV['HOMEBREW_OPTIMIZATION_LEVEL']}" args.concat ENV['HOMEBREW_OPTFLAGS'].split(' ') if ENV['HOMEBREW_OPTFLAGS'] args end def archflags args = [] args.concat ENV['HOMEBREW_ARCHFLAGS'].split(' ') if cccfg? 'u' args end def syslibpath # We reject brew's lib as we explicitly add this as a -L flag, thus it # is given higher priority by cc, so it surpasses the system libpath. # NOTE this only counts if Homebrew is installed at /usr/local syspath.map{|d| "#{d}/lib" }.reject{|d| d == "#$brewfix/lib" } end def syscpath isystem, _ = cpath isystem + syspath.map{|d| "#{d}/include" } end def cpath cpath = ENV['CMAKE_PREFIX_PATH'].split(':').map{|d| "#{d}/include" } + ENV['CMAKE_INCLUDE_PATH'].split(':') opt = cpath.select{|prefix| prefix =~ %r{^#$brewfix/opt} } sys = cpath - opt [sys, opt] end def libpath ENV['CMAKE_PREFIX_PATH'].split(':').map{|d| "#{d}/lib" } + ENV['CMAKE_LIBRARY_PATH'].split(':') - syslibpath end def ldflags args = libpath.to_flags('-L') case mode when :ld then args << '-headerpad_max_install_names' when :ccld then args << '-Wl,-headerpad_max_install_names' when :cxxld args << '-Wl,-headerpad_max_install_names' args << '-stdlib=libc++' if cccfg? 'g' args << '-stdlib=libstdc++' if cccfg? 'h' end args end def cppflags sys, opt = cpath # we want our keg-only includes to be found before system includes *and* # before any other includes the build-system adds sys.to_flags('-isystem') + opt.to_flags('-I') end def make_fuss args return unless make_fuss? dels = @args - args adds = args - @args dups = dels & args LOGGER.puts "superenv removed: #{dels*' '}" unless dels.empty? LOGGER.puts "superenv deduped: #{dups}" unless dups.empty? LOGGER.puts "superenv added: #{adds*' '}" unless adds.empty? end def make_fuss? cccfg? 'O' and not configure? end def configure? # configure scripts generated with autoconf 2.61 or later export as_nl ENV.key? 'as_nl' end end if __FILE__ == $PROGRAM_NAME ##################################################################### sanity abort "The build-tool has reset ENV. --env=std required." unless ENV['HOMEBREW_BREW_FILE'] case ENV['HOMEBREW_CC'].chuzzle when 'cc', nil # those values are not allowed ENV['HOMEBREW_CC'] = 'clang' end ####################################################################### main LOGGER.puts "#{File.basename($0)} called with: #{ARGV.join(" ")}" cmd = Cmd.new($0, ARGV) tool, args = cmd.tool, cmd.args LOGGER.puts "superenv executed: #{tool} #{args.join(" ")}" LOGGER.log! exec "xcrun", tool, *args end