Skip to content

Commit fab7422

Browse files
committed
add build scripts
1 parent 52b70a6 commit fab7422

File tree

9 files changed

+539
-0
lines changed

9 files changed

+539
-0
lines changed

build/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# GNU Fortran Universal Build
2+
3+
This directory contains the scripts used to build a fully universal
4+
GNU Fortran for macOS. The scripts will build for the `/opt/gfortran`
5+
destination with macOS 11 target and SDK. (Most scripts have a single
6+
`prefix` parameter, but some don't). The build requires macOS 11
7+
or higher and Apple silicon machine with Rosetta 2 installed.
8+
9+
The scripts are described in the order they should be run.
10+
11+
* `deps.sh` - downloads and builds GCC dependencies.
12+
They are built statically, so they do not need to be distributed
13+
with the compiler. They will be installed in the `deps`
14+
subdirectory.
15+
Note: for Rosetta 2 safety `gmp` is built with `westmere` target to
16+
avoid illegal instruction faults in Rosetta due to AVX.
17+
18+
* `build.sh` - builds GCC + GNU Fortran for all four combinations.
19+
20+
The first step is to build stubs for all system tools,
21+
because cross-compilation requires `<arch>-<build>-<tool>` even
22+
though all tools are universal, so the stubs simply execute
23+
`/usr/bin/<tool>` instead (see the `stubs` directory).
24+
25+
Then the native arm64 and x86_64 compilers are built (for simplicity
26+
we treat x86_64 as native) then the corresponding cross-compilers
27+
targetting the other architecture. The resulting directories have
28+
this structure:
29+
30+
* `obj-<host_arch>-<target_arch>` location of the GCC build
31+
* `dst-<host_arch>-<target_arch>` location of the installation
32+
33+
After each successful build the result is installed in the prefix
34+
location, becuase the cross-compiler build uses GCC built in the
35+
native step from the installed location.
36+
37+
Note: all `@rpath`s are removed and replaced with target locations,
38+
becuase run-time libraries are notoriously fragile when using
39+
`@rpath` (works for the compilers, but not the run-time).
40+
41+
`UPDATE=1` env var can be set in repeated calls to `build.sh` to
42+
avoid re-building already built `dst-*-*` directories.
43+
44+
* `mkuniversal.sh` - uses the `dst-*-*` directories to create a final
45+
`dst-universal` directory. It is based on the cross-compiled
46+
builds (because the native builds include `g++` which is not
47+
necessary and opens a whole can of worms we don't want). Then all
48+
binaries for the same target are `lipo`ed from both architectures -
49+
both the drivers in `bin` and `libexec`. Then the simple `gfortran`
50+
driver-driver is built (from `stubs`).
51+
52+
This process is more or less based on Apple's old `gcc` and the way we
53+
created universal GNU Fortran back then for PowerPC and Intel, which
54+
was copnceptually built this way. Unfortunately, the original
55+
`driverdriver.c` can no longer be used, because the GCC driver API has
56+
changed dramatically in 2010 so we provide a very rudimentary
57+
driver-driver which only filters duplicate `-arch` flags and raises an
58+
error if different archs are used.
59+
60+
An alternative `dr.sh` is provided to support multi-arch compilation
61+
in one call via `-arch arm64 -arch x86_64`, but that is not
62+
well-tested and instead of linking a full GCC driver, it relies on a
63+
hack where the GCC driver will list output files into a file specified
64+
by `_GCC_WRITE_OUTFILES` which are then `lipo`ed together.
65+
66+
Note: all builds use `--enable-version-specific-runtime-libs` to
67+
ensure that both the native compilers and the cross-compilers use the
68+
same run-time library locations. Technically, we could `lipo` both
69+
`lib` locations into one (which is what we did when we used the native
70+
prefix location), but the rationale is that not using the native
71+
location is safer.
72+
73+
Simon Urbanek
74+
75+
February 2023

build/build.sh

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/bin/bash
2+
3+
prefix=/opt/gfortran
4+
infix=apple-darwin20.0
5+
export MACOSX_DEPLOYMENT_TARGET=11.0
6+
7+
# ----
8+
9+
set -e
10+
11+
if [ ! -e $prefix/bin ]; then
12+
echo Creating $prefix
13+
mkdir -p $prefix/bin
14+
fi
15+
16+
BASE=$(pwd)
17+
18+
echo Build stubs
19+
( cd stubs && sh build.sh )
20+
21+
echo Copy stubs to $prefix
22+
cp -p stubs/*darwin* $prefix/bin
23+
24+
if [ ! -e $prefix/SDK ]; then
25+
if [ -z "$SDKROOT" ]; then
26+
if [ -e /Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk ]; then
27+
SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk
28+
else
29+
SDKROOT=$(xcrun --show-sdk-path)
30+
fi
31+
fi
32+
echo Link SDK to $SDKROOT
33+
ln -s $SDKROOT $prefix/SDK
34+
export SDKROOT
35+
else
36+
export SDKROOT=$prefix/SDK
37+
fi
38+
39+
depprefix=$(pwd)/deps
40+
if [ ! -e ${depprefix}/lib/libmpc.a ]; then
41+
if [ -e ${prefix}/lib/libmpc.a ]; then
42+
depprefix="$prefix"
43+
else
44+
echo "ERROR: cannot find build dependencies - try running deps.sh first" >&2
45+
exit 1
46+
fi
47+
fi
48+
49+
echo "Using $depprefix for build dependencies"
50+
51+
## no-slash version of the prefix (relative)
52+
relprefix=$(echo $prefix | sed 's:^/*::')
53+
54+
fixpaths() {
55+
for libfile in $(find $relprefix -name \*.dylib -type f); do
56+
echo " - $libfile"
57+
install_name_tool -id /$libfile $libfile
58+
for libname in $(find $relprefix -name \*.dylib -type f | sed 's:.*/::'); do
59+
install_name_tool -change "@rpath/$libname" "/$(dirname $libfile)/$libname" "$libfile"
60+
done
61+
done
62+
}
63+
64+
## harch, tarch, [lang]
65+
build1 () {
66+
rm -rf obj-${harch}-${tarch}
67+
mkdir obj-${harch}-${tarch}
68+
cd obj-${harch}-${tarch}
69+
charch=$harch
70+
ctarch=$tarch
71+
if [ $harch == arm64 ]; then charch=aarch64; fi
72+
if [ $tarch == arm64 ]; then ctarch=aarch64; fi
73+
if [ -z "$lang" ]; then lang=fortran; fi
74+
75+
if [ $harch == $tarch ]; then ## native => clang
76+
cc="clang -arch $harch"
77+
cxx="clang++ -arch $harch"
78+
else ## cross => use what we built
79+
cc="${charch}-${infix}-gcc"
80+
cxx="${charch}-${infix}-g++"
81+
fi
82+
PATH=$prefix/bin:$PATH ../gcc-12-branch/configure --host=${charch}-${infix} --build=${charch}-${infix} --target=${ctarch}-${infix} --prefix=$prefix --enable-languages=$lang --with-gmp=$depprefix --with-mpc=$depprefix --with-mpfr=$depprefix --with-isl=$depprefix --with-sysroot=$prefix/SDK --enable-version-specific-runtime-libs LDFLAGS_FOR_TARGET=-Wl,-headerpad_max_install_names "CC=$cc" "CXX=$cxx"
83+
84+
PATH=$prefix/bin:$PATH make -j32 BOOT_CFLAGS='-O'
85+
dst="$(pwd)/../dst-${harch}-${tarch}"
86+
rm -rf "$dst"
87+
PATH=$prefix/bin:$PATH make install DESTDIR="$dst"
88+
89+
echo Fixing paths ...
90+
cd "$dst"
91+
fixpaths
92+
93+
echo Copying installed ...
94+
rsync -a $relprefix/ $prefix/
95+
96+
cd ..
97+
echo Done with $harch compiling for $tarch
98+
}
99+
100+
## 1) native
101+
harch=arm64
102+
tarch=arm64
103+
if [ -z "$UPDATE" -o ! -e dst-${harch}-${tarch} ]; then
104+
lang=c,c++,fortran
105+
build1
106+
fi
107+
108+
## 2) x86_64 native
109+
harch=x86_64
110+
tarch=x86_64
111+
if [ -z "$UPDATE" -o ! -e dst-${harch}-${tarch} ]; then
112+
lang=c,c++,fortran
113+
build1
114+
fi
115+
116+
## 3) arm->x86 cross
117+
harch=arm64
118+
tarch=x86_64
119+
if [ -z "$UPDATE" -o ! -e dst-${harch}-${tarch} ]; then
120+
lang=fortran
121+
build1
122+
fi
123+
124+
## 4) x86->arm cross
125+
harch=x86_64
126+
tarch=arm64
127+
if [ -z "$UPDATE" -o ! -e dst-${harch}-${tarch} ]; then
128+
lang=fortran
129+
build1
130+
fi

build/deps.sh

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/bin/sh
2+
3+
set -e
4+
5+
build=apple-darwin20
6+
export MACOSX_DEPLOYMENT_TARGET=11.0
7+
8+
if [ ! -d deps ]; then
9+
mkdir deps
10+
fi
11+
12+
if [ ! -d deps/src ]; then
13+
mkdir deps/src
14+
fi
15+
16+
GMP=gmp-6.2.1
17+
MPFR=mpfr-4.2.0
18+
MPC=mpc-1.3.1
19+
ISL=isl-0.24
20+
21+
modules="$GMP $MPFR $MPC $ISL"
22+
23+
## download and unpack sources
24+
cd deps/src
25+
for module in $modules; do
26+
bm=$(echo $module | sed 's:-.*::')
27+
suff=.xz
28+
if [ $bm == mpc ]; then suff=.gz; fi
29+
if [ $bm == isl ]; then suff=.bz2; fi
30+
if [ ! -e ${module}.tar$suff ]; then
31+
if [ $bm == isl ]; then
32+
curl -LO https://gcc.gnu.org/pub/gcc/infrastructure/${module}.tar$suff
33+
else
34+
curl -LO https://ftp.gnu.org/gnu/${bm}/${module}.tar$suff
35+
fi
36+
fi
37+
if [ ! -e ${module} ]; then
38+
tar fxj ${module}.tar$suff
39+
fi
40+
done
41+
cd ../..
42+
43+
if [ -z "$UPDATE" ]; then
44+
rm -rf deps/build
45+
mkdir deps/build
46+
fi
47+
48+
cd deps
49+
prefix=$(pwd)
50+
relprefix=$(echo $prefix | sed 's:^/*::')
51+
cd build
52+
53+
for module in $modules; do
54+
bm=$(echo $module | sed 's:-.*::')
55+
for arch in arm64 x86_64; do
56+
conf="--prefix=$prefix --disable-shared --enable-static"
57+
if [ $bm == gmp -a $arch == x86_64 ]; then
58+
conf="--build=westmere-$build $conf"
59+
else
60+
carch=$arch
61+
if [ $arch == arm64 ]; then
62+
carch=aarch64
63+
fi
64+
conf="--build=$carch-$build $conf"
65+
fi
66+
if [ -z "$UPDATE" -o ! -e "dst-$arch-$module" ]; then
67+
obj=obj-$arch-$module
68+
rm -rf $obj
69+
mkdir $obj
70+
cd $obj
71+
../../src/$module/configure $conf "CC=clang -arch $arch" "CXX=clang++ -arch $arch" CPPFLAGS=-I$prefix/include LIBS=-L$prefix/lib --disable-shared --enable-static --with-pic
72+
make -j16
73+
rm -rf ../dst-$arch-$module
74+
make install DESTDIR="$(pwd)/../dst-$arch-$module"
75+
if [ $arch == arm64 ]; then
76+
make install
77+
fi
78+
cd ..
79+
fi
80+
if [ $arch == x86_64 ]; then
81+
cd dst-$arch-$module
82+
libs=$(find $relprefix -name \*.a)
83+
cd ..
84+
for lib in $libs; do
85+
echo lipo -create -arch arm64 dst-arm64-$module/$lib -arch x86_64 dst-x86_64-$module/$lib -output /$lib
86+
lipo -create -arch arm64 dst-arm64-$module/$lib -arch x86_64 dst-x86_64-$module/$lib -output /$lib
87+
done
88+
fi
89+
done
90+
done

build/dr.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/bin/bash
2+
#
3+
# experimental driver-driver which supports multiple
4+
# -arch <arch> flags and runs lipo on the results.
5+
#
6+
# NOTE: requires gcc to be compiled with _GCC_WRITE_OUTFILES support.
7+
#
8+
9+
# -- you can customize here --
10+
PROG=gfortran
11+
PREFIX=/opt/gfortran
12+
BUILD=apple-darwin20.0
13+
# --- end --
14+
15+
set -e
16+
17+
i=0
18+
DARGS=("$@")
19+
FINARGS=()
20+
ARCHS=()
21+
TOF=/tmp/outfiles.$$
22+
23+
while [ $i -lt ${#DARGS[@]} ]; do
24+
if [ ${DARGS[$i]} == -arch ]; then
25+
i=$((i + 1))
26+
ARCHS+=(${DARGS[$i]})
27+
else
28+
FINARGS+=(${DARGS[$i]})
29+
fi
30+
i=$((i + 1))
31+
done
32+
33+
if [ -z $ARCHS ]; then
34+
ARCHS+=arm64
35+
fi
36+
37+
if [ ${#ARCHS[@]} == 1 ]; then
38+
dst=$ARCHS
39+
if [ $dst == arm64 ]; then
40+
dst=aarch64
41+
fi
42+
exec ${PREFIX}/bin/${dst}-${BUILD}-$PROG ${FINARGS[@]}
43+
fi
44+
45+
LIPOS=()
46+
LOUT=()
47+
for arch in ${ARCHS[@]}; do
48+
echo $arch
49+
dst=$arch
50+
if [ $dst == arm64 ]; then
51+
dst=aarch64
52+
fi
53+
rm -f $TOF
54+
_GCC_WRITE_OUTFILES=$TOF ${PREFIX}/bin/${dst}-${BUILD}-$PROG -arch $arch ${FINARGS[@]}
55+
i=0
56+
for fn in `cat $TOF`; do
57+
echo "Look for $fn"
58+
if [ -e $fn ]; then
59+
cp $fn "${fn}-$arch"
60+
if [ -z ${LOUT[$i]} ]; then
61+
LOUT[$i]=$fn
62+
fi
63+
LIPOS[$i]="${LIPOS[$i]} -arch $arch ${fn}-$arch"
64+
i=$((i + 1))
65+
fi
66+
done
67+
done
68+
i=0
69+
while [ $i -lt ${#LOUT[@]} ]; do
70+
echo lipo -create ${LIPOS[$i][@]} -output ${LOUT[$i]}
71+
lipo -create ${LIPOS[$i][@]} -output ${LOUT[$i]}
72+
i=$((i + 1))
73+
done
74+
rm -f $TOF

build/gcc-12-branch

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
..

0 commit comments

Comments
 (0)