aboutsummaryrefslogtreecommitdiffstats
path: root/Library/ENV/4.3/cc
blob: d4df6d90f4e3657a03516e07b76e222a8bcb41b1 (plain)
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#!/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

module ExecLogExtension
  def exec *args
    path = File.expand_path('~/Library/Logs/Homebrew/cc.log')
    open(path, 'a') do |f|
      f.print '[', $0
      f.print " -%s" % ENV['HOMEBREW_CCCFG'] if ENV['HOMEBREW_CCCFG']
      f.print '] '
      f.puts args.join(' ')
      f.puts
    end
    Kernel.exec *args
  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'
      :ccE
    else
      :ccld
    end
  end
  def tool
    @tool ||= case @arg0
    when 'ld' then 'ld'
    when 'cpp' then 'cpp'
    when /\w\+\+$/
      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 :cc
      cflags + args + cppflags
    when :ccE
      args + cppflags
    when :cpp
      args + cppflags
    when :ld
      ldflags + args
    end.compact
    make_fuss(allflags) if verbose?
    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',
           '-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],/
      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
    return [] unless cccfg? 'O'

    args = %w{-pipe -w -Os}

    # When bottling use the oldest supported CPU type.
    if cccfg? 'bc'
      # Custom bottle specified during the build
      args << ENV['HOMEBREW_ARCHFLAGS']
    elsif cccfg? 'bi6'
      args << '-march=core2'
    elsif cccfg? 'bi'
      args << '-march=prescott'
    elsif cccfg? 'bpA'
      args << '-mcpu=7400'
    elsif cccfg? 'bp'
      args << '-mcpu=750'
    else
      args << '-march=native' if tool =~ /clang/
    end

    ENV['HOMEBREW_ARCHS'].split(',').each { |a| args << "-arch" << a } if cccfg? 'u'
    args << "-std=#{@arg0}" if @arg0 =~ /c[89]9/
    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'
    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
    dels = @args - args
    adds = args - @args
    dups = dels & args

    puts "brew: superenv removed: #{dels*' '}" unless dels.empty?
    puts "brew: superenv deduped: #{dups}" unless dups.empty?
    puts "brew: superenv added: #{adds*' '}" unless adds.empty?
  end
  def verbose?
    !ENV['VERBOSE'].nil? || !ENV['HOMEBREW_VERBOSE'].nil?
  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
  extend(ExecLogExtension) if ENV['HOMEBREW_LOG']
  cmd = Cmd.new($0, ARGV)
  exec "xcrun", cmd.tool, *cmd.args
end