calog/tools/crossArchive.sh

210 lines
14 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# crossArchive.sh -- cross-compile calog's compression/archive stack (libarchive + its 5 codecs:
# zlib, bzip2, lz4, zstd, xz/liblzma) for musl (fully static Linux, zero runtime deps) and Windows
# x64, using a single zig toolchain as the cross-compiler. Companion to crossBuild.sh, which proves
# the core+engines+sockets; this proves the archive library, the one CMake-heavy vendored stack.
#
# The musl artifact is fully static and RUNS on the build host, so this script builds AND runs a
# static testArchive (real round-trip through every codec). The Windows artifact is a testArchive.exe
# verified as a valid PE (running it needs a Windows host or wine).
#
# Scope: the MUSL target is full-featured -- it also cross-builds OpenSSL + libxml2 (iconv is in musl's
# libc) so its libarchive has XAR read+write, and the static testArchive round-trips xar on the host.
# The WINDOWS target is codecs + the other formats only; cross XAR for Windows additionally needs
# OpenSSL + libxml2 + win-iconv built for mingw, which is not done yet (a follow-up).
#
# Requirements: zig (https://ziglang.org/download/) and cmake. Point ZIG at the binary or PATH it:
# ZIG=/path/to/zig ./tools/crossArchive.sh
#
# Why zig for the CMake libs: xz + libarchive build out of tree with CMake. zig is wrapped as a
# single-binary compiler (bin/zcc-*) so CMake sees a normal cc. The musl toolchain deliberately does
# NOT set CMAKE_SYSTEM_NAME -- musl-static binaries run on this Linux host, so the build stays
# "native" and CMake's try_run feature-detection works. The Windows toolchain DOES set it, so CMake
# enters cross mode and skips its run-only checks (libarchive guards its one CHECK_C_SOURCE_RUNS on
# CMAKE_CROSSCOMPILING; xz has none), so nothing needs pre-seeding.
set -u
cd "$(dirname "$0")/.."
R=$(pwd)
ZIG=${ZIG:-${CALOG_ZIG:-/home/scott/zig/current/zig}}
X=${OUT:-build/cross}
XABS="$R/$X"
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
command -v cmake >/dev/null 2>&1 || { echo "error: cmake not found." >&2; exit 1; }
ZIGABS=$(command -v "$ZIG"); ZIGABS=$(readlink -f "$ZIGABS" 2>/dev/null || echo "$ZIGABS")
echo "zig: $($ZIG version)"
echo "cmake: $(cmake --version | head -1)"
mkdir -p "$XABS/bin"
# --- single-binary compiler/archiver wrappers with the resolved zig path baked in (ephemeral) ---
gen() { printf '#!/bin/sh\nexec "%s" %s "$@"\n' "$ZIGABS" "$2" > "$XABS/bin/$1"; chmod +x "$XABS/bin/$1"; }
gen zcc-musl "cc -target x86_64-linux-musl"
gen zcc-win "cc -target x86_64-windows-gnu"
gen zar "ar"
gen zranlib "ranlib"
# musl toolchain: no CMAKE_SYSTEM_NAME -> native build, try_run works with static musl on the host.
cat > "$XABS/toolchain-musl.cmake" <<EOF
set(CMAKE_C_COMPILER "$XABS/bin/zcc-musl")
set(CMAKE_AR "$XABS/bin/zar")
set(CMAKE_RANLIB "$XABS/bin/zranlib")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-static")
EOF
# windows toolchain: CMAKE_SYSTEM_NAME=Windows -> cross mode, run-only checks auto-skipped.
cat > "$XABS/toolchain-win.cmake" <<EOF
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER "$XABS/bin/zcc-win")
set(CMAKE_AR "$XABS/bin/zar")
set(CMAKE_RANLIB "$XABS/bin/zranlib")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
EOF
pass=0; fail=0
CORE="src/broker.c src/value.c src/context.c"
LUASRC=$(ls vendor/lua/src/*.c | grep -vE '/(lua|luac)\.c$' | tr '\n' ' ')
# build_codecs <cc-wrapper> <outdir> -- the 4 object-rule codecs (zlib needs -DHAVE_UNISTD_H so it
# includes <unistd.h> for lseek; configure normally sets that, our object-rule build must pass it).
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" || return 1; done
"$XABS/bin/zar" 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" || return 1; done
"$XABS/bin/zar" 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" || return 1; done
"$XABS/bin/zar" 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" || return 1; done
"$XABS/bin/zar" rcs "$O/lib/libzstd.a" "$O"/obj/zs_*.o
}
# build_xz <toolchain> <outdir> -- vendored xz/liblzma (libarchive's lzma + 7-zip backend).
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 || { echo " xz configure FAILED (see $2/xz-cfg.log)"; return 1; }
cmake --build "$2/xz" --target liblzma -j >"$2/xz-build.log" 2>&1 || { echo " xz build FAILED (see $2/xz-build.log)"; return 1; }
}
# build_cmake_libs <toolchain> <outdir> -- xz then libarchive WITHOUT xar, each codec backend pinned
# to the just-built cross libs (headers are arch-independent -> include dirs stay the vendored source).
build_cmake_libs() {
local TC=$1 O=$2
build_xz "$TC" "$O" || return 1
cmake -S vendor/libarchive -B "$O/libarchive" -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_ICONV=OFF -DENABLE_LIBB2=OFF \
-DENABLE_LIBXML2=OFF -DENABLE_EXPAT=OFF -DENABLE_OPENSSL=OFF \
-DENABLE_LZO=OFF -DENABLE_NETTLE=OFF -DENABLE_MBEDTLS=OFF -DENABLE_PCREPOSIX=OFF -DENABLE_PCRE2POSIX=OFF \
-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-cfg.log" 2>&1 || { echo " libarchive configure FAILED (see $O/la-cfg.log)"; return 1; }
cmake --build "$O/libarchive" --target archive_static -j >"$O/la-build.log" 2>&1 || { echo " libarchive build FAILED (see $O/la-build.log)"; return 1; }
}
# --- XAR (read+write) support for musl. libarchive's xar needs an XML lib + MD5/SHA1 + iconv; musl
# supplies iconv in libc, so only OpenSSL (MD5/SHA1) and libxml2 must be cross-built. ---
build_openssl_musl() { # <outdir>
local O=$1 SRC="$1/openssl-src"
# OpenSSL cannot build out-of-tree against a source tree already configured in-tree (which the
# native build leaves vendor/openssl in), so build in a fresh COPY -- vendor/openssl is untouched.
rm -rf "$SRC"; cp -r "$R/vendor/openssl" "$SRC" || return 1
# Drop the copied glibc objects (cp preserves mtimes, so a plain make would treat them as current).
find "$SRC" -type f \( -name '*.o' -o -name '*.a' -o -name '*.d' \) -delete
rm -f "$SRC/configdata.pm" "$SRC/Makefile"
( cd "$SRC" && perl ./Configure linux-x86_64 no-shared no-tests no-docs CC="$XABS/bin/zcc-musl" >"$O/openssl-cfg.log" 2>&1 \
&& make -j4 build_libs >"$O/openssl-build.log" 2>&1 ) || { echo " openssl-musl FAILED (see $O/openssl-build.log)"; return 1; }
}
build_libxml2_musl() { # <outdir>
local O=$1
cmake -S vendor/libxml2 -B "$O/libxml2" -DCMAKE_TOOLCHAIN_FILE="$XABS/toolchain-musl.cmake" \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \
-DLIBXML2_WITH_PYTHON=OFF -DLIBXML2_WITH_PROGRAMS=OFF -DLIBXML2_WITH_TESTS=OFF \
-DLIBXML2_WITH_HTTP=OFF -DLIBXML2_WITH_FTP=OFF -DLIBXML2_WITH_ICU=OFF \
-DLIBXML2_WITH_LZMA=OFF -DLIBXML2_WITH_ZLIB=OFF -DLIBXML2_WITH_ICONV=OFF \
-DLIBXML2_WITH_MODULES=OFF -DLIBXML2_WITH_CATALOG=OFF -DLIBXML2_WITH_THREADS=ON >"$O/libxml2-cfg.log" 2>&1 \
&& cmake --build "$O/libxml2" --target LibXml2 -j >"$O/libxml2-build.log" 2>&1 || { echo " libxml2-musl FAILED (see $O/libxml2-build.log)"; return 1; }
}
build_libarchive_xar_musl() { # <outdir> -- libarchive with xar, iconv+libxml2+openssl pinned to the musl builds.
local O=$1
# Empty dir: satisfies libarchive's IF(ICONV_INCLUDE_DIR) so FIND_PATH(iconv.h) does NOT return
# the host /usr/include (glibc), whose headers break the musl compile. zig cc finds musl's iconv.h
# implicitly, so an empty -I is enough. LIBXML2_INCLUDE_DIR is a two-entry list (source API +
# the build dir with the generated xmlversion.h).
mkdir -p "$O/noinc"
cmake -S vendor/libarchive -B "$O/libarchive-xar" -DCMAKE_TOOLCHAIN_FILE="$XABS/toolchain-musl.cmake" \
-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_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-src/libssl.a" -DOPENSSL_CRYPTO_LIBRARY="$O/openssl-src/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 || { echo " libarchive-xar configure FAILED (see $O/la-xar-cfg.log)"; return 1; }
cmake --build "$O/libarchive-xar" --target archive_static -j >"$O/la-xar-build.log" 2>&1 || { echo " libarchive-xar build FAILED (see $O/la-xar-build.log)"; return 1; }
}
# the compression stack link line (libarchive FIRST for static link order), per target outdir.
archlibs() { echo "$1/libarchive/libarchive/libarchive.a $1/xz/liblzma.a $1/lib/libzstd.a $1/lib/liblz4.a $1/lib/libbz2.a $1/lib/libz.a"; }
# the full XAR stack link line (musl): xar-enabled libarchive + libxml2 + codecs + openssl (its MD5/SHA1).
archlibsXar() { echo "$1/libarchive-xar/libarchive/libarchive.a $1/libxml2/libxml2.a $1/xz/liblzma.a $1/lib/libzstd.a $1/lib/liblz4.a $1/lib/libbz2.a $1/lib/libz.a $1/openssl-src/libssl.a $1/openssl-src/libcrypto.a"; }
ARCHINC="-Isrc -Isrc/lua -Ilibs -Ivendor/lua/src -Ivendor/libarchive/libarchive"
echo "== musl (fully static Linux, built AND run -- includes XAR) =="
# musl gets full XAR: codecs + xz, then OpenSSL + libxml2, then a libarchive built with them + iconv
# (in musl's libc). The static testArchive links the whole XAR stack and runs its xar round-trip here.
if build_codecs "$XABS/bin/zcc-musl" "$XABS/musl" && build_xz "$XABS/toolchain-musl.cmake" "$XABS/musl" \
&& build_openssl_musl "$XABS/musl" && build_libxml2_musl "$XABS/musl" && build_libarchive_xar_musl "$XABS/musl"; then
if "$XABS/bin/zcc-musl" -static -O2 -pthread -w $ARCHINC -DLUA_USE_POSIX \
$CORE src/lua/luaEngine.c src/lua/luaAdapter.c libs/calogArchive.c libs/calogHandle.c tests/testArchive.c \
$LUASRC $(archlibsXar "$XABS/musl") -lm -o "$XABS/musl/testArchive-musl" 2>"$XABS/musl/testArchive-link.err"; then
if "$XABS/musl/testArchive-musl" >/dev/null 2>&1; then
echo " [musl RUN ok] testArchive (libarchive + 5 codecs + XAR via libxml2/openssl, fully static)"; pass=$((pass+1))
else echo " [musl RAN, nonzero] testArchive"; fail=$((fail+1)); fi
else echo " [musl LINK FAIL] testArchive (see $XABS/musl/testArchive-link.err)"; fail=$((fail+1)); fi
else fail=$((fail+1)); fi
echo "== Windows x64 (.exe built against vendored winpthreads; run needs wine) =="
# testArchive uses calog's pthread actor model -> needs winpthreads (vendored), like crossBuild.sh.
rm -rf "$XABS/wpobj"; mkdir -p "$XABS/wpobj" "$XABS/win"
wpok=1
for c in vendor/winpthreads/src/*.c; do
"$XABS/bin/zcc-win" -c -O2 -w -Ivendor/winpthreads/include -Ivendor/winpthreads/src \
-DWINPTHREAD_STATIC=1 -DIN_WINPTHREAD=1 "$c" -o "$XABS/wpobj/$(basename "$c" .c).o" || { wpok=0; break; }
done
"$XABS/bin/zar" rcs "$XABS/win/libwinpthreads.a" "$XABS"/wpobj/*.o 2>/dev/null
# libarchive on Windows uses system libs the POSIX build does not: CNG crypto (bcrypt) for its AES/
# PBKDF2, and XmlLite + OLE (xmllite/ole32) for XAR, which auto-replaces the libxml2/expat we disable.
WINSYS="-lbcrypt -lxmllite -lole32 -ladvapi32 -lcrypt32 -lshlwapi"
if [ "$wpok" = 1 ] && build_codecs "$XABS/bin/zcc-win" "$XABS/win" && build_cmake_libs "$XABS/toolchain-win.cmake" "$XABS/win"; then
if "$XABS/bin/zcc-win" -O2 -w $ARCHINC -Ivendor/winpthreads/include -DWINPTHREAD_STATIC=1 \
$CORE src/lua/luaEngine.c src/lua/luaAdapter.c libs/calogArchive.c libs/calogHandle.c tests/testArchive.c \
$LUASRC $(archlibs "$XABS/win") "$XABS/win/libwinpthreads.a" $WINSYS -o "$XABS/win/testArchive.exe" 2>"$XABS/win/testArchive-link.err"; then
if file "$XABS/win/testArchive.exe" | grep -q 'PE32+ executable'; then
echo " [win BUILT] testArchive.exe (libarchive + 5 codecs, valid PE)"; pass=$((pass+1))
else echo " [win ??] testArchive.exe (not a PE?)"; fail=$((fail+1)); fi
else echo " [win LINK FAIL] testArchive.exe (see $XABS/win/testArchive-link.err)"; fail=$((fail+1)); fi
else fail=$((fail+1)); fi
echo
echo "== crossArchive: $pass ok, $fail failed (artifacts in $X/) =="
[ "$fail" -eq 0 ]