From df516600663f95b4fb2f399744fff1ddc88d3b9c Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 17 Sep 2015 16:25:07 +0100 Subject: [PATCH] contrib: fat-gcc.sh A script that mimics the Apple driver-driver command that builds multiple objects and lipos them together. ***WARNING*** likely very incomplete --- contrib/fat-gcc.sh | 420 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100755 contrib/fat-gcc.sh diff --git a/contrib/fat-gcc.sh b/contrib/fat-gcc.sh new file mode 100755 index 00000000000..b4f6dd2f5eb --- /dev/null +++ contrib/fat-gcc.sh @@ -0,0 +1,420 @@ +#! /bin/sh +# +# (C) Iain Sandoe May 2015. +# +# Try to mimic enough of the behaviour of the apple "driverdriver" and clang +# that we can build fat binaries using the normal GCC driver for most of the +# work. +# +# Install this in your toolchain 'bin' directory - it finds the toolchain +# pieces relative to its install. +# +# You would have (say): +# .../bin/fat-gcc-N.M (iff your gcc is installed with a numerical suffix) +# .../bin/fat-gcc (if not) +# +# Likewise: +# +# .../fat-gfortran +# .../fat-g++ / fat-c++ +# +# TODO: figure out how to install this instead of the non-canonical target +# compiler. +# +# +# Mostly this is about building multiple objects and lipo-ing them together, +# however it might also be necessary to partition arch-specific flags in some +# cases. +# +# TODO: Handle different source types, handle arch-specific options. +# +# version 0.1 - May 2015 - basic support for ppc and x86 +# version 0.2 - July 2022 - add arm64. + +prog="${0##*/}" +prog="${prog%.*}" +homedir="${0%/*}" +version="xtools - $prog 0.7.0" + +destprog="${prog#*fat-}" +archs=0 +ppc=0 +ppc64=0 +x86=0 +x86_64=0 +arm64=0 +new_cl= +linking=1 +non_link_opt_seen=0 +seen_e_or_s= +seen_c=0 +b_path= +output_name= +seen_version=0 +seen_help=0 +seen_v=0 +seen_dylib=0 +seen_hashes=0 +min_vers= +save_temps= +seen_save_temps=0 +other_arg_count=0 +no_infiles= + +while [ $# -ne 0 ]; do + case $1 in + --version) seen_version=1; new_cl="${new_cl} $1"; no_infiles="${no_infiles} $1"; shift ;; + --help*) seen_help=1; new_cl="${new_cl} $1"; no_infiles="${no_infiles} $1"; shift ;; + -###) seen_hashes=1; new_cl="${new_cl} $1"; no_infiles="${no_infiles} $1"; shift ;; + -v) seen_v=1; new_cl="${new_cl} $1"; no_infiles="${no_infiles} $1"; shift;; + -save-temps*) seen_save_temps=1; save_temps=$1; shift;; + -arch) + ((archs+=1)); shift + case $1 in + ppc64) ppc64=1;; + ppc*) ppc=1; ppc_arch=$1;; + i386) x86=1; x86_arch=$1;; + i686) x86=1; x86_arch=$1;; + x86_64) x86_64=1;; + arm64 | aarch64) arm64=1;; + *) echo "$prog : Unknown arch: $1 (ignored)";; + esac + shift + ;; + -mmacosx-version-min=*) + min_vers="${1#*=}"; + no_infiles="${no_infiles} $1"; + new_cl="${new_cl} $1"; + shift + ;; + -S|-E) + seen_e_or_s=$1; + linking=0 + no_infiles="${no_infiles} $1"; + new_cl="${new_cl} $1"; + shift + ;; + -c) + seen_c=1; + linking=0 + no_infiles="${no_infiles} $1"; + new_cl="${new_cl} $1"; + shift + ;; + -B) + # We care about picking up auxiliary commands (like lipo) from + # overridden PATHs. + new_cl="${new_cl} $1 $2"; + no_infiles="${no_infiles} $1 $2"; + b_path="$b_path$2:"; shift 2 + ;; + -B*) + new_cl="${new_cl} $1"; + no_infiles="${no_infiles} $1"; + b_path="$b_path${1#*B}:"; shift + ;; + -o) + output_name=$2; + shift 2 + ;; + -dynamiclib | -shared) + seen_dylib=1 + no_infiles="${no_infiles} $1"; + new_cl="${new_cl} $1"; + shift + ;; + -I | -L | -framework | -compatibility_version | -current_version | -install_name | -x) + no_infiles="${no_infiles} $1 $2"; + new_cl="${new_cl} $1 $2"; + shift 2 + ;; + -*) + new_cl="${new_cl} $1"; + no_infiles="${no_infiles} $1"; + shift + ;; + *) + # keep the remainder of the positional args. + other_args[other_arg_count]="$1" + (( other_arg_count += 1)) + new_cl="${new_cl} $1"; + shift + ;; + esac +done + +if [ $archs -le 1 ]; then + if [ $ppc -eq 1 ]; then new_cl="${new_cl} -arch $ppc_arch"; fi + if [ $x86 -eq 1 ]; then new_cl="${new_cl} -arch $x86_arch"; fi + if [ $ppc64 -eq 1 ]; then new_cl="${new_cl} -arch ppc64"; fi + if [ $x86_64 -eq 1 ]; then new_cl="${new_cl} -arch x86_64"; fi + if [ $arm64 -eq 1 ]; then new_cl="${new_cl} -arch arm64"; fi + if [ -n "$output_name" ]; then new_cl="${new_cl} -o $output_name"; fi + #echo "$prog : $destprog $new_cl $save_temps" + exec $destprog $new_cl +fi + +# OK - so it's not a simple case, make sure we detect when we're cross-compiling +# from a non-darwin host. + +is_darwin=0 +is_ppc=0 +is_arm64=0 +default_darwin_vers=9 +default_darwin_arch="i686" +host_info=`uname -srm` +read sys vers narch <<< "$host_info" +case $sys in + Darwin) + is_darwin=1; + case $narch in + # We don't get anything to indicate a powerpc64 platform (because there + # never was - a public - one). + Power*) default_darwin_arch="powerpc"; is_ppc=0;; + x86_64) default_darwin_arch="x86_64"; is_x86=1 ;; + i386) default_darwin_arch="i686"; is_x86=1;; + Arm64) default_darwin_arch="arm64"; is_arm64=1;; + *) echo "$prog : didn't recognise the arch from $narch";; + esac + # Drop any micro version + default_darwin_vers="${vers%.*}" + # Drop any mini version + default_darwin_vers="${default_darwin_vers%.*}" + ;; + *) ;; +esac + +if [ -n "$min_vers" ]; then + maj="${min_vers#darwin.}" + maj="${maj%.*}" + if [ $maj -le 10 ]; then + # Lose the pointless 10. + darwin="${min_vers#*10.}" + # Lose the minor, + darwin="${darwin%.*}" + (( darwin += 4 )) + else + (( darwin += 9 )) + fi +elif [ $is_arm64 -eq 1 ]; then + darwin=20 +else + darwin=$default_darwin_vers +fi + + +#echo "sys $sys version $vers native arch $narch (darwin vers $darwin)" + +case $seen_e_or_s in + -S) + echo "$prog : error: cannot use 'assembler' output with multiple -arch options" + exit 1 + ;; + -E) + echo "$prog : error: cannot use 'c++-cpp-output' output with multiple -arch options" + exit 1 + ;; + *) ;; +esac + +# So now we want to build a per arch preferred toolname. +# This is a bit icky, we're spawning commands to check if we can find compilers, +# I suppose we could just assume that they're installed and fail at runtime. + +arch_idx=0 +if [ $ppc -eq 1 ] || [ $ppc64 -eq 1 ]; then + if [ $ppc -eq 1 ]; then + arch_list[$arch_idx]="$ppc_arch"; + arch_type[$arch_idx]=0; + (( arch_idx += 1 )) + fi + if [ $ppc64 -eq 1 ]; then + arch_list[$arch_idx]="$ppc64"; + arch_type[$arch_idx]=0; + (( arch_idx += 1 )) + fi + ppc_cmd="$homedir/powerpc-apple-darwin$darwin-$destprog" + if ! which $ppc_cmd >/dev/null ; then + ppc_cmd="$homedir/powerpc64-apple-darwin$darwin-$destprog" + if ! which $ppc_cmd >/dev/null ; then + echo "$prog : didn't find powerpc{,64}-apple-darwin$darwin-$destprog" + if [ $is_darwin -eq 1 ] && [ ! $is_x86 ]; then + echo "$prog : trying $homedir/$destprog" + ppc_cmd="$homedir/$destprog" + else + # Fall back to most likely. + ppc_cmd="powerpc-apple-darwin9-$destprog" + echo "$prog : trying $ppc_cmd" + fi + fi + fi +fi + +if [ $x86 -eq 1 ] || [ $x86_64 -eq 1 ]; then + if [ $x86 -eq 1 ]; then + arch_list[$arch_idx]="$x86_arch"; + arch_type[$arch_idx]=1; + (( arch_idx += 1 )) + fi + if [ $x86_64 -eq 1 ]; then + arch_list[$arch_idx]="x86_64"; + arch_type[$arch_idx]=1; + (( arch_idx += 1 )) + fi + x86_cmd="$homedir/x86_64-apple-darwin$darwin-$destprog" + if ! which $x86_cmd >/dev/null ; then + x86_cmd="$homedir/i686-apple-darwin$darwin-$destprog" + if ! which $x86_cmd >/dev/null ; then + echo "$prog : didn't find {x86_64,i686}-apple-darwin$darwin-$destprog" + if [ $is_darwin -eq 1 ] && [ $is_x86 ]; then + echo "$prog : trying $homedir/$destprog" + x86_cmd="$homedir/$destprog" + fi + fi + fi +fi + +if [ $arm64 -eq 1 ]; then + arch_list[$arch_idx]="arm64"; + arch_type[$arch_idx]=2; + (( arch_idx += 1 )) + arm64_cmd="$homedir/aarch64-apple-darwin$darwin-$destprog" + if ! which $arm64_cmd >/dev/null ; then + echo "$prog : didn't find aarch64-apple-darwin$darwin-$destprog" + fi +fi + +# The next easiest case is when we only need to worry about linking the result. +if [ -z $output_name ]; then + output_name="a.out" +fi +idx=0 +while [ $idx -lt $arch_idx ]; do + if [ $seen_save_temps -eq 1 ]; then + tdir="./$prog.$$.${arch_list[idx]}" + save_temps="-save-temps=obj" + else + tdir="/tmp/$prog.$$.${arch_list[idx]}" + save_temps= + fi + tdirs[idx]="$tdir" + if [ $seen_hashes -eq 0 ]; then + if ! mkdir -p $tdir ; then + echo "failed to make $tdir" + fi + fi + (( idx += 1 )) +done + +if [ $linking -ne 0 ]; then + extra_link_opts= + if [ $seen_dylib -ne 0 ]; then + extra_link_opts="-Wl,-arch_multiple,-final_output,$output_name" + fi + object_list= + idx=0 + while [ $idx -lt $arch_idx ]; do + object_arch="${tdirs[idx]}/$output_name" + object_list="${object_list} $object_arch" + cl=" -arch ${arch_list[idx]} $new_cl $extra_link_opts -o $object_arch $save_temps" + if [ ${arch_type[idx]} -eq 0 ]; then + #echo "$ppc_cmd $cl" + if ! $ppc_cmd $cl ; then + echo "$ppc_cmd $cl failed" + fi + elif [ ${arch_type[idx]} -eq 2 ]; then + #echo "$ppc_cmd $cl" + if ! $arm64_cmd $cl ; then + echo "$arm64_cmd $cl failed" + fi + else + #echo "$x86_cmd $cl" + if ! $x86_cmd $cl ; then + echo "$x86_cmd $cl failed" + fi + fi + (( idx += 1 )) + done + if [ $seen_version -eq 1 ] || [ $seen_v -eq 1 ]; then + echo "$version" + if [ $seen_version -eq 1 ]; then exit 0; fi + fi + if [ $seen_hashes -eq 1 ]; then + lipo_to_use=`which lipo` + echo "\"$lipo_to_use\" -create -o $output_name $object_list" + exit 0 + fi + if ! lipo -create -o $output_name $object_list ; then + echo "lipo -create -o $output_name $object_list ... failed" + exit 1 + fi + if [ $seen_hashes -eq 0 ] && [ $seen_save_temps -eq 0 ]; then + idx=0 + while [ $idx -lt $arch_idx ]; do + #echo "removing : ${tdirs[idx]}" + rm -r "${tdirs[idx]}" + (( idx += 1 )) + done + fi + exit 0 +fi + +# Now the longest-winded of them all. +if [ $seen_c -ne 0 ]; then + idx=0 + while [ $idx -lt $other_arg_count ]; do + name="${other_args[idx]}" + name="${name##*/}" + oname="${name%.*}" + oname="${oname}.o" + #echo "source to build : ${other_args[idx]} => $name => $oname" + aidx=0 + object_list= + while [ $aidx -lt $arch_idx ]; do + object_arch="${tdirs[aidx]}/$oname" + object_list="${object_list} $object_arch" + cl=" -arch ${arch_list[aidx]} $no_infiles $extra_link_opts -o $object_arch $save_temps ${other_args[idx]}" + if [ ${arch_type[aidx]} -eq 0 ]; then + #echo "$ppc_cmd $cl" + if ! $ppc_cmd $cl ; then + echo "$ppc_cmd $cl failed" + fi + elif [ ${arch_type[aidx]} -eq 2 ]; then + #echo "$arm64_cmd $cl" + if ! $arm64_cmd $cl ; then + echo "$arm64_cmd $cl failed" + fi + else + #echo "$x86_cmd $cl" + if ! $x86_cmd $cl ; then + echo "$x86_cmd $cl failed" + fi + fi + (( aidx += 1 )) + done + if [ $seen_hashes -eq 1 ]; then + lipo_to_use=`which lipo` + echo "\"$lipo_to_use\" -create -o $oname $object_list" + elif ! lipo -create -o $oname $object_list ; then + echo "lipo -create -o $oname $object_list ... failed" + fi +# if [ $seen_hashes -eq 0 ] && [ $seen_save_temps -eq 0 ]; then +# rm -r "$object_list" +# fi + (( idx += 1)) + done +fi +if [ $seen_hashes -eq 0 ] && [ $seen_save_temps -eq 0 ]; then + idx=0 + while [ $idx -lt $arch_idx ]; do + #echo "removing : ${tdirs[idx]}" + rm -r "${tdirs[idx]}" + (( idx += 1 )) + done +fi + +#echo "$prog : #archs : $archs : ppc=$ppc_arch x86=$x86_arch ppc64=$ppc64 x86_64=$x86_64 arm64=$arm64" +#echo "$prog : Bpaths : $b_path : min_vers $min_vers linking $linking" +#echo "$prog : new commandline : $new_cl (arcs $arch_idx archlist : $arch_list)" + +exit 0