
Combined with https://github.com/Homebrew/homebrew-test-bot/pull/247 this will test Homebrew's use of frozen strings in CI. After this we will then enable it for Homebrew developers and eventually all Homebrew users.
452 lines
12 KiB
Bash
Executable File
452 lines
12 KiB
Bash
Executable File
#!/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"
|
|
|
|
def mac?
|
|
RUBY_PLATFORM[/darwin/]
|
|
end
|
|
|
|
def high_sierra_or_later?
|
|
mac? && ENV["HOMEBREW_MACOS_VERSION_NUMERIC"].to_s.to_i >= 101300
|
|
end
|
|
|
|
def linux?
|
|
RUBY_PLATFORM[/linux/]
|
|
end
|
|
|
|
class Cmd
|
|
attr_reader :config, :prefix, :cellar, :opt, :cachedir, :tmpdir, :sysroot, :deps
|
|
attr_reader :archflags, :optflags, :keg_regex, :formula_prefix
|
|
|
|
def initialize(arg0, args)
|
|
@arg0 = arg0
|
|
@args = args.freeze
|
|
@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"]
|
|
@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
|
|
@keg_regex = %r[(#{Regexp.escape(opt)}|#{Regexp.escape(cellar)})/([\w+-.@]+)]
|
|
end
|
|
|
|
def mode
|
|
if @arg0 == "cpp"
|
|
:cpp
|
|
elsif ["ld", "ld.gold", "gold"].include? @arg0
|
|
:ld
|
|
elsif @args.include? "-c"
|
|
if @arg0 =~ /(?:c|g|clang)\+\+/
|
|
:cxx
|
|
else
|
|
:cc
|
|
end
|
|
elsif @args.include?("-xc++-header") || @args.each_cons(2).include?(["-x", "c++-header"])
|
|
:cxx
|
|
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 "gold", "ld.gold" then "ld.gold"
|
|
when "cpp" then "cpp"
|
|
when /llvm_(clang(\+\+)?)/
|
|
"#{ENV["HOMEBREW_PREFIX"]}/opt/llvm/bin/#{$1}"
|
|
when /\w\+\+(-\d(\.\d)?)?$/
|
|
case ENV["HOMEBREW_CC"]
|
|
when /clang/
|
|
"clang++"
|
|
when /llvm-gcc/
|
|
"llvm-g++-4.2"
|
|
when /(g)?cc(-\d(\.\d)?)?$/
|
|
"g++" + $2.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.
|
|
if ENV["HOMEBREW_CC"] == "llvm_clang"
|
|
"#{ENV["HOMEBREW_PREFIX"]}/opt/llvm/bin/clang"
|
|
else
|
|
ENV["HOMEBREW_CC"]
|
|
end
|
|
end
|
|
end
|
|
|
|
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
|
|
|
|
if !refurbish_args? || tool == "ld" || configure?
|
|
args = @args.dup
|
|
else
|
|
args = refurbished_args
|
|
end
|
|
|
|
if sysroot
|
|
if tool == "ld"
|
|
args << "-syslibroot" << sysroot
|
|
else
|
|
args << "-isysroot" << sysroot << "--sysroot=#{sysroot}"
|
|
end
|
|
end
|
|
|
|
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
|
|
end
|
|
|
|
def refurbished_args
|
|
@lset = Set.new(library_paths + system_library_paths)
|
|
@iset = Set.new(isystem_paths + include_paths)
|
|
|
|
args = []
|
|
enum = @args.each
|
|
|
|
loop do
|
|
case arg = enum.next
|
|
when "-arch"
|
|
if permit_arch_flags?
|
|
args << arg << enum.next
|
|
else
|
|
enum.next
|
|
end
|
|
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
|
|
end
|
|
|
|
args
|
|
end
|
|
|
|
def refurbish_arg(arg, enum)
|
|
args = []
|
|
|
|
case arg
|
|
when /^-g\d?$/, /^-gstabs\d+/, "-gstabs+", /^-ggdb\d?/,
|
|
/^-march=.+/, /^-mtune=.+/, /^-mcpu=.+/,
|
|
/^-O[0-9zs]?$/, "-fast", "-no-cpp-precomp",
|
|
"-pedantic", "-pedantic-errors", "-Wno-long-double",
|
|
"-Wno-unused-but-set-variable"
|
|
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"
|
|
# clang doesn't support these flags
|
|
args << arg unless tool =~ /^clang/
|
|
when "-Xpreprocessor"
|
|
# used for -Xpreprocessor -fopenmp
|
|
args << arg << enum.next
|
|
when /-mmacosx-version-min=10\.(\d+)/
|
|
arg = "-mmacosx-version-min=10.9" if high_sierra_or_later? && $1.to_i < 9
|
|
args << arg
|
|
when "--fast-math"
|
|
arg = "-ffast-math" if tool =~ /^clang/
|
|
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-/
|
|
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/
|
|
if mac?
|
|
sdk = enum.next
|
|
# We set the sysroot for macOS SDKs
|
|
args << "-isysroot" << sdk unless sdk.downcase.include? "osx"
|
|
else
|
|
args << arg << enum.next
|
|
end
|
|
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)
|
|
# 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
|
|
elsif path.start_with?("/opt/local", "/opt/boxen/homebrew", "/opt/X11", "/sw", "/usr/X11")
|
|
# ignore MacPorts, Boxen's Homebrew, X11, fink
|
|
false
|
|
elsif mac?
|
|
true
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def cflags
|
|
args = []
|
|
|
|
return args unless refurbish_args? || configure?
|
|
|
|
args << "-pipe"
|
|
args << "-w" unless configure?
|
|
args << "-#{ENV["HOMEBREW_OPTIMIZATION_LEVEL"]}"
|
|
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 cxx11?
|
|
args << "-stdlib=libc++" if libcxx?
|
|
args << "-stdlib=libstdc++" if libstdcxx?
|
|
args
|
|
end
|
|
|
|
def cppflags
|
|
path_flags("-isystem", isystem_paths) + path_flags("-I", include_paths)
|
|
end
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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"]
|
|
paths << "/usr/local/lib" unless sysroot || ENV["SDKROOT"]
|
|
paths
|
|
end
|
|
|
|
def configure?
|
|
# configure scripts generated with autoconf 2.61 or later export as_nl
|
|
ENV.key? "as_nl"
|
|
end
|
|
|
|
def refurbish_args?
|
|
config.include?("O")
|
|
end
|
|
|
|
def cxx11?
|
|
config.include?("x")
|
|
end
|
|
|
|
def libcxx?
|
|
config.include?("g")
|
|
end
|
|
|
|
def libstdcxx?
|
|
config.include?("h")
|
|
end
|
|
|
|
def permit_arch_flags?
|
|
config.include?("K")
|
|
end
|
|
|
|
def no_weak_imports?
|
|
config.include?("w")
|
|
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
|
|
|
|
# Unlike path_flags, do not prune non-existant directories.
|
|
# formula.lib for example does not yet exist, but should not be pruned.
|
|
def rpath_flags(prefix, paths)
|
|
paths.uniq.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
|
|
|
|
def log(basename, argv, tool, args)
|
|
return unless ENV.key?("HOMEBREW_CC_LOG_PATH")
|
|
|
|
adds = args - argv
|
|
dels = argv - args
|
|
|
|
s = +""
|
|
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
|
|
|
|
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
|
|
|
|
if __FILE__ == $PROGRAM_NAME
|
|
##################################################################### sanity
|
|
|
|
if (cc = ENV["HOMEBREW_CC"]).nil? || cc.empty? || cc == "cc"
|
|
# those values are not allowed
|
|
ENV["HOMEBREW_CC"] = "clang"
|
|
end
|
|
|
|
####################################################################### main
|
|
|
|
dirname, basename = File.split($0)
|
|
|
|
cmd = Cmd.new(basename, ARGV)
|
|
tool = cmd.tool
|
|
args = cmd.args
|
|
|
|
log(basename, ARGV, tool, args)
|
|
|
|
args << { :close_others => false }
|
|
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
|
|
end
|