Skip to content
This repository
tree: 34c0484368
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

executable file 188 lines (179 sloc) 5.332 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
#!/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 create mechanism to specify build effects like %w{-O0 -O4 vanilla-arg-parsing sdk=10.6} etc.

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 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'
      :cc
    elsif @args.include? '-E'
      :cpp
    else
      :ccld
    end
  end
  def tool
    @tool ||= case @arg0
    when 'ld' then 'ld'
    when 'cc', 'c99', 'c89'
      # Ideally we would run `cx9`, however these tools are POSIX compliant
      # and don't support many flags. We need -isystem for instance, but also
      # reliability is generally much higher if we just get clang/gcc to do
      # the work since Makefiles are dumb and include a lot of excess flags.
      ENV['HOMEBREW_CC']
    when 'c++'
      if ENV['HOMEBREW_CC'] =~ /gcc/
        'g++'
      else
        'clang++'
      end
    else
      @arg0
    end
  end
  def args
    args = if not cccfg? 'O' 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
    lset = Set.new(syslibpath)
    iset = Set.new(syscpath)

    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',
           '-pedantic', '-pedantic-errors'
      when '-fopenmp', '-lgomp'
        # clang doesn't support OpenMP
        args << arg if not tool =~ /^clang/
      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(.+)/
        path = $1.chuzzle || whittler.next
        case path.cleanpath
        when %r{^/opt}, %r{^/sw}, %r{/usr/X11}
          # NOOP
        else
          args << "-L#{path}" if lset.add?(path.cleanpath)
        end
      else
        args << arg
      end
    end
    make_fuss(args)
    args
  end
  def cflags
    if cccfg? 'Ob'
      %w{-mtune=generic -Oz}
    elsif cccfg? 'O'
      args = %w{-pipe -w -Os}
      args << '-march=native' if tool =~ /clang/
      args += %w{-arch i386 -arch x86_64} if cccfg? 'u'
      args << "--std=#{@arg0}" if @arg0 =~ /c[89]9/
      args
    else
      []
    end
  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{ "#$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
    libpath.to_flags('-L')
  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
    dels = @args - args
    adds = args - @args
    dups = dels & args
    puts "brew: Superenv removed: #{dels*' '}" unless dels.empty?
    puts "brew: Superenv deduped: #{dels}" unless dups.empty?
    puts "brew: Superenv added: #{adds*' '}" unless adds.empty?
  end
end

####################################################################### 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
cmd = Cmd.new($0, ARGV)
exec "xcrun", cmd.tool, *cmd.args
Something went wrong with that request. Please try again.