361 lines
8.8 KiB
Ruby
Executable File
361 lines
8.8 KiB
Ruby
Executable File
#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -W0
|
|
|
|
$:.unshift "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8"
|
|
require 'pathname'
|
|
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
|
|
|
|
class Cmd
|
|
attr_reader :brewfix, :brewtmp, :sdkroot
|
|
|
|
def initialize path, args
|
|
@arg0 = File.basename(path).freeze
|
|
@args = args.freeze
|
|
@brewfix = ENV['HOMEBREW_PREFIX']
|
|
@brewtmp = ENV['HOMEBREW_TEMP']
|
|
@sdkroot = ENV['HOMEBREW_SDKROOT']
|
|
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
|
|
if @args.length == 1 and @args[0] == '-v'
|
|
# Don't add linker arguments if -v passed as sole option. This stops gcc
|
|
# -v with no other arguments from outputting a linker error. Some
|
|
# software uses gcc -v (wrongly) to sniff the GCC version.
|
|
return @args.dup
|
|
end
|
|
if !cccfg?("O") || tool == "ld" || configure?
|
|
args = @args.dup
|
|
else
|
|
args = refurbished_args
|
|
end
|
|
|
|
if sdkroot
|
|
if tool == "ld"
|
|
args << "-syslibroot" << sdkroot
|
|
else
|
|
args << "--sysroot=#{sdkroot}"
|
|
end
|
|
end
|
|
|
|
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 = []
|
|
enum = @args.each
|
|
|
|
loop do
|
|
case arg = enum.next
|
|
when "-arch"
|
|
if cccfg?("K")
|
|
args << arg << enum.next
|
|
else
|
|
enum.next
|
|
end
|
|
when "-m32"
|
|
args << arg if cccfg?("3") || cccfg?("K")
|
|
when "-m64"
|
|
args << arg if cccfg?("K")
|
|
when /^-Xarch_/
|
|
refurbished = refurbish_arg(enum.next, enum)
|
|
unless refurbished.empty?
|
|
args << arg
|
|
args += refurbished
|
|
end
|
|
else
|
|
args += refurbish_arg(arg, enum)
|
|
end
|
|
end
|
|
|
|
args
|
|
end
|
|
|
|
def refurbish_arg(arg, enum)
|
|
args = []
|
|
|
|
case arg
|
|
when /^-g\d?/, /^-gstabs\d+/, '-gstabs+', /^-ggdb\d?/, '-gdwarf-2',
|
|
/^-march=.+/, /^-mtune=.+/, /^-mcpu=.+/,
|
|
/^-O[0-9zs]?$/, '-fast', '-no-cpp-precomp',
|
|
'-pedantic', '-pedantic-errors'
|
|
when '-fopenmp', '-lgomp', '-mno-fused-madd', '-fforce-addr', '-fno-defer-pop',
|
|
'-mno-dynamic-no-pic', '-fearly-inlining', '-finline-functions-called-once',
|
|
/^-finline-limit/, /^-f(?:no-)?check-new/, '-fno-delete-null-pointer-checks',
|
|
'-fcaller-saves', '-fthread-jumps', '-fno-reorder-blocks', '-fcse-skip-blocks',
|
|
'-frerun-cse-after-loop', '-frerun-loop-opt', '-fcse-follow-jumps',
|
|
'-fno-regmove', '-fno-for-scope'
|
|
# clang doesn't support these flags
|
|
args << arg if not tool =~ /^clang/
|
|
when /^-W[alp],/, /^-Wno-/
|
|
args << arg
|
|
when /^-W.*/
|
|
# prune warnings
|
|
when '-macosx_version_min', '-dylib_install_name'
|
|
args << "-Wl,#{arg},#{enum.next}"
|
|
when '-multiply_definedsuppress'
|
|
args << "-Wl,-multiply_defined,suppress"
|
|
when '-undefineddynamic_lookup'
|
|
args << "-Wl,-undefined,dynamic_lookup"
|
|
when /^-isysroot/, /^--sysroot/
|
|
# We set the sysroot
|
|
enum.next
|
|
when '-dylib'
|
|
args << "-Wl,#{arg}"
|
|
when /^-I(.+)?/
|
|
# Support both "-Ifoo" (one argument) and "-I foo" (two arguments)
|
|
val = chuzzle($1) || enum.next
|
|
path = canonical_path(val)
|
|
args << "-I#{val}" if keep?(path) && @iset.add?(path)
|
|
when /^-L(.+)?/
|
|
val = chuzzle($1) || enum.next
|
|
path = canonical_path(val)
|
|
args << "-L#{val}" if keep?(path) && @lset.add?(path)
|
|
else
|
|
args << arg
|
|
end
|
|
|
|
args
|
|
end
|
|
|
|
def keep? path
|
|
case path
|
|
when %r{^#{Regexp.escape(brewfix)}}o, %r{^#{Regexp.escape(brewtmp)}}o
|
|
# maybe homebrew is installed to /sw or /opt/brew
|
|
true
|
|
when %r{^/opt}, %r{^/sw}, %r{/usr/X11}
|
|
false
|
|
else
|
|
true
|
|
end
|
|
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
|
|
ENV["HOMEBREW_ARCHFLAGS"].split(" ")
|
|
end
|
|
|
|
def syspath
|
|
if sdkroot
|
|
%W{#{sdkroot}/usr /usr/local}
|
|
else
|
|
%W{/usr /usr/local}
|
|
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.reject { |d| d == brewfix }.map! { |d| File.join(d, "lib") }
|
|
end
|
|
|
|
# Paths added as "-isystem<path>" and "-I<path>" flags
|
|
def cpath
|
|
cpath = path_split("CMAKE_PREFIX_PATH").map! { |d| File.join(d, "include") }
|
|
cpath += path_split("CMAKE_INCLUDE_PATH")
|
|
opt = cpath.grep(%r{^#{Regexp.escape(brewfix)}/opt}o)
|
|
sys = cpath - opt
|
|
[sys, opt]
|
|
end
|
|
|
|
# Paths added as "-L<path>" flags
|
|
def libpath
|
|
libpath = path_split("CMAKE_PREFIX_PATH").map! { |d| File.join(d, "lib") }
|
|
libpath += path_split("CMAKE_LIBRARY_PATH")
|
|
libpath -= syslibpath
|
|
libpath
|
|
end
|
|
|
|
def ldflags
|
|
args = path_flags("-L", libpath)
|
|
case mode
|
|
when :ld
|
|
args << "-headerpad_max_install_names"
|
|
when :ccld, :cxxld
|
|
args << "-Wl,-headerpad_max_install_names"
|
|
end
|
|
args
|
|
end
|
|
|
|
# Keg-only opt paths get the "-I" treatment since it has higher priority than
|
|
# "-isystem", and we want them to be searched before system directories as
|
|
# well as any directories added by the build system.
|
|
def cppflags
|
|
sys, opt = cpath
|
|
path_flags("-isystem", sys) + path_flags("-I", opt)
|
|
end
|
|
|
|
def make_fuss args
|
|
return unless make_fuss?
|
|
|
|
dels = @args - args
|
|
adds = args - @args
|
|
|
|
LOGGER.puts "superenv removed: #{dels*' '}" unless dels.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
|
|
|
|
def cccfg? flags
|
|
flags.split('').all?{|c| ENV['HOMEBREW_CCCFG'].include? c } if ENV['HOMEBREW_CCCFG']
|
|
end
|
|
|
|
def canonical_path(path)
|
|
path = Pathname.new(path)
|
|
path = path.realpath if path.exist?
|
|
path.to_s
|
|
end
|
|
|
|
def path_flags(prefix, paths)
|
|
paths = paths.uniq.select { |path| File.directory?(path) }
|
|
paths.map! { |path| prefix + path }
|
|
end
|
|
|
|
def path_split(key)
|
|
ENV.fetch(key) { "" }.split(File::PATH_SEPARATOR)
|
|
end
|
|
|
|
def chuzzle(val)
|
|
return val if val.nil?
|
|
val = val.chomp
|
|
return val unless val.empty?
|
|
end
|
|
end
|
|
|
|
if __FILE__ == $PROGRAM_NAME
|
|
##################################################################### sanity
|
|
abort "The build-tool has reset ENV. --env=std required." unless ENV['HOMEBREW_BREW_FILE']
|
|
|
|
if (cc = ENV["HOMEBREW_CC"]).nil? || cc.empty? || cc == "cc"
|
|
# 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
|