478 lines
12 KiB
Plaintext
Raw Normal View History

#!/bin/sh
# Make sure this shim uses the same Ruby interpreter that is used by Homebrew.
if [[ -z "${HOMEBREW_RUBY_PATH}" ]]
then
echo "${0##*/}: The build tool has reset ENV; --env=std required." >&2
exit 1
fi
exec "${HOMEBREW_RUBY_PATH}" --enable-frozen-string-literal --disable=gems,did_you_mean,rubyopt -x "$0" "$@"
#!/usr/bin/env ruby -W0
require "pathname"
require "set"
2021-01-12 13:44:57 -08:00
require "English"
2018-05-28 11:54:16 -07:00
def mac?
RUBY_PLATFORM[/darwin/]
end
def high_sierra_or_later?
mac? && ENV["HOMEBREW_MACOS_VERSION_NUMERIC"].to_s.to_i >= 101300
end
2018-05-28 11:54:16 -07:00
def linux?
RUBY_PLATFORM[/linux/]
end
class Cmd
CXX_REGEX = /(?:c|g|clang)\+\+/.freeze
attr_reader :config, :prefix, :cellar, :opt, :cachedir, :tmpdir, :sysroot, :deps
attr_reader :archflags, :optflags, :keg_regex, :formula_prefix
2013-11-21 14:50:35 -06:00
2015-02-12 19:13:03 -05:00
def initialize(arg0, args)
@arg0 = arg0
@args = args.freeze
2021-01-08 12:15:55 -08:00
@config = ENV.fetch("HOMEBREW_CCCFG", "")
@prefix = ENV["HOMEBREW_PREFIX"]
@cellar = ENV["HOMEBREW_CELLAR"]
@cachedir = ENV["HOMEBREW_CACHE"]
@opt = ENV["HOMEBREW_OPT"]
@tmpdir = ENV["HOMEBREW_TEMP"]
@sysroot = ENV["HOMEBREW_SDKROOT"]
2021-01-08 12:15:55 -08:00
@archflags = ENV.fetch("HOMEBREW_ARCHFLAGS", "").split
@optflags = ENV.fetch("HOMEBREW_OPTFLAGS", "").split
@deps = Set.new(ENV.fetch("HOMEBREW_DEPENDENCIES", "").split(","))
@formula_prefix = ENV["HOMEBREW_FORMULA_PREFIX"]
# matches opt or cellar prefix and formula name
2021-01-08 12:15:55 -08:00
@keg_regex = %r{(#{Regexp.escape(opt)}|#{Regexp.escape(cellar)})/([\w+-.@]+)}
end
2014-04-21 00:17:22 -05:00
def mode
2018-05-28 11:54:16 -07:00
if @arg0 == "cpp"
:cpp
elsif ["ld", "ld.gold", "gold"].include? @arg0
:ld
elsif @args.include? "-c"
if CXX_REGEX.match?(@arg0)
:cxx
else
:cc
end
elsif @args.include?("-xc++-header") || @args.each_cons(2).include?(["-x", "c++-header"])
:cxx
elsif @args.include? "-E"
:ccE
elsif CXX_REGEX.match?(@arg0)
2021-01-08 12:15:55 -08:00
:cxxld
else
2021-01-08 12:15:55 -08:00
:ccld
end
end
2014-04-21 00:17:22 -05:00
def tool
@tool ||= case @arg0
when "ld" then "ld"
2018-05-28 11:54:16 -07:00
when "gold", "ld.gold" then "ld.gold"
when "cpp" then "cpp"
when /llvm_(clang(\+\+)?)/
2021-01-08 12:15:55 -08:00
"#{ENV["HOMEBREW_PREFIX"]}/opt/llvm/bin/#{Regexp.last_match(1)}"
2020-05-01 01:31:57 +10:00
when /\w\+\+(-\d+(\.\d)?)?$/
case ENV["HOMEBREW_CC"]
when /clang/
"clang++"
when /llvm-gcc/
"llvm-g++-4.2"
2020-05-01 01:31:57 +10:00
when /(g)?cc(-\d+(\.\d)?)?$/
2021-01-08 12:15:55 -08:00
"g++#{Regexp.last_match(2)}"
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.
if ENV["HOMEBREW_CC"] == "llvm_clang"
"#{ENV["HOMEBREW_PREFIX"]}/opt/llvm/bin/clang"
else
ENV["HOMEBREW_CC"]
end
end
end
2014-04-21 00:17:22 -05:00
def args
if @args.length == 1 && @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
args = if !refurbish_args? || mode == :ld || configure?
2021-01-08 12:15:55 -08:00
@args.dup
else
2021-01-08 12:15:55 -08:00
refurbished_args
end
if sysroot
2014-04-21 00:17:22 -05:00
if tool == "ld"
args << "-syslibroot" << sysroot
2014-04-21 00:17:22 -05:00
else
args << "-isysroot#{sysroot}" << "--sysroot=#{sysroot}"
end
end
2015-02-10 20:27:26 -05:00
case mode
when :ccld
2013-08-31 10:38:18 -05:00
cflags + args + cppflags + ldflags
when :cxxld
cxxflags + args + cppflags + ldflags
when :cc
2013-08-31 10:38:18 -05:00
cflags + args + cppflags
when :cxx
cxxflags + args + cppflags
when :ccE
args + cppflags
when :cpp
args + cppflags
when :ld
ldflags + args
2015-02-12 20:09:31 -05:00
end
end
2014-04-21 00:17:22 -05:00
def refurbished_args
@lset = Set.new(library_paths + system_library_paths)
@iset = Set.new(isystem_paths + include_paths)
args = []
2014-05-06 15:21:01 -05:00
enum = @args.each
loop do
case arg = enum.next
when "-arch"
if permit_arch_flags?
args << arg << enum.next
else
enum.next
end
2014-05-22 09:18:34 -05:00
when "-m32", "-m64"
args << arg if permit_arch_flags?
when /^-Xarch_/
refurbished = refurbish_arg(enum.next, enum)
unless refurbished.empty?
args << arg
args += refurbished
end
else
args += refurbish_arg(arg, enum)
end
2014-05-06 15:21:01 -05:00
end
args
end
def refurbish_arg(arg, enum)
args = []
case arg
when /^-g\d?$/, /^-gstabs\d+/, "-gstabs+", /^-ggdb\d?/,
/^-mtune=.+/, /^-mcpu=.+/, /^-O[0-9zs]?$/,
"-fast", "-no-cpp-precomp", "-pedantic",
"-pedantic-errors", "-Wno-long-double",
"-Wno-unused-but-set-variable"
when /^-march=.+/
args << arg if runtime_cpu_detection?
when "-fopenmp", "-lgomp", "-mno-fused-madd", "-fforce-addr", "-fno-defer-pop",
"-mno-dynamic-no-pic", "-fearly-inlining", /^-f(?:no-)?inline-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", "-fno-tree-pre", "-fno-tree-dominator-opts",
"-fuse-linker-plugin", "-frounding-math"
2014-05-06 15:21:01 -05:00
# clang doesn't support these flags
args << arg unless tool =~ /^clang/
when "-Xpreprocessor", "-Xclang"
# used for -Xpreprocessor -fopenmp
args << arg << enum.next
2020-06-23 17:10:07 +01:00
when /-mmacosx-version-min=(\d+)\.(\d+)/
2021-01-08 12:15:55 -08:00
if high_sierra_or_later? && Regexp.last_match(1) == "10" && Regexp.last_match(2).to_i < 9
arg = "-mmacosx-version-min=10.9"
end
args << arg
when "--fast-math"
2021-01-08 12:15:55 -08:00
arg = "-ffast-math" if /^clang/.match?(tool)
args << arg
when "-Wno-deprecated-register"
# older gccs don't support these flags
args << arg unless tool =~ /^g..-4.[02]/
when /^-Wl,-z,defs/
# -Wl,-undefined,error is already the default
when /^-W[alp],/, /^-Wno-/, "-Werror=implicit-function-declaration"
2014-05-06 19:29:18 -05:00
args << arg
2014-05-06 15:21:01 -05:00
when /^-W.*/
2014-05-06 19:29:18 -05:00
# prune warnings
when "-macosx_version_min", "-dylib_install_name"
2014-05-06 15:21:01 -05:00
args << "-Wl,#{arg},#{enum.next}"
when "-multiply_definedsuppress"
2014-05-06 15:21:01 -05:00
args << "-Wl,-multiply_defined,suppress"
when "-undefineddynamic_lookup"
2014-05-06 15:21:01 -05:00
args << "-Wl,-undefined,dynamic_lookup"
when /^-isysroot=/, /^--sysroot=/
2018-05-28 11:54:16 -07:00
if mac?
sdk = arg.split("=")[1..-1].join("=")
# We set the sysroot for macOS SDKs
args << arg unless sdk.downcase.include? "osx"
else
args << arg
end
when /^-isysroot(.+)?/, /^--sysroot(.+)?/
# Support both "-isysrootfoo" and "-isysroot foo" (two arguments)
sdk = chuzzle(Regexp.last_match(1))
if mac?
sdk ||= enum.next
2018-05-28 11:54:16 -07:00
# We set the sysroot for macOS SDKs
args << "-isysroot#{sdk}" unless sdk.downcase.include? "osx"
2018-05-28 11:54:16 -07:00
else
args << arg
args << enum.next unless sdk
2018-05-28 11:54:16 -07:00
end
when "-dylib"
2014-05-06 15:21:01 -05:00
args << "-Wl,#{arg}"
when /^-I(.+)?/
# Support both "-Ifoo" (one argument) and "-I foo" (two arguments)
2021-01-08 12:15:55 -08:00
val = chuzzle(Regexp.last_match(1)) || enum.next
2014-05-06 15:21:01 -05:00
path = canonical_path(val)
args << "-I#{val}" if keep?(path) && @iset.add?(path)
when /^-L(.+)?/
2021-01-08 12:15:55 -08:00
val = chuzzle(Regexp.last_match(1)) || enum.next
2014-05-06 15:21:01 -05:00
path = canonical_path(val)
args << "-L#{val}" if keep?(path) && @lset.add?(path)
else
args << arg
end
2014-05-06 15:21:01 -05:00
args
end
def keep?(path)
# Allow references to self
if formula_prefix && path.start_with?("#{formula_prefix}/")
true
# first two paths: reject references to Cellar or opt paths
# for unspecified dependencies
elsif path.start_with?(cellar) || path.start_with?(opt)
dep = path[keg_regex, 2]
dep && @deps.include?(dep)
elsif path.start_with?(prefix, cachedir, tmpdir)
true
2018-05-28 11:54:16 -07:00
elsif path.start_with?("/opt/local", "/opt/boxen/homebrew", "/opt/X11", "/sw", "/usr/X11")
# ignore MacPorts, Boxen's Homebrew, X11, fink
2018-05-28 11:54:16 -07:00
false
elsif prefix != "/usr/local" && path.start_with?("/usr/local")
# ignore things in /usr/local if Homebrew is in another prefix;
# on macOS, this probably means that the user is on ARM and has an Intel
# Homebrew in /usr/local
false
2018-05-28 11:54:16 -07:00
elsif mac?
true
else
false
end
end
def cflags
args = []
2021-01-08 12:15:55 -08:00
return args if !refurbish_args? && !configure?
args << "-pipe"
args << "-w" unless configure?
args << "-#{ENV["HOMEBREW_OPTIMIZATION_LEVEL"]}"
args.concat(optflags) unless runtime_cpu_detection?
args.concat(archflags)
2021-01-08 12:15:55 -08:00
args << "-std=#{@arg0}" if /c[89]9/.match?(@arg0)
args
end
2014-04-21 00:17:22 -05:00
def cxxflags
args = cflags
args << "-std=c++11" if cxx11?
args << "-stdlib=libc++" if libcxx?
args << "-stdlib=libstdc++" if libstdcxx?
args
end
2014-04-21 00:17:22 -05:00
def cppflags
path_flags("-isystem", isystem_paths) + path_flags("-I", include_paths)
end
2014-04-21 00:17:22 -05:00
2018-05-28 11:54:16 -07:00
def ldflags_mac(args)
case mode
when :ld
args << "-headerpad_max_install_names"
args << "-no_weak_imports" if no_weak_imports?
when :ccld, :cxxld
args << "-Wl,-headerpad_max_install_names"
args << "-Wl,-no_weak_imports" if no_weak_imports?
end
args
end
2014-04-21 00:17:22 -05:00
2018-05-28 11:54:16 -07:00
def ldflags_linux(args)
unless mode == :ld
wl = "-Wl,"
args << "-B#{@opt}/glibc/lib"
end
args += rpath_flags("#{wl}-rpath=", rpath_paths)
args += ["#{wl}--dynamic-linker=#{dynamic_linker_path}"] if dynamic_linker_path
args
end
def ldflags
args = path_flags("-L", library_paths)
if mac?
ldflags_mac(args)
elsif linux?
ldflags_linux(args)
else
args
end
end
def isystem_paths
path_split("HOMEBREW_ISYSTEM_PATHS")
end
def include_paths
path_split("HOMEBREW_INCLUDE_PATHS")
end
def library_paths
path_split("HOMEBREW_LIBRARY_PATHS")
end
2018-05-28 11:54:16 -07:00
def rpath_paths
path_split("HOMEBREW_RPATH_PATHS")
end
def dynamic_linker_path
chuzzle(ENV["HOMEBREW_DYNAMIC_LINKER"])
end
def system_library_paths
paths = ["#{sysroot}/usr/lib"]
2021-01-08 12:15:55 -08:00
paths << "/usr/local/lib" if !sysroot && !ENV["SDKROOT"]
paths
end
2014-04-21 00:17:22 -05:00
def configure?
# configure scripts generated with autoconf 2.61 or later export as_nl
ENV.key? "as_nl"
end
2014-04-21 00:17:22 -05:00
def refurbish_args?
2015-02-08 20:04:06 -05:00
config.include?("O")
end
def cxx11?
2015-02-08 20:04:06 -05:00
config.include?("x")
end
def libcxx?
2015-02-08 20:04:06 -05:00
config.include?("g")
end
def libstdcxx?
2015-02-08 20:04:06 -05:00
config.include?("h")
end
def permit_arch_flags?
2015-02-08 20:04:06 -05:00
config.include?("K")
end
def runtime_cpu_detection?
config.include?("d")
end
def no_weak_imports?
config.include?("w")
end
2014-04-20 19:57:01 -05:00
def canonical_path(path)
path = Pathname.new(path)
path = path.realpath if path.exist?
path.to_s
end
2014-04-21 00:17:22 -05:00
2014-04-20 19:54:32 -05:00
def path_flags(prefix, paths)
paths = paths.uniq.select { |path| File.directory?(path) }
paths.map! { |path| prefix + path }
end
2014-04-21 00:17:22 -05:00
# Unlike {path_flags}, do not prune non-existent directories.
# `formula.lib` for example does not yet exist, but should not be pruned.
2018-05-28 11:54:16 -07:00
def rpath_flags(prefix, paths)
paths.uniq.map { |path| prefix + path }
end
2014-04-20 22:39:47 -05:00
def path_split(key)
2021-01-08 12:15:55 -08:00
ENV.fetch(key, "").split(File::PATH_SEPARATOR)
2014-04-20 22:39:47 -05:00
end
2014-04-21 00:17:22 -05:00
2014-04-20 22:39:47 -05:00
def chuzzle(val)
return val if val.nil?
2021-01-08 12:15:55 -08:00
2014-04-20 22:39:47 -05:00
val = val.chomp
return val unless val.empty?
end
end
2015-02-10 20:27:26 -05:00
def log(basename, argv, tool, args)
return unless ENV.key?("HOMEBREW_CC_LOG_PATH")
adds = args - argv
dels = argv - args
s = +""
2015-02-10 20:27:26 -05:00
s << "#{basename} called with: #{argv.join(" ")}\n"
s << "superenv removed: #{dels.join(" ")}\n" unless dels.empty?
s << "superenv added: #{adds.join(" ")}\n" unless adds.empty?
s << "superenv executed: #{tool} #{args.join(" ")}\n\n"
File.open("#{ENV["HOMEBREW_CC_LOG_PATH"]}.cc", "a+") { |f| f.write(s) }
end
2018-05-28 11:54:16 -07:00
def remove_superbin_from_path(paths)
superbin = Pathname.new(__FILE__).dirname.realpath
paths.reject do |x|
path = Pathname.new(x)
path.directory? && path.realpath == superbin
end
end
2013-03-03 22:11:26 -06:00
if __FILE__ == $PROGRAM_NAME
##################################################################### sanity
2014-04-20 22:39:47 -05:00
if (cc = ENV["HOMEBREW_CC"]).nil? || cc.empty? || cc == "cc"
2013-03-03 22:11:26 -06:00
# those values are not allowed
ENV["HOMEBREW_CC"] = "clang"
2013-03-03 22:11:26 -06:00
end
2013-03-03 22:11:26 -06:00
####################################################################### main
2021-01-08 12:15:55 -08:00
dirname, basename = File.split($PROGRAM_NAME)
2015-02-12 19:13:03 -05:00
cmd = Cmd.new(basename, ARGV)
tool = cmd.tool
args = cmd.args
2015-02-10 20:27:26 -05:00
log(basename, ARGV, tool, args)
2021-01-08 12:15:55 -08:00
args << { close_others: false }
2018-05-28 11:54:16 -07:00
if mac?
exec "#{dirname}/xcrun", tool, *args
else
paths = ENV["PATH"].split(":")
paths = remove_superbin_from_path(paths)
paths.unshift "#{ENV["HOMEBREW_PREFIX"]}/bin"
ENV["PATH"] = paths.join(":")
exec tool, *args
end
2013-03-03 22:11:26 -06:00
end