From f6afa23e28f49d39ac1e8f86d6b995aa24780b8b Mon Sep 17 00:00:00 2001 From: Benjamin de Waal Date: Wed, 6 May 2026 07:06:41 +0100 Subject: [PATCH] libnh: fix native build of libnh.a on macOS Fixes four issues that prevented `make WANT_LIBNH=1 all` from producing a libnh.a that could be linked into a host program on macOS. Before these patches, it built but the resulting archive was unusable: macOS ld errored on a nested liblua archive member, was missing date.o and hacklib symbols (`populate_nomakedefs`, `eos`, `lcase`, `mungspaces`, ...), and had duplicate definitions of `main`, `whoami`, etc. Specific changes: 1. sys/libnh/libnhmain.c: drop `static` on `whoami()`. src/earlyarg.c declares it `extern` and calls it from `scores_only()`; the static makes it file-local and that reference goes unresolved. 2. sys/libnh/libnhmain.c: gate the emscripten-only code in get_nhuuid with `#ifdef __EMSCRIPTEN__` instead of `#ifdef NHUUID`. The macOS hints define NHUUID for the libnh build (they did so unconditionally before NO_NHUUID even existed), so on native builds the compiler tried to call `emscripten_run_script_int` / `_string` and failed with implicit-function-declaration errors. __EMSCRIPTEN__ is the real signal for "this is being cross-compiled to WASM." 3. sys/unix/hints/macOS.500: in the WANT_LIBNH block, add an explicit `recover: lua_support` dependency (gated by MAKEFILE_TOP). When $(GAME) is overridden to empty, the regular `recover: $(GAME)` chain no longer triggers `lua_support`, so include/nhlua.h never gets generated and recover.c's transitive #include of hack.h fails. 4. sys/unix/hints/macOS.500: rewrite the libnh.a rule. The previous `ar rcs libnh.a $(HOBJ) $(LIBNHSYSOBJ) liblua-$(LUA_VERSION).a` had four problems: (a) ar archives liblua.a as a single opaque member that macOS ld can't dereference, (b) date.o (DATE_O, kept separate from HOBJ) was never archived, so `populate_nomakedefs` and `nomakedefs` were missing, (c) hacklib.a was likewise omitted, and (d) HOBJ already contains $(SYSOBJ) (with unixmain.o) and $(WINOBJ) (the tty windowport), which duplicated symbols from libnhmain.o / winshim.o. The fix uses `libtool -static` so hacklib.a and liblua's archive have their members merged rather than nested, depends on $(LUALIB) so lua_support runs first, includes $(DATE_O) and $(TARGET_HACKLIB), and uses $(filter-out $(SYSOBJ) $(WINOBJ),$(HOBJ)) to drop the duplicates. Verified by clean rebuild on macOS 26 (arm64, Apple clang 17): make spotless make fetch-Lua make WANT_LIBNH=1 all and link-tested with a tiny harness that calls shim_graphics_set_callback() against the resulting libnh.a. Co-Authored-By: Claude Opus 4.7 (1M context) --- sys/libnh/libnhmain.c | 10 +++++----- sys/unix/hints/macOS.500 | 22 ++++++++++++++++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/sys/libnh/libnhmain.c b/sys/libnh/libnhmain.c index 90907f987..67b38e4e0 100644 --- a/sys/libnh/libnhmain.c +++ b/sys/libnh/libnhmain.c @@ -37,7 +37,7 @@ extern struct passwd *getpwnam(const char *); #ifdef CHDIR static void chdirx(const char *, boolean); #endif /* CHDIR */ -static boolean whoami(void); +boolean whoami(void); static void process_options(int, char **); #ifdef _M_UNIX @@ -516,7 +516,7 @@ chdirx(const char *dir, boolean wr) #endif /* CHDIR */ /* returns True iff we set plname[] to username which contains a hyphen */ -static boolean +boolean whoami(void) { /* @@ -805,7 +805,7 @@ get_nhuuid(void) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; char *uuid = (char *) &stmp[0]; #ifndef NONHUUID -#ifdef NHUUID +#ifdef __EMSCRIPTEN__ int uuid_available = 0; #endif #endif @@ -814,7 +814,7 @@ get_nhuuid(void) return; #ifndef NONHUUID -#ifdef NHUUID +#ifdef __EMSCRIPTEN__ uuid_available = emscripten_run_script_int( "typeof crypto !== 'undefined'" " && typeof crypto.randomUUID === 'function'"); @@ -824,7 +824,7 @@ get_nhuuid(void) uuid = (char *) &stmp[0]; } } -#endif /* NHUUID */ +#endif /* __EMSCRIPTEN__ */ #endif /* NONHUUID */ Snprintf(svn.nhuuid, sizeof svn.nhuuid, "%s", uuid); } diff --git a/sys/unix/hints/macOS.500 b/sys/unix/hints/macOS.500 index 2a400d1a3..6de98b96d 100755 --- a/sys/unix/hints/macOS.500 +++ b/sys/unix/hints/macOS.500 @@ -345,6 +345,12 @@ LIBNHSYSOBJ = $(TARGETPFX)libnhmain.o $(TARGETPFX)ioctl.o \ #without winshim override GAME= MOREALL += ( cd src ; $(MAKE) pregame ; $(MAKE) $(TARGETPFX)libnh.a ) +# With $(GAME) empty, the regular `recover: $(GAME)` chain no longer +# triggers lua_support, but recover.c still pulls in hack.h -> nhlua.h +# transitively. Make recover depend on lua_support explicitly. +ifdef MAKEFILE_TOP +recover: lua_support +endif endif # WANT_LIBNH # Lua @@ -568,8 +574,20 @@ $(TARGETPFX)macuuid.o: ../sys/unix/macuuid.m endif ifdef WANT_LIBNH -$(TARGETPFX)libnh.a: $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua-$(LUA_VERSION).a - $(AR) rcs $@ $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua-$(LUA_VERSION).a +# Build libnh.a by merging the engine objects, the shim windowport, +# date.o (separate from $(HOBJ) per Makefile.src), hacklib.a, and Lua's +# static archive. Use libtool -static rather than ar so hacklib.a and +# liblua-$(LUA_VERSION).a have their members merged in -- ar would archive +# them as opaque .a members and macOS ld can't dereference those. +# Depending on $(LUALIB) triggers the lua_support build (which generates +# include/nhlua.h); without it, $(HOBJ) won't compile. +# filter-out drops $(SYSOBJ) (which carries unixmain.o) and $(WINOBJ) (the +# tty windowport) from $(HOBJ); $(LIBNHSYSOBJ) supplies libnhmain.o and +# winshim.o in their place. +$(TARGETPFX)libnh.a: $(LUALIB) $(HOBJ) $(LIBNHSYSOBJ) $(DATE_O) $(TARGET_HACKLIB) + libtool -static -o $@ \ + $(filter-out $(SYSOBJ) $(WINOBJ),$(HOBJ)) \ + $(LIBNHSYSOBJ) $(DATE_O) $(TARGET_HACKLIB) $(LUALIB) @echo "$@ built." $(TARGETPFX)libnhmain.o : ../sys/libnh/libnhmain.c $(HACK_H) $(CC) $(CFLAGS) -c -o$@ $<