Skip to content

Commit 45b61a4

Browse files
romgrkclaude
andcommitted
ci: drive Windows DLL bundle from a curated seed list file
Move the bundled-library list out of the bash script into windows/runtime-libraries.txt, which doubles as user-facing reference and the build script's input. Emit the exact resolved closure to <binding-dir>/bundled-dlls.txt at build time. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent decf94f commit 45b61a4

2 files changed

Lines changed: 93 additions & 24 deletions

File tree

scripts/windows-bundle-runtime.sh

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,58 @@
55
# Make a freshly-built Windows prebuilt self-contained so it can be used WITHOUT
66
# MSYS2/MinGW or any compiler on the target machine. We copy the addon's entire
77
# transitive MinGW DLL closure next to the .node, plus the GObject-Introspection
8-
# typelibs it needs at runtime.
8+
# typelibs and runtime data it needs at runtime.
9+
#
10+
# The set of bundled libraries is driven by the curated seed list
11+
# windows/runtime-libraries.txt (also a user-facing reference). The exact
12+
# resolved DLL set is written to <binding-dir>/bundled-dlls.txt.
913
#
1014
# Run inside the MINGW64 shell, after building.
1115
#
1216
# ./scripts/windows-bundle-runtime.sh lib/binding/node-v127-win32-x64
1317
#
1418
set -euo pipefail
1519

16-
BINDING_DIR="${1:?usage: windows-bundle-runtime.sh <binding-dir>}"
20+
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
21+
22+
BINDING_DIR="${1:?usage: windows-bundle-runtime.sh <binding-dir> [runtime-libraries.txt]}"
23+
# The curated seed list lives in a text file so it doubles as user-facing
24+
# reference (windows/runtime-libraries.txt). Override with $2 if needed.
25+
LIBS_FILE="${2:-$SCRIPT_DIR/../windows/runtime-libraries.txt}"
26+
# MinGW bin dir the seed names resolve against.
27+
MB="${MINGW_BIN:-/mingw64/bin}"
28+
1729
NODE_FILE="$BINDING_DIR/node_gtk.node"
1830

1931
if [ ! -f "$NODE_FILE" ]; then
2032
echo "error: $NODE_FILE not found"
2133
ls -la "$BINDING_DIR" || true
2234
exit 1
2335
fi
36+
if [ ! -f "$LIBS_FILE" ]; then
37+
echo "error: seed list $LIBS_FILE not found"
38+
exit 1
39+
fi
2440

2541
# GObject-Introspection loads each namespace's shared library at runtime via
2642
# g_module_open() when you call gi.require('Gtk', ...). Those libraries
27-
# (libgio, libgtk, libgdk, libpango, libatk, libgdk_pixbuf, ...) are NOT linked
28-
# by node_gtk.node, so ntldd on the addon alone misses them. We therefore seed
29-
# the closure from the addon AND from the GTK runtime libraries themselves.
30-
MB=/mingw64/bin
31-
# Seed from the addon AND from every runtime library a real GTK4/Adwaita app
32-
# pulls in (the quilx target: Gtk4 + Adw + GtkSourceView5 + Pango + GdkPixbuf +
33-
# Graphene). Missing entries are skipped, so a few GTK3 names are kept too for
34-
# robustness.
35-
ENTRY_LIBS=(
36-
"$NODE_FILE"
37-
"$MB/libgirepository-1.0-1.dll"
38-
"$MB/libgio-2.0-0.dll"
39-
"$MB/libgtk-4-1.dll"
40-
"$MB/libadwaita-1-0.dll"
41-
"$MB/libgtksourceview-5-0.dll"
42-
"$MB/libpango-1.0-0.dll"
43-
"$MB/libpangocairo-1.0-0.dll"
44-
"$MB/libgdk_pixbuf-2.0-0.dll"
45-
"$MB/libgraphene-1.0-0.dll"
46-
"$MB/libcairo-gobject-2.dll"
47-
)
43+
# (libgio, libgtk, libgdk, libpango, libgdk_pixbuf, ...) are NOT linked by
44+
# node_gtk.node, so ntldd on the addon alone misses them. We therefore seed the
45+
# closure from the addon AND from the GTK runtime libraries listed in LIBS_FILE.
46+
echo "## Seed libraries from $LIBS_FILE"
47+
ENTRY_LIBS=("$NODE_FILE")
48+
while IFS= read -r line; do
49+
# strip comments and surrounding whitespace; skip blanks
50+
name="${line%%#*}"
51+
name="$(echo "$name" | tr -d '[:space:]')"
52+
[ -z "$name" ] && continue
53+
if [ -f "$MB/$name" ]; then
54+
ENTRY_LIBS+=("$MB/$name")
55+
echo " - $name"
56+
else
57+
echo " (skip missing seed $name)"
58+
fi
59+
done < "$LIBS_FILE"
4860

4961
echo "## Computing recursive DLL closure for the addon + GTK runtime"
5062
# ntldd -R prints every transitive dependency with its resolved Windows path:
@@ -73,7 +85,21 @@ sort -u /tmp/dll-closure.txt | while IFS= read -r u; do
7385
copied=$((copied + 1))
7486
fi
7587
done
76-
echo "## DLLs bundled into $BINDING_DIR ($(ls "$BINDING_DIR"/*.dll | wc -l) total)"
88+
DLL_COUNT=$(ls "$BINDING_DIR"/*.dll 2>/dev/null | wc -l | tr -d ' ')
89+
echo "## DLLs bundled into $BINDING_DIR ($DLL_COUNT total)"
90+
91+
# Write the exact set that shipped, as a reference manifest (ships in the
92+
# bundle and the artifact). Generated from the curated seed list above.
93+
MANIFEST="$BINDING_DIR/bundled-dlls.txt"
94+
{
95+
echo "# bundled-dlls.txt — Windows DLLs shipped with this node-gtk prebuilt."
96+
echo "# GENERATED by scripts/windows-bundle-runtime.sh; do not edit by hand."
97+
echo "# Edit the curated seed list windows/runtime-libraries.txt instead."
98+
echo "# $DLL_COUNT DLLs, expanded from the seed list via 'ntldd -R'."
99+
echo "#"
100+
( cd "$BINDING_DIR" && ls -1 *.dll 2>/dev/null | sort )
101+
} > "$MANIFEST"
102+
echo "## Wrote manifest $MANIFEST"
77103

78104
echo "## Bundling GObject-Introspection typelibs"
79105
TYPELIB_SRC=$(pkg-config --variable=typelibdir gobject-introspection-1.0 2>/dev/null || true)

windows/runtime-libraries.txt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# windows/runtime-libraries.txt
2+
#
3+
# Seed libraries bundled with the node-gtk Windows prebuilt.
4+
#
5+
# These are the high-level GObject-Introspection runtime libraries that a
6+
# GTK4 / Adwaita application loads at run time (via g_module_open when you call
7+
# gi.require('Gtk', '4.0') etc.). They are NOT linked by node_gtk.node itself,
8+
# so scanning the addon's own imports does not find them — they must be listed
9+
# here explicitly.
10+
#
11+
# scripts/windows-bundle-runtime.sh reads this file, then uses `ntldd -R` to
12+
# expand each entry into its full transitive DLL closure (~78 DLLs: harfbuzz,
13+
# cairo, pixman, fribidi, freetype, zlib, ...) and copies the whole set next to
14+
# the compiled addon. The exact resolved list that actually ships is written to
15+
# <binding-dir>/bundled-dlls.txt at build time — read that file to see every DLL.
16+
#
17+
# Format:
18+
# - one DLL file name per line (no path; resolved against the MinGW bin dir,
19+
# e.g. /mingw64/bin)
20+
# - lines starting with '#' and blank lines are ignored
21+
# - entries that don't exist in the MinGW prefix are skipped with a warning,
22+
# so it is safe to list optional libraries
23+
#
24+
# To support additional namespaces, add the providing DLL here. For example:
25+
# libgstreamer-1.0-0.dll # GStreamer (gi.require('Gst', '1.0'))
26+
# libsoup-3.0-0.dll # libsoup (gi.require('Soup', '3.0'))
27+
# Note: VTE (terminal) has no Windows port and cannot be bundled.
28+
29+
# --- GObject-Introspection core ---
30+
libgirepository-1.0-1.dll
31+
libgio-2.0-0.dll
32+
33+
# --- GTK4 stack (quilx target) ---
34+
libgtk-4-1.dll
35+
libadwaita-1-0.dll
36+
libgtksourceview-5-0.dll
37+
38+
# --- text / graphics ---
39+
libpango-1.0-0.dll
40+
libpangocairo-1.0-0.dll
41+
libgdk_pixbuf-2.0-0.dll
42+
libgraphene-1.0-0.dll
43+
libcairo-gobject-2.dll

0 commit comments

Comments
 (0)