#!/bin/bash # Aethernal2 Mac app 1.1: Wine from CodeWeavers' LGPL CrossOver 26.3.0 sources, built on the owner's Mac. # Everything lives in ~/aetbuild-wine (deleted after packaging). No sudo, no system changes. # Sources (sha256): crossover-sources-26.3.0.tar.gz ac99c8ca4b3848f3e81784135f023df266b61c2345726ea55a50b3e030dd6872 # (https://media.codeweavers.com/pub/crossover/source/), nettle-3.10.tar.gz b4c518adb174e484cb4acea54118f02380c7133771e7e9beb98a0787194ee47c, # freetype-2.13.3.tar.xz 0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289, bison-3.8.2.tar.xz # 9bba0214ccf7f1079c5d59210045227bcf619519840ebfa80cd3849cff5a5bf2 (build tool), LLVM-23.1.5-macOS-ARM64.tar.xz # aba592b4a15e8f90c634c5367b51ec8418b8bcd2829865db331a17dc9418dcff (official LLVM release: clang + lld for the PE side). # Needs Xcode (its SDK; no first-launch step needed), no Homebrew. Unix side: Apple clang -arch x86_64; PE side: mingw-w64 # GCC 15.2 built here from binutils-2.45 / gcc-15.2.0 / mingw-w64-v13.0.0 / mpfr-4.2.2 / mpc-1.3.1 (GNU + SourceForge). # usage: build-wine-mac.sh steps: tools deps wine stage pack (or all) set -euo pipefail B="$HOME/aetbuild-wine"; S="$B/src"; T="$B/tools"; D="$B/deps"; L="$B/logs" export SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk export MACOSX_DEPLOYMENT_TARGET=10.15 export PATH="$T/bin:$T/llvm/bin:/usr/bin:/bin:/usr/sbin:/sbin" J=8 X86_CC="/usr/bin/clang -arch x86_64"; X86_CXX="/usr/bin/clang++ -arch x86_64" HOSTOPT="--build=x86_64-apple-darwin --host=x86_64-apple-darwin" DEPCFLAGS="-O2 -mmacosx-version-min=10.15" DEPLDFLAGS="-mmacosx-version-min=10.15 -Wl,-headerpad_max_install_names -Wl,-rpath,@loader_path" log() { echo "$(date '+%F %T') $*"; } fix_ids() { # install names -> @rpath/ so the libs work from Contents/Resources/wine/lib for f in "$D"/lib/*.dylib; do [ -L "$f" ] && continue install_name_tool -id "@rpath/$(basename "$f")" "$f" { otool -L "$f" | awk 'NR>1 {print $1}' | grep "^$D/lib/" || true; } | while read -r dep; do install_name_tool -change "$dep" "@rpath/$(basename "$dep")" "$f" done done } step_tools() { log "bison" rm -rf "$B/work/bison" && mkdir -p "$B/work" && tar -xJf "$S/bison-3.8.2.tar.xz" -C "$B/work" && mv "$B/work/bison-3.8.2" "$B/work/bison" (cd "$B/work/bison" && ./configure --prefix="$T" --disable-nls >/dev/null && make -j$J >/dev/null && make install >/dev/null) bison --version | head -1 log "llvm (clang, lld, llvm-dlltool, llvm-ar/ranlib/objcopy/strip for the PE side)" rm -rf "$T/llvm" && mkdir -p "$T/llvm" tar -xJf "$S/LLVM-23.1.5-macOS-ARM64.tar.xz" -C "$T/llvm" --strip-components 1 \ '*/bin/clang' '*/bin/clang-23' '*/bin/clang++' '*/bin/lld' '*/bin/ld.lld' '*/bin/lld-link' '*/bin/ld64.lld' \ '*/bin/llvm-dlltool' '*/bin/llvm-ar' '*/bin/llvm-ranlib' '*/bin/llvm-lib' '*/bin/llvm-objcopy' '*/bin/llvm-strip' \ '*/lib/clang' 2>/dev/null || true ls "$T/llvm/bin"; "$T/llvm/bin/clang" --version | head -1 } step_mingw() { # mingw-w64 GCC cross compilers (i686 + x86_64) for the PE side, built from GNU / mingw-w64 sources # Why not LLVM clang for the PE side: the unix side is built with Apple clang, which assumes that callers zero-extend # BOOLEAN/char arguments; LLVM's Windows-ABI code leaves garbage in the upper bits of stack arguments, so e.g. # NtQueryDirectoryObject saw restart=TRUE forever and PowerShell hung in GetLogicalDrives. GCC (what Gcenx and # CodeWeavers use for the PE side) writes the full slot. M="$T/mingw"; WK="$B/work/mingw" rm -rf "$M" "$WK" && mkdir -p "$M" "$WK" export CC="$X86_CC" CXX="$X86_CXX" CFLAGS="-O2" CXXFLAGS="-O2" LDFLAGS="" export MACOSX_DEPLOYMENT_TARGET=14.0 # build tools for this Mac only (the SDK's libc++ no longer supports 10.15) export PATH="$M/bin:$PATH" tar -xJf "$S/binutils-2.45.tar.xz" -C "$WK"; tar -xJf "$S/gcc-15.2.0.tar.xz" -C "$WK"; tar -xjf "$S/mingw-w64-v13.0.0.tar.bz2" -C "$WK" tar -xJf "$S/mpfr-4.2.2.tar.xz" -C "$WK"; tar -xzf "$S/mpc-1.3.1.tar.gz" -C "$WK"; cp -Rp "$S/sources/gnutls/gmp" "$WK/gmp-src" ln -s ../gmp-src "$WK/gcc-15.2.0/gmp"; ln -s ../mpfr-4.2.2 "$WK/gcc-15.2.0/mpfr"; ln -s ../mpc-1.3.1 "$WK/gcc-15.2.0/mpc" for t in x86_64-w64-mingw32 i686-w64-mingw32; do log "binutils $t" mkdir -p "$WK/b-$t" && (cd "$WK/b-$t" && "$WK/binutils-2.45/configure" $HOSTOPT --target=$t --prefix="$M" --disable-nls --disable-werror --disable-multilib --disable-gdb --disable-gprofng --with-system-zlib >/dev/null && make -j$J >/dev/null && make install >/dev/null) log "mingw-w64 headers $t" mkdir -p "$WK/h-$t" && (cd "$WK/h-$t" && "$WK/mingw-w64-v13.0.0/mingw-w64-headers/configure" --host=$t --prefix="$M/$t" >/dev/null && make install >/dev/null) log "gcc (compiler only) $t" mkdir -p "$WK/g-$t" && (cd "$WK/g-$t" && "$WK/gcc-15.2.0/configure" $HOSTOPT --target=$t --prefix="$M" --enable-languages=c --disable-nls --disable-multilib --disable-shared --disable-lto --disable-libssp --disable-libquadmath --disable-libgomp --disable-libatomic --disable-werror --disable-plugin --with-system-zlib >/dev/null && make -j$J all-gcc >/dev/null && make install-gcc >/dev/null) log "mingw-w64 crt $t" if [ $t = i686-w64-mingw32 ]; then libs="--enable-lib32 --disable-lib64"; else libs="--enable-lib64 --disable-lib32"; fi mkdir -p "$WK/c-$t" && (cd "$WK/c-$t" && CC= CXX= CFLAGS= "$WK/mingw-w64-v13.0.0/mingw-w64-crt/configure" --host=$t --prefix="$M/$t" $libs >/dev/null && make -j$J >/dev/null && make install >/dev/null) log "libgcc $t" (cd "$WK/g-$t" && make -j$J all-target-libgcc >/dev/null && make install-target-libgcc >/dev/null) $t-gcc --version | head -1 done } step_deps() { rm -rf "$D" "$B/work/deps" && mkdir -p "$D" "$B/work/deps" export CC="$X86_CC" CXX="$X86_CXX" CFLAGS="$DEPCFLAGS" CXXFLAGS="$DEPCFLAGS" LDFLAGS="$DEPLDFLAGS -L$D/lib" CPPFLAGS="-I$D/include" export PKG_CONFIG_PATH="$D/lib/pkgconfig" log "gmp 6.3.0 (CrossOver tree)" cp -Rp "$S/sources/gnutls/gmp" "$B/work/deps/gmp" (cd "$B/work/deps/gmp" && ./configure $HOSTOPT --prefix="$D" --enable-shared --disable-static --disable-cxx >/dev/null && make -j$J >/dev/null && make install >/dev/null) log "nettle 3.10 (GNU release tarball = the CrossOver tree's version)" tar -xzf "$S/nettle-3.10.tar.gz" -C "$B/work/deps" (cd "$B/work/deps/nettle-3.10" && ./configure $HOSTOPT --prefix="$D" --enable-shared --disable-static --disable-documentation --disable-openssl --with-include-path="$D/include" --with-lib-path="$D/lib" >/dev/null && make -j$J >/dev/null && make install >/dev/null) log "gnutls 3.8.3 (CrossOver tree)" cp -Rp "$S/sources/gnutls/gnutls" "$B/work/deps/gnutls" (cd "$B/work/deps/gnutls" && touch aclocal.m4 && sleep 1 && touch configure && sleep 1 && find . -name Makefile.in -exec touch {} + && touch config.h.in && NETTLE_CFLAGS="-I$D/include" NETTLE_LIBS=-lnettle HOGWEED_CFLAGS="-I$D/include" HOGWEED_LIBS=-lhogweed GMP_CFLAGS="-I$D/include" GMP_LIBS=-lgmp ./configure $HOSTOPT --prefix="$D" --enable-shared --disable-static --with-included-libtasn1 --with-included-unistring \ --without-p11-kit --without-idn --without-zlib --without-brotli --without-zstd --without-tpm --without-tpm2 --disable-doc --disable-manpages --disable-tests \ --disable-tools --disable-cxx --disable-nls --disable-libdane --disable-openssl-compatibility --disable-hardware-acceleration --disable-gost >/dev/null \ && make -j$J >/dev/null && make install >/dev/null) log "freetype 2.13.3 (release tarball = the CrossOver tree's version)" tar -xJf "$S/freetype-2.13.3.tar.xz" -C "$B/work/deps" (cd "$B/work/deps/freetype-2.13.3" && ./configure $HOSTOPT --prefix="$D" --enable-shared --disable-static --without-harfbuzz --without-png --without-brotli --without-bzip2 --with-zlib=no >/dev/null && make -j$J >/dev/null && make install >/dev/null) fix_ids ls -la "$D/lib"/*.dylib for f in "$D"/lib/*.dylib; do [ -L "$f" ] || { echo "$f"; otool -L "$f" | tail -n +2; lipo -archs "$f"; }; done } step_wine() { [ "${FRESH:-0}" = 1 ] && rm -rf "$B/build/wine" mkdir -p "$B/build/wine" && cd "$B/build/wine" export CC="$X86_CC" CXX="$X86_CXX" CFLAGS="-O2 -mmacosx-version-min=10.15" CROSSCFLAGS="-O2" export LDFLAGS="-mmacosx-version-min=10.15 -Wl,-headerpad_max_install_names -Wl,-rpath,@loader_path/../../ -Wl,-rpath,$D/lib -L$D/lib" # the 2nd rpath only for the build tools; removed in stage export CPPFLAGS="-I$D/include" export FREETYPE_CFLAGS="-I$D/include/freetype2" FREETYPE_LIBS="-L$D/lib -lfreetype" export GNUTLS_CFLAGS="-I$D/include" GNUTLS_LIBS="-L$D/lib -lgnutls" export PATH="$T/mingw/bin:$PATH" log "configure" "$S/sources/wine/configure" $HOSTOPT --prefix=/opt/wine --enable-archs=i386,x86_64 --disable-tests --disable-win16 \ --with-mingw --with-coreaudio --with-freetype --with-gnutls \ --without-alsa --without-capi --without-cups --without-dbus --without-ffmpeg --without-fontconfig --without-gettext --without-gettextpo \ --without-gphoto --without-gssapi --without-gstreamer --without-inotify --without-krb5 --without-netapi --without-opencl \ --without-oss --without-pcap --without-pcsclite --without-pulse --without-sane --without-sdl --without-udev --without-usb \ --without-v4l2 --without-vulkan --without-wayland --without-x # Vulkan is not built (the game is D3D8 on wined3d/OpenGL), but CrossOver's win32u/vulkan.c ("CW HACK 25909") uses # SONAME_LIBVULKAN unconditionally: define it in the generated config.h so the file compiles; at run time the # dlopen of libMoltenVK.dylib simply fails (not bundled) and Vulkan is unavailable, as with --without-vulkan. grep -q '^#define SONAME_LIBVULKAN' include/config.h || echo '#define SONAME_LIBVULKAN "libMoltenVK.dylib"' >>include/config.h grep -E "^(i386|x86_64)_CC|PE_ARCHS|SONAME_LIBGNUTLS|SONAME_LIBFREETYPE|SONAME_LIBVULKAN|HAVE_SYS_SYSCTL_H" Makefile include/config.h 2>/dev/null | head log "make -j$J" make -j$J log "wine built" } step_stage() { export PATH="$T/mingw/bin:$PATH" # make install may rebuild PE objects: same mingw GCC as step_wine rm -rf "$B/stage" && mkdir -p "$B/stage" cd "$B/build/wine" && make install DESTDIR="$B/stage" >/dev/null W="$B/stage/opt/wine" rm -rf "$W/share/man" "$W/include" "$W/share/applications" # runtime only: no development tools or import libraries (cd "$W/bin" && rm -f function_grep.pl widl winebuild winecpp winedump wineg++ winegcc winemaker wmc wrc) find "$W/lib/wine" -name '*.a' -delete mkdir -p "$W/lib" for f in "$D"/lib/*.dylib; do cp -a "$f" "$W/lib/"; done install_name_tool -add_rpath @loader_path/../lib "$W/bin/wineserver" 2>/dev/null || true # drop the build-only rpath (an absolute path into the build folder) from every Mach-O file find "$W" -type f \( -name '*.so' -o -name '*.dylib' -o -perm -u+x \) | while read -r f; do file "$f" | grep -q 'Mach-O' || continue install_name_tool -delete_rpath "$D/lib" "$f" 2>/dev/null || true done du -sh "$W"; ls "$W" "$W/bin" "$W/lib" | head -60 otool -L "$W/lib/wine/x86_64-unix/ntdll.so" | head "$W/bin/wine" --version } step_pack() { W="$B/stage/opt/wine"; LIC="$W/licenses"; SRC="$S/sources" rm -rf "$LIC" && mkdir -p "$LIC" cp "$SRC/wine/COPYING.LIB" "$LIC/WINE-COPYING.LIB.txt"; cp "$SRC/wine/LICENSE" "$LIC/WINE-LICENSE.txt"; cp "$SRC/wine/AUTHORS" "$LIC/WINE-AUTHORS.txt" cp "$SRC/gnutls/gnutls/LICENSE" "$LIC/GNUTLS-LICENSE.txt"; cp "$SRC/gnutls/gnutls/doc/COPYING.LESSER" "$LIC/GNUTLS-COPYING.LESSER.txt" cp "$SRC/gnutls/gmp/COPYING.LESSERv3" "$LIC/GMP-COPYING.LESSERv3.txt"; cp "$SRC/gnutls/gmp/COPYINGv2" "$LIC/GMP-COPYINGv2.txt" cp "$B/work/deps/nettle-3.10/COPYING.LESSERv3" "$LIC/NETTLE-COPYING.LESSERv3.txt"; cp "$B/work/deps/nettle-3.10/COPYINGv2" "$LIC/NETTLE-COPYINGv2.txt" cp "$B/work/deps/freetype-2.13.3/LICENSE.TXT" "$LIC/FREETYPE-LICENSE.txt"; cp "$B/work/deps/freetype-2.13.3/docs/FTL.TXT" "$LIC/FREETYPE-FTL.txt" cp "$B/work/deps/freetype-2.13.3/docs/GPLv2.TXT" "$LIC/FREETYPE-GPLv2.txt" out="$B/aethernal2-wine-cx26.3.0-aet1-osx64.tar.xz" rm -f "$out"; (cd "$B/stage/opt" && tar -cJf "$out" wine) shasum -a 256 "$out"; ls -la "$out" } case "${1:-all}" in fixids) fix_ids; for f in "$D"/lib/*.dylib; do [ -L "$f" ] || { echo "$f"; otool -L "$f" | tail -n +2; lipo -archs "$f"; }; done ;; mingw) step_mingw ;; tools) step_tools ;; deps) step_deps ;; wine) step_wine ;; stage) step_stage ;; pack) step_pack ;; all) step_tools; step_mingw; step_deps; step_wine; step_stage; step_pack ;; esac