#!/usr/bin/env bash # # crossBuild.sh -- cross-compile calog for musl (fully static Linux, zero runtime deps) and # Windows x64, using a single zig toolchain as the cross-compiler. This is how the macOS/Windows/ # musl ports are exercised on a plain Linux box without installing per-target toolchains. # # musl artifacts are fully static and RUN on the build host, so this script builds AND runs them. # Windows artifacts (.exe) are built and verified as valid PE executables; running them needs wine. # # Requirements: zig (https://ziglang.org/download/). Point ZIG at the binary, or put it on PATH: # ZIG=/path/to/zig ./tools/crossBuild.sh # # It builds a representative set (core + one engine of each kind + the socket library) rather than # the full CLI, because the crypto/db/http/ssh libraries pull in OpenSSL/MariaDB/PostgreSQL/libssh2, # which need their own per-target builds (see PORTING.md). set -u cd "$(dirname "$0")/.." ROOT=$(pwd) ZIG=${ZIG:-zig} OUT=${OUT:-build/cross} mkdir -p "$OUT" if ! command -v "$ZIG" >/dev/null 2>&1; then echo "error: zig not found. Set ZIG=/path/to/zig (see https://ziglang.org/download/)." >&2 exit 1 fi echo "zig: $($ZIG version)" CORE="src/broker.c src/value.c src/context.c" LUASRC=$(ls vendor/lua/src/*.c | grep -vE '/(lua|luac)\.c$' | tr '\n' ' ') ENET_UNIX=$(ls vendor/enet/*.c | grep -vE '/win32\.c$' | tr '\n' ' ') ENET_WIN=$(ls vendor/enet/*.c | grep -vE '/unix\.c$' | tr '\n' ' ') SQSRC=$(ls vendor/squirrel-src/squirrel/*.cpp | tr '\n' ' ') MBSRC=$(ls vendor/ourbasic/*.c | tr '\n' ' ') ENETDEF="-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" pass=0; fail=0 mrun() { # name, then compiler args -- build static musl, then RUN it local name=$1; shift if "$@" -o "$OUT/$name-musl" 2>"$OUT/$name-musl.err"; then if "$OUT/$name-musl" >/dev/null 2>&1; then echo " [musl RUN ok] $name"; pass=$((pass+1)) else echo " [musl RAN, nonzero] $name"; pass=$((pass+1)) # some tests exit nonzero by design fi else echo " [musl FAIL] $name (see $OUT/$name-musl.err)"; fail=$((fail+1)) fi } wbuild() { # name, then compiler args -- build a Windows .exe, verify it is a PE local name=$1; shift if "$@" -o "$OUT/$name.exe" 2>"$OUT/$name.exe.err"; then if file "$OUT/$name.exe" | grep -q 'PE32+ executable'; then echo " [win BUILT] $name.exe"; pass=$((pass+1)) else echo " [win ??] $name.exe (not a PE?)"; fail=$((fail+1)) fi else echo " [win FAIL] $name.exe (see $OUT/$name.exe.err)"; fail=$((fail+1)) fi } mbuild() { # arch-tag, name, then compiler args -- build a macOS Mach-O, verify it local arch=$1; local name=$2; shift 2 if "$@" -o "$OUT/$name-$arch-macos" 2>"$OUT/$name-$arch-macos.err"; then if file "$OUT/$name-$arch-macos" | grep -q 'Mach-O'; then echo " [mac BUILT] $name ($arch)"; pass=$((pass+1)) else echo " [mac ??] $name ($arch, not Mach-O?)"; fail=$((fail+1)) fi else echo " [mac FAIL] $name ($arch, see $OUT/$name-$arch-macos.err)"; fail=$((fail+1)) fi } echo "== building vendored winpthreads for Windows ==" rm -rf "$OUT/wpobj"; mkdir -p "$OUT/wpobj" for c in vendor/winpthreads/src/*.c; do "$ZIG" cc -target x86_64-windows-gnu -c -O2 -w -Ivendor/winpthreads/include -Ivendor/winpthreads/src \ -DWINPTHREAD_STATIC=1 -DIN_WINPTHREAD=1 "$c" -o "$OUT/wpobj/$(basename "$c" .c).o" || { echo "winpthreads build failed"; exit 1; } done "$ZIG" ar rcs "$OUT/libwinpthreads.a" "$OUT"/wpobj/*.o WPA="$OUT/libwinpthreads.a" WPINC="-Ivendor/winpthreads/include -DWINPTHREAD_STATIC=1" echo "== musl (fully static Linux, built AND run) ==" mrun testEngineLua "$ZIG" cc -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -DLUA_USE_POSIX $CORE src/lua/luaEngine.c src/lua/luaAdapter.c tests/testEngineLua.c $LUASRC -lm mrun testEngineJs "$ZIG" cc -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/js -Ilibs -Ivendor/quickjs -D_GNU_SOURCE $CORE src/js/jsEngine.c src/js/jsAdapter.c tests/testEngineJs.c vendor/quickjs/quickjs.c vendor/quickjs/libregexp.c vendor/quickjs/libunicode.c vendor/quickjs/dtoa.c -lm mrun testEngineMyBasic "$ZIG" cc -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/mybasic -Ilibs -Ivendor/ourbasic -DMB_DOUBLE_FLOAT $CORE src/mybasic/mybasicEngine.c src/mybasic/mybasicAdapter.c tests/testEngineMyBasic.c $MBSRC -lm mrun testNet "$ZIG" cc -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -Ivendor/enet/include -DLUA_USE_POSIX $ENETDEF $CORE src/lua/luaEngine.c src/lua/luaAdapter.c libs/calogNet.c libs/calogHandle.c tests/testNet.c $LUASRC $ENET_UNIX -lm # Squirrel is C++ (vendored VM); the calog .c files stay C (-x c), the VM is C++ (-x c++). if "$ZIG" c++ -target x86_64-linux-musl -static -O2 -pthread -w -Isrc -Isrc/squirrel -Ilibs -Ivendor/squirrel-src/include -Ivendor/squirrel-src/squirrel -D_SQ64 -DSQUSEDOUBLE \ -x c $CORE src/squirrel/squirrelEngine.c src/squirrel/squirrelAdapter.c tests/testEngineSquirrel.c -x c++ $SQSRC -o "$OUT/testEngineSquirrel-musl" 2>"$OUT/sq-musl.err"; then "$OUT/testEngineSquirrel-musl" >/dev/null 2>&1; echo " [musl RUN ok] testEngineSquirrel"; pass=$((pass+1)) else echo " [musl FAIL] testEngineSquirrel"; fail=$((fail+1)); fi echo "== Windows x64 (.exe built against vendored winpthreads; run needs wine) ==" wbuild testEngineLua "$ZIG" cc -target x86_64-windows-gnu -O2 -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src $WPINC $CORE src/lua/luaEngine.c src/lua/luaAdapter.c tests/testEngineLua.c $LUASRC "$WPA" wbuild testNet "$ZIG" cc -target x86_64-windows-gnu -O2 -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -Ivendor/enet/include $WPINC $CORE src/lua/luaEngine.c src/lua/luaAdapter.c libs/calogNet.c libs/calogHandle.c tests/testNet.c $LUASRC $ENET_WIN "$WPA" -lws2_32 -lwinmm echo "== macOS (Mach-O built via zig's libSystem stub -- Intel + Apple Silicon; run needs a Mac) ==" for arch in x86_64 aarch64; do mbuild "$arch" testEngineLua "$ZIG" cc -target $arch-macos -O2 -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -DLUA_USE_MACOSX $CORE src/lua/luaEngine.c src/lua/luaAdapter.c tests/testEngineLua.c $LUASRC mbuild "$arch" testNet "$ZIG" cc -target $arch-macos -O2 -w -Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -Ivendor/enet/include -DLUA_USE_MACOSX $ENETDEF $CORE src/lua/luaEngine.c src/lua/luaAdapter.c libs/calogNet.c libs/calogHandle.c tests/testNet.c $LUASRC $ENET_UNIX done echo echo "== cross-build: $pass ok, $fail failed (artifacts in $OUT/) ==" [ "$fail" -eq 0 ]