calog/tools/crossMacFull.sh

211 lines
15 KiB
Bash
Executable file

#!/usr/bin/env bash
# crossMacFull.sh -- cross-build the FULL calog CLI as macOS Mach-O executables for BOTH
# x86_64 (Intel) and arm64 (Apple Silicon) with a single zig toolchain on Linux. No Apple
# SDK: zig ships a libSystem stub (libc/pthreads/BSD sockets/getaddrinfo/dlopen/iconv), which
# is everything calog's OS surface needs.
#
# It is the Darwin sibling of tools/crossWinFull.sh. The Windows-isms are dropped: macOS has
# native pthreads in libSystem (no vendored winpthreads, no -DWINPTHREAD_STATIC), the C++
# engine (Squirrel) links against zig's libc++ via the c++ driver, there is no -ldl, no Win32
# import libs, and no libpq pthread-symbol patch (libpq uses libSystem pthreads directly). The
# compression/archive stack is built here per the crossArchive.sh MUSL xar recipe -- macOS is
# like musl for xar: iconv is in libSystem (empty-dir ICONV_INCLUDE_DIR pin), and there is no
# CommonCrypto without the Apple SDK, so xar's MD5/SHA1 come from the pinned OpenSSL.
#
# Per arch under build/cross/<mac-x64|mac-arm64>/ the heavy deps -- openssl-src (+openssl-repack),
# libxml2, pcre2, libssh2, mariadb, tcl, mruby, postgres-build, the codecs, xz, libarchive -- are
# PINNED (not rebuilt here). Rebuild them reproducibly from vendor/ source with
# `./tools/crossDeps.sh mac-x64` (or mac-arm64) before running this script; that is what makes the
# full cross build reproducible from a clean checkout.
# This script still builds: sqlite, enet (unix.c backend), the engine VMs (lua/quickjs/squirrel/
# my-basic/berry/s7/wren/janet), and all of calog's own source, then links the complete runner.
set -eu
cd /home/scott/claude/calog
R=/home/scott/claude/calog
ZIG=${ZIG:-${CALOG_ZIG:-/home/scott/zig/current/zig}}
[ -x "$ZIG" ] || { echo "error: zig not found at $ZIG. Set ZIG=/path/to/zig (a PERMANENT install, not a session/temp dir); see https://ziglang.org/download/." >&2; exit 1; }
export CALOG_ZIG="$ZIG" # the build/cross/bin wrappers resolve zig via $CALOG_ZIG
AR="$R/build/cross/bin/zar"
# macOS Keychain trust for the HTTPS client (calogHttp httpLoadMacRoots). It needs the real Apple SDK
# framework headers (Security, CoreFoundation, libDER), which zig does NOT ship. When an SDK is
# present, calogHttp is compiled with -DCALOG_MAC_KEYCHAIN_TRUST against those headers and linked
# against the minimal tools/macStubs/*.tbd (zig's Mach-O linker segfaults on the real multi-target
# SDK .tbd, so we link tiny hand-written stubs that export only the few symbols we call; the binary
# still imports the real system frameworks by install-name at runtime). Without an SDK the build
# omits Keychain trust and HTTPS falls back to SSL_CERT_FILE / CALOG_CA_BUNDLE. Set CALOG_MACSDK to
# point at a MacOSX*.sdk directory (default: a permanent install alongside zig).
MACSDK="${CALOG_MACSDK:-/home/scott/macos-sdk/MacOSX13.3.sdk}"
MACTRUST=""
MACTRUSTLINK=""
if [ -d "$MACSDK/System/Library/Frameworks/Security.framework" ]; then
MACTRUST="-DCALOG_MAC_KEYCHAIN_TRUST -F$MACSDK/System/Library/Frameworks -isystem $MACSDK/usr/include"
# Link stubs (resolved to the real system libraries by install-name at runtime): the Keychain-trust
# frameworks, plus libiconv, which the SDK-built libarchive references for its xar support.
MACTRUSTLINK="$R/tools/macStubs/CoreFoundation.tbd $R/tools/macStubs/Security.tbd $R/tools/macStubs/libiconv.tbd"
echo "[sdk] macOS Keychain trust + libarchive xar ENABLED (SDK: $MACSDK)"
else
echo "[sdk] macOS SDK not found at $MACSDK -- Keychain trust DISABLED (HTTPS needs SSL_CERT_FILE/CALOG_CA_BUNDLE); set CALOG_MACSDK to enable"
fi
# ---------------------------------------------------------------------------------------------
# Helpers (compile a directory of sources into one static archive).
# ---------------------------------------------------------------------------------------------
mklib(){ local out=$1 cc=$2 vo=$3; shift 3; local fl=(); while [ "$1" != -- ]; do fl+=("$1"); shift; done; shift
local objs=() s b; rm -rf "$vo/$out"; mkdir -p "$vo/$out"
for s in "$@"; do b=$(basename "$s"); b=${b%.*}; "$cc" "${fl[@]}" -c "$s" -o "$vo/$out/$b.o"; objs+=("$vo/$out/$b.o"); done
"$AR" rcs "$out" "${objs[@]}"; echo " [lib] $(basename "$out")"; }
# build_codecs <cc> <outdir> -- zlib/bzip2/lz4/zstd object-rule codecs (zlib needs
# -DHAVE_UNISTD_H for lseek, as in crossArchive.sh).
build_codecs(){ local CC=$1 O=$2 c
mkdir -p "$O/obj" "$O/lib"
for c in vendor/zlib/*.c; do "$CC" -std=c11 -D_GNU_SOURCE -DHAVE_UNISTD_H -w -O2 -Ivendor/zlib -c "$c" -o "$O/obj/z_$(basename "$c" .c).o"; done
"$AR" rcs "$O/lib/libz.a" "$O"/obj/z_*.o
for c in vendor/bzip2/*.c; do "$CC" -std=c11 -D_GNU_SOURCE -w -O2 -Ivendor/bzip2 -c "$c" -o "$O/obj/bz_$(basename "$c" .c).o"; done
"$AR" rcs "$O/lib/libbz2.a" "$O"/obj/bz_*.o
for c in vendor/lz4/*.c; do "$CC" -std=c11 -D_GNU_SOURCE -w -O2 -Ivendor/lz4 -c "$c" -o "$O/obj/l4_$(basename "$c" .c).o"; done
"$AR" rcs "$O/lib/liblz4.a" "$O"/obj/l4_*.o
for c in vendor/zstd/common/*.c vendor/zstd/compress/*.c vendor/zstd/decompress/*.c; do "$CC" -std=c11 -D_GNU_SOURCE -w -O2 -DZSTD_DISABLE_ASM -Ivendor/zstd -c "$c" -o "$O/obj/zs_$(basename "$c" .c).o"; done
"$AR" rcs "$O/lib/libzstd.a" "$O"/obj/zs_*.o
echo " [lib] libz/libbz2/liblz4/libzstd.a"; }
# build_xz <toolchain> <outdir> -- vendored xz/liblzma via its own CMake, out of tree.
build_xz(){ cmake -S vendor/xz -B "$2/xz" -DCMAKE_TOOLCHAIN_FILE="$1" \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \
-DENABLE_THREADS=OFF -DENABLE_NLS=OFF -DENABLE_DOXYGEN=OFF >"$2/xz-cfg.log" 2>&1
cmake --build "$2/xz" --target liblzma -j >"$2/xz-build.log" 2>&1
echo " [lib] liblzma.a"; }
# build_libarchive_xar <toolchain> <outdir> -- libarchive WITH xar, exactly the crossArchive.sh
# MUSL recipe but with the PINNED mac OpenSSL + libxml2. iconv comes from libSystem (empty-dir
# ICONV_INCLUDE_DIR pin so FIND_PATH does not grab host glibc headers; zig resolves iconv.h).
# CommonCrypto is unavailable (no Apple SDK), so OpenSSL supplies xar's MD5/SHA1.
build_libarchive_xar(){ local TC=$1 O=$2
mkdir -p "$O/noinc"
cmake -S vendor/libarchive -B "$O/libarchive-xar" -DCMAKE_TOOLCHAIN_FILE="$TC" \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DENABLE_TEST=OFF -DENABLE_INSTALL=OFF \
-DENABLE_TAR=OFF -DENABLE_CPIO=OFF -DENABLE_CAT=OFF -DENABLE_UNZIP=OFF \
-DENABLE_ACL=OFF -DENABLE_XATTR=OFF -DENABLE_LIBB2=OFF \
-DENABLE_LZO=OFF -DENABLE_NETTLE=OFF -DENABLE_MBEDTLS=OFF -DENABLE_PCREPOSIX=OFF -DENABLE_PCRE2POSIX=OFF \
-DENABLE_EXPAT=OFF -DPOSIX_REGEX_LIB=NONE \
-DENABLE_ICONV=ON -DICONV_INCLUDE_DIR="$O/noinc" \
-DENABLE_LIBXML2=ON "-DLIBXML2_INCLUDE_DIR=$R/vendor/libxml2/include;$O/libxml2" -DLIBXML2_LIBRARY="$O/libxml2/libxml2.a" \
-DENABLE_OPENSSL=ON -DOPENSSL_ROOT_DIR="$O/openssl-src" -DOPENSSL_INCLUDE_DIR="$O/openssl-src/include" \
-DOPENSSL_SSL_LIBRARY="$O/openssl-repack/libssl.a" -DOPENSSL_CRYPTO_LIBRARY="$O/openssl-repack/libcrypto.a" \
-DENABLE_ZLIB=ON -DZLIB_INCLUDE_DIR="$R/vendor/zlib" -DZLIB_LIBRARY="$O/lib/libz.a" \
-DENABLE_BZip2=ON -DBZIP2_INCLUDE_DIR="$R/vendor/bzip2" -DBZIP2_LIBRARIES="$O/lib/libbz2.a" \
-DENABLE_LZMA=ON -DLIBLZMA_INCLUDE_DIR="$R/vendor/xz/src/liblzma/api" -DLIBLZMA_LIBRARY="$O/xz/liblzma.a" \
-DENABLE_ZSTD=ON -DZSTD_INCLUDE_DIR="$R/vendor/zstd" -DZSTD_LIBRARY="$O/lib/libzstd.a" \
-DENABLE_LZ4=ON -DLZ4_INCLUDE_DIR="$R/vendor/lz4" -DLZ4_LIBRARY="$O/lib/liblz4.a" \
>"$O/la-xar-cfg.log" 2>&1
cmake --build "$O/libarchive-xar" --target archive_static -j >"$O/la-xar-build.log" 2>&1
echo " [lib] libarchive.a (+xar via libxml2/openssl)"; }
# =============================================================================================
# build_arch <mac-x64|mac-arm64>
# =============================================================================================
build_arch(){
local A=$1
local CC="$R/build/cross/bin/zcc-$A"
local CXX="$R/build/cross/bin/zcxx-$A"
local TC="$R/build/cross/toolchain-$A.cmake"
local M="$R/build/cross/$A"
local LIB="$M/lib" VO="$M/vmobj" CO="$M/cobj"
mkdir -p "$LIB" "$VO" "$CO"
echo "==================== $A ===================="
# --- 1. compression / archive stack -------------------------------------------------------
build_codecs "$CC" "$M"
build_xz "$TC" "$M"
build_libarchive_xar "$TC" "$M"
# --- 2. engine VMs + sqlite + enet (unix.c backend on macOS, native HAS_* defines) --------
mklib "$LIB/liblua.a" "$CC" "$VO" -std=c99 -w -O2 -DLUA_USE_MACOSX -Ivendor/lua/src -- $(ls vendor/lua/src/*.c | grep -vE '/(lua|luac)\.c$')
mklib "$LIB/libquickjs.a" "$CC" "$VO" -std=c11 -w -O1 -D_GNU_SOURCE -Ivendor/quickjs -- vendor/quickjs/quickjs.c vendor/quickjs/libregexp.c vendor/quickjs/libunicode.c vendor/quickjs/dtoa.c
mklib "$LIB/libsquirrel.a" "$CXX" "$VO" -std=c++11 -w -O1 -D_SQ64 -DSQUSEDOUBLE -Ivendor/squirrel-src/include -Ivendor/squirrel-src/squirrel -- $(ls vendor/squirrel-src/squirrel/*.cpp)
mklib "$LIB/libmybasic.a" "$CC" "$VO" -std=gnu11 -w -O1 -DMB_DOUBLE_FLOAT -- vendor/ourbasic/ourBasic.c
mklib "$LIB/libberry.a" "$CC" "$VO" -std=c99 -w -O1 -Ivendor/berry/src -Ivendor/berry -- $(ls vendor/berry/src/*.c) vendor/berry/default/be_port.c vendor/berry/default/be_modtab.c
mklib "$LIB/libs7.a" "$CC" "$VO" -std=c99 -w -O1 -D_GNU_SOURCE -Ivendor/s7 -- vendor/s7/s7.c
mklib "$LIB/libwren.a" "$CC" "$VO" -std=c99 -w -O1 -Ivendor/wren -- vendor/wren/wren.c
mklib "$LIB/libjanet.a" "$CC" "$VO" -std=c11 -w -O2 -DJANET_NO_NET -DJANET_NO_PROCESSES -DJANET_NO_DYNAMIC_MODULES -Ivendor/janet -- vendor/janet/janet.c
mklib "$LIB/libsqlite3.a" "$CC" "$VO" -std=c11 -w -O2 -DSQLITE_THREADSAFE=1 -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_OMIT_LOAD_EXTENSION -Ivendor/sqlite -- vendor/sqlite/sqlite3.c
mklib "$LIB/libenet.a" "$CC" "$VO" -std=c11 -w -O2 -D_GNU_SOURCE -DHAS_FCNTL=1 -DHAS_POLL=1 -DHAS_GETADDRINFO=1 -DHAS_GETNAMEINFO=1 -DHAS_GETHOSTBYNAME_R=1 -DHAS_GETHOSTBYADDR_R=1 -DHAS_INET_PTON=1 -DHAS_INET_NTOP=1 -DHAS_MSGHDR_FLAGS=1 -DHAS_SOCKLEN_T=1 -Ivendor/enet/include -- $(ls vendor/enet/*.c | grep -vE '/win32\.c$')
# --- 3. compile calog's own objects (core + 10 engine adapters/engines + 19 libs + main) --
local INC="-Isrc -Isrc/lua -Isrc/mybasic -Isrc/squirrel -Isrc/js -Isrc/berry -Isrc/s7 -Isrc/wren -Isrc/mruby -Isrc/tcl -Isrc/janet -Ilibs"
# -Wall -Wextra (not -Werror: vendored headers under clang-cross are not warning-clean) so the
# macOS-only #ifdef branches of calog's own sources get compiler scrutiny they never get on Linux.
local BASE="-std=c11 -Wall -Wextra -O1 -g -pthread $INC"
local ENGDEF="-DCALOG_WITH_LUA -DCALOG_WITH_JS -DCALOG_WITH_SQUIRREL -DCALOG_WITH_MYBASIC -DCALOG_WITH_BERRY -DCALOG_WITH_S7 -DCALOG_WITH_WREN -DCALOG_WITH_MRUBY -DCALOG_WITH_TCL -DCALOG_WITH_JANET"
cc(){ local out=$1 src=$2; shift 2; "$CC" $BASE "$@" -c "$src" -o "$CO/$out.o"; }
cc broker src/broker.c
cc value src/value.c
cc context src/context.c
cc luaEngine src/lua/luaEngine.c; cc jsEngine src/js/jsEngine.c
cc squirrelEngine src/squirrel/squirrelEngine.c; cc mybasicEngine src/mybasic/mybasicEngine.c
cc berryEngine src/berry/berryEngine.c; cc s7Engine src/s7/s7Engine.c
cc wrenEngine src/wren/wrenEngine.c; cc mrubyEngine src/mruby/mrubyEngine.c
cc tclEngine src/tcl/tclEngine.c; cc janetEngine src/janet/janetEngine.c
cc luaAdapter src/lua/luaAdapter.c -Ivendor/lua/src
cc jsAdapter src/js/jsAdapter.c -Ivendor/quickjs
cc squirrelAdapter src/squirrel/squirrelAdapter.c -D_SQ64 -DSQUSEDOUBLE -Ivendor/squirrel-src/include
cc mybasicAdapter src/mybasic/mybasicAdapter.c -Ivendor/ourbasic -DMB_DOUBLE_FLOAT
cc berryAdapter src/berry/berryAdapter.c -Ivendor/berry/src -Ivendor/berry
cc s7Adapter src/s7/s7Adapter.c -Ivendor/s7
cc wrenAdapter src/wren/wrenAdapter.c -Ivendor/wren
cc mrubyAdapter src/mruby/mrubyAdapter.c -Ivendor/mruby/include -Ivendor/mruby/build/cross-$A/include
cc tclAdapter src/tcl/tclAdapter.c -Ivendor/tcl/generic -DSTATIC_BUILD
cc janetAdapter src/janet/janetAdapter.c -Ivendor/janet
cc calogMain src/calogMain.c $ENGDEF
cc calogArchive libs/calogArchive.c -Ivendor/libarchive/libarchive
cc calogCrypto libs/calogCrypto.c -I"$M/openssl-src/include"
cc calogCsv libs/calogCsv.c
cc calogDbFull libs/calogDb.c -Ivendor/sqlite -Ivendor/postgres/src/interfaces/libpq -Ivendor/postgres/src/include -I"$M/postgres-build/src/include" -Ivendor/mariadb/include -I"$M/mariadb/include" -DCALOG_WITH_SQLITE -DCALOG_WITH_PG -DCALOG_WITH_MYSQL
cc calogExport libs/calogExport.c
cc calogFs libs/calogFs.c
cc calogHttp libs/calogHttp.c -I"$M/openssl-src/include" $MACTRUST
cc calogJson libs/calogJson.c
cc calogKv libs/calogKv.c
cc calogNet libs/calogNet.c -Ivendor/enet/include -I"$M/openssl-src/include"
cc calogProc libs/calogProc.c
cc calogPubsub libs/calogPubsub.c
cc calogRegex libs/calogRegex.c -I"$M/pcre2" -DPCRE2_STATIC
cc calogSsh libs/calogSsh.c -Ivendor/libssh2/include
cc calogTask libs/calogTask.c $ENGDEF
cc calogTime libs/calogTime.c
cc calogTimer libs/calogTimer.c
cc calogXml libs/calogXml.c -Ivendor/libxml2/include -I"$M/libxml2" -DLIBXML_STATIC
cc calogHandle libs/calogHandle.c
echo " [obj] all calog objects compiled"
# --- 4. link the full Mach-O. C++ driver pulls zig's libc++ (Squirrel). Native pthreads,
# sockets, DNS, dlopen, iconv all resolve against libSystem -- no -ldl, no sys libs.
# The PG frontend archives are circular, so they go in a --start-group with the
# separately-shipped fe_memutils.o (excluded from libpgcommon_shlib.a). OpenSSL is
# linked from openssl-repack/ (the BSD-format __.SYMDEF archives): OpenSSL's own build
# emitted GNU-format .a files under openssl-src/, which zig's Mach-O linker cannot
# parse ("unknown cpu architecture"); the repack is the same objects in BSD ar form. --
local VMLIBS="$LIB/liblua.a $LIB/libquickjs.a $LIB/libsquirrel.a $LIB/libmybasic.a $LIB/libberry.a $LIB/libs7.a $LIB/libwren.a $LIB/libjanet.a $M/mruby/libmruby.a $M/tcl/libtcl9.0.a $LIB/libsqlite3.a $LIB/libenet.a"
local ARCH="$M/libarchive-xar/libarchive/libarchive.a $M/libxml2/libxml2.a $M/xz/liblzma.a $LIB/libzstd.a $LIB/liblz4.a $LIB/libbz2.a $LIB/libz.a"
local PG="$M/postgres-build/src/interfaces/libpq/libpq.a $M/postgres-build/src/common/libpgcommon_shlib.a $M/postgres-build/src/port/libpgport_shlib.a $M/postgres-build/src/common/fe_memutils.o"
# libssh2 is built Release (-O2, no UBSan) by tools/crossDeps.sh, so no __ubsan_handle_* symbols to
# resolve and NO -fsanitize=undefined at link -- matching the Windows/Linux binaries (which never
# carried the UBSan runtime). If you instead use an old UBSan-instrumented libssh2.a, add
# `-fsanitize=undefined` back here to pull zig's UBSan runtime.
"$CXX" -O1 -g -o "$M/calog" $CO/*.o \
$VMLIBS "$M/libssh2/_build/src/libssh2.a" $ARCH "$M/pcre2/libpcre2-8.a" \
-Wl,--start-group $PG -Wl,--end-group \
"$M/mariadb/libmariadb/libmariadbclient.a" "$M/openssl-repack/libssl.a" "$M/openssl-repack/libcrypto.a" \
$MACTRUSTLINK \
-lm
echo " [BIN] $M/calog"
file "$M/calog"
}
# Build the arch(s) named on the command line, or both by default.
ARCHS=("$@")
[ ${#ARCHS[@]} -eq 0 ] && ARCHS=(mac-x64 mac-arm64)
for a in "${ARCHS[@]}"; do build_arch "$a"; done
echo "== done =="