136 lines
6.3 KiB
Bash
Executable File
136 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# build-win32.sh — Cross-compile native-hello for Windows using mingw64.
|
|
#
|
|
# Requirements:
|
|
# - x86_64-w64-mingw32-gcc (brew install mingw-w64)
|
|
# - elc in PATH or at ../../../lang/dist/platform/elc
|
|
#
|
|
# Output: build-win32/native-hello.exe
|
|
#
|
|
# Note: We use el_runtime_win32.c instead of el_runtime.c / el_seed.c because
|
|
# those files have deep POSIX dependencies (pthread, curl, dlfcn, sys/socket,
|
|
# etc.) that cannot be cross-compiled to Windows. el_runtime_win32.c provides
|
|
# all the runtime symbols that native-hello and the el-native vessel actually
|
|
# call, using only standard C11 + Win32 APIs.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
EL_LANG_ROOT="${SCRIPT_DIR}/../../../lang"
|
|
EL_UI_ROOT="${SCRIPT_DIR}/../.."
|
|
EL_RUNTIME="${EL_LANG_ROOT}/el-compiler/runtime"
|
|
EL_NATIVE_VESSEL="${EL_UI_ROOT}/vessels/el-native/src/main.el"
|
|
BUILD_DIR="${SCRIPT_DIR}/build-win32"
|
|
|
|
# ── Locate elc ───────────────────────────────────────────────────────────────
|
|
if command -v elc &>/dev/null; then
|
|
ELC="elc"
|
|
elif [ -x "${EL_LANG_ROOT}/dist/platform/elc" ]; then
|
|
ELC="${EL_LANG_ROOT}/dist/platform/elc"
|
|
else
|
|
echo "Error: elc not found. Add it to PATH or place it at:"
|
|
echo " ${EL_LANG_ROOT}/dist/platform/elc"
|
|
exit 1
|
|
fi
|
|
|
|
CC="x86_64-w64-mingw32-gcc"
|
|
|
|
# -D_WIN32_IE for SetWindowSubclass (comctl32 >= IE 0x0600)
|
|
CFLAGS=(
|
|
-std=c11
|
|
-DEL_TARGET_WIN32
|
|
-D_WIN32_IE=0x0600
|
|
-I"${EL_RUNTIME}"
|
|
-Wall
|
|
-Wno-unused-function
|
|
)
|
|
|
|
mkdir -p "${BUILD_DIR}"
|
|
|
|
# ── 1. Compile el-native vessel to C ─────────────────────────────────────────
|
|
echo "==> Compiling el-native vessel to C..."
|
|
"${ELC}" "${EL_NATIVE_VESSEL}" \
|
|
> "${BUILD_DIR}/el_native_vessel.c"
|
|
|
|
# ── 2. Generate vessel header (declarations only) ────────────────────────────
|
|
# elc --emit-header includes function bodies AND bare global variable definitions.
|
|
# We extract only forward declarations with two awk passes:
|
|
# Pass 1: stop before function bodies (same as macOS build.sh).
|
|
# Pass 2: convert bare global variable definitions (^el_val_t NAME;) to
|
|
# `extern el_val_t NAME;` so the app TU sees them as declarations,
|
|
# not definitions — avoiding duplicate symbol errors at link time.
|
|
echo "==> Generating el-native vessel header (declarations only)..."
|
|
"${ELC}" --emit-header "${EL_NATIVE_VESSEL}" \
|
|
| awk 'BEGIN{in_body=0} /^[a-zA-Z_].+\)[[:space:]]*\{/ {in_body=1} !in_body{print}' \
|
|
| awk '/^el_val_t [A-Za-z_][A-Za-z0-9_]*;/{print "extern " $0; next} {print}' \
|
|
> "${BUILD_DIR}/el_native_vessel.h"
|
|
|
|
# ── 3. Compile app entry to C ─────────────────────────────────────────────────
|
|
echo "==> Compiling app entry to C..."
|
|
EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \
|
|
"${ELC}" "${SCRIPT_DIR}/src/main.el" \
|
|
> "${BUILD_DIR}/native_hello.c"
|
|
|
|
# ── 4. Compile el_win32.c ─────────────────────────────────────────────────────
|
|
echo "==> Compiling el_win32.c (Win32 widget bridge)..."
|
|
${CC} "${CFLAGS[@]}" \
|
|
-c "${EL_RUNTIME}/el_win32.c" \
|
|
-o "${BUILD_DIR}/el_win32.o"
|
|
|
|
# ── 5. Compile el_runtime_win32.c ─────────────────────────────────────────────
|
|
# This replaces el_seed.c + el_runtime.c for Win32 targets.
|
|
echo "==> Compiling el_runtime_win32.c (Win32 runtime stub)..."
|
|
${CC} "${CFLAGS[@]}" \
|
|
-c "${EL_RUNTIME}/el_runtime_win32.c" \
|
|
-o "${BUILD_DIR}/el_runtime_win32.o"
|
|
|
|
# ── 6. Compile el-native vessel object ───────────────────────────────────────
|
|
echo "==> Compiling el-native vessel object..."
|
|
${CC} "${CFLAGS[@]}" \
|
|
-include "${EL_RUNTIME}/el_native_target.h" \
|
|
-c "${BUILD_DIR}/el_native_vessel.c" \
|
|
-o "${BUILD_DIR}/el_native_vessel.o"
|
|
|
|
echo "==> Patching el_native_vessel.o (localising vessel main + shared globals)..."
|
|
# The vessel's compiled C has its own main(). We localize it so the linker sees
|
|
# only the app's main(). We also localize any globals (TOKEN_*, etc.) that the
|
|
# vessel defines but the app also declares — matching the nmedit step in
|
|
# build.sh (macOS), using objcopy for mingw.
|
|
VESSEL_LOCAL_SYMS=()
|
|
# Collect all defined (T and D/B/C) symbols in the vessel object
|
|
while IFS= read -r sym; do
|
|
VESSEL_LOCAL_SYMS+=("--localize-symbol=${sym}")
|
|
done < <(x86_64-w64-mingw32-nm "${BUILD_DIR}/el_native_vessel.o" 2>/dev/null \
|
|
| awk '/^[0-9a-f]+ [TDBCt] /{print $3}' \
|
|
| grep -E '^(main|TOKEN_)' || true)
|
|
if [ ${#VESSEL_LOCAL_SYMS[@]} -gt 0 ]; then
|
|
x86_64-w64-mingw32-objcopy "${VESSEL_LOCAL_SYMS[@]}" \
|
|
"${BUILD_DIR}/el_native_vessel.o"
|
|
fi
|
|
|
|
# ── 7. Compile app object ─────────────────────────────────────────────────────
|
|
echo "==> Compiling app object..."
|
|
${CC} "${CFLAGS[@]}" \
|
|
-include "${EL_RUNTIME}/el_native_target.h" \
|
|
-include "${BUILD_DIR}/el_native_vessel.h" \
|
|
-c "${BUILD_DIR}/native_hello.c" \
|
|
-o "${BUILD_DIR}/native_hello.o"
|
|
|
|
# ── 8. Link ───────────────────────────────────────────────────────────────────
|
|
# -Wl,--export-all-symbols: make el callback functions (on_greet_click etc.)
|
|
# visible to GetProcAddress(GetModuleHandle(NULL), fn_name) — the Win32
|
|
# equivalent of POSIX dlsym(RTLD_DEFAULT, fn_name).
|
|
echo "==> Linking native-hello.exe..."
|
|
${CC} "${CFLAGS[@]}" \
|
|
-Wl,--export-all-symbols \
|
|
"${BUILD_DIR}/native_hello.o" \
|
|
"${BUILD_DIR}/el_native_vessel.o" \
|
|
"${BUILD_DIR}/el_win32.o" \
|
|
"${BUILD_DIR}/el_runtime_win32.o" \
|
|
-lcomctl32 -luser32 -lgdi32 -lkernel32 \
|
|
-o "${BUILD_DIR}/native-hello.exe"
|
|
|
|
echo ""
|
|
echo "==> Built: ${BUILD_DIR}/native-hello.exe"
|
|
ls -lh "${BUILD_DIR}/native-hello.exe"
|