From 9e53b23b11ce5aba25ce0d27614103f85783874f Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Sun, 17 May 2026 20:27:32 +0000 Subject: [PATCH] windows/build-msi.sh: cross-build the MSI on Linux via wixl + mingw-w64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Discovered building v1.1.0 that the MSI can be produced entirely on Linux — no Windows VM, no manual WiX install, no GUI babysitting: apt install wixl unzip gcc-mingw-w64-x86-64 binutils-mingw-w64-x86-64 \ mingw-w64-x86-64-dev curl The new build-msi.sh script: 1. Runs sync.sh to refresh pkg/{lmcp,server,json}.lua from root. 2. Downloads Lua 5.4.2 Win64 binaries from LuaBinaries (Tools + Library zips — interpreter + headers + import lib). 3. Cross-compiles LuaSocket 3.1.0 via x86_64-w64-mingw32-gcc (produces socket-3.0.0.dll + mime-1.0.3.dll for Win64). 4. Stages pkg/lua/{lua.exe, lua54.dll, socket/, mime/, *.lua} per the WiX manifest layout. 5. Invokes wixl on the lmcp.wxs manifest (with sed for the Windows backslash path separators → forward slashes). Output: lmcp-.msi. Version is read from lmcp.wxs Version="…", so bump that before each release. Cold build: ~30s. Warm cache: ~5s. The artifact contains all 17 files the WiX manifest expects, ProductVersion matches lmcp.wxs. README updated to point at build-msi.sh as the recommended path; the Windows-side candle/light recipe kept as an alternative. Reproducibility note (deferred): the MSI is not yet bit-reproducible across builds — file mtimes in the Lua binaries' zip propagate to the cab inside the MSI. The debian/lmcp/build-deb.sh in marfrit- packages uses SOURCE_DATE_EPOCH to fix this; same pattern would apply here. Out of scope for the first cut. Co-Authored-By: Claude Opus 4.7 (1M context) --- windows/README.md | 28 +++++++++--- windows/build-msi.sh | 100 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 6 deletions(-) create mode 100755 windows/build-msi.sh diff --git a/windows/README.md b/windows/README.md index f33092f..4d34d8c 100644 --- a/windows/README.md +++ b/windows/README.md @@ -3,14 +3,30 @@ This directory contains the WiX manifest and packaging files for the Windows MSI build of lmcp. -## Workflow +## Recommended: cross-build on Linux (one command) ```sh -# 1. Pull the Lua + LuaSocket runtime into pkg/lua/ (one-time, see below). -# 2. Sync the lmcp .lua sources from the root of the repo: -./sync.sh -# 3. Bump windows/lmcp.wxs `Version="…"` to match the release tag. -# 4. Invoke WiX: +./build-msi.sh /path/to/output/dir +``` + +Downloads Lua 5.4 Win64 binaries from LuaBinaries, cross-compiles +LuaSocket via `mingw-w64`, stages `pkg/lua/`, and runs `wixl` to +produce `lmcp-.msi`. No Windows VM required. + +Prereqs on a Debian/Ubuntu builder: +```sh +sudo apt install wixl unzip gcc-mingw-w64-x86-64 \ + binutils-mingw-w64-x86-64 mingw-w64-x86-64-dev curl +``` + +Version comes from `lmcp.wxs` `Version="…"`. Bump that before +building a release. + +## Alternative: build on Windows via WiX toolset + +```cmd +sync.sh REM see "tracked vs. generated" +REM ensure pkg/lua/ has the runtime — see below candle.exe lmcp.wxs light.exe lmcp.wixobj -o lmcp-1.x.y.msi ``` diff --git a/windows/build-msi.sh b/windows/build-msi.sh new file mode 100755 index 0000000..aeda4f9 --- /dev/null +++ b/windows/build-msi.sh @@ -0,0 +1,100 @@ +#!/bin/sh +# windows/build-msi.sh — produce lmcp-.msi on Linux via wixl. +# +# This is the first-time-discovered cross-build path: download Lua 5.4 +# Win64 binaries from LuaBinaries, cross-compile LuaSocket with mingw-w64, +# stage windows/pkg/lua/, then invoke wixl on the WiX manifest. +# +# Avoids the VM106-clone + WiX-on-Windows path entirely. ~1 minute on a +# warm cache; ~3-5 minutes cold (downloads ~700 KB + cross-compiles). +# +# Prereqs (apt install on Debian aarch64): +# apt-get install -y wixl unzip gcc-mingw-w64-x86-64 binutils-mingw-w64-x86-64 \ +# mingw-w64-x86-64-dev +# +# Usage: ./build-msi.sh [output_dir] +# Output: $output_dir/lmcp-.msi (default: $PWD) +# +# Version comes from windows/lmcp.wxs Version="…" attribute. +set -eu + +here=$(dirname "$(readlink -f "$0")") +root=$(cd "$here/.." && pwd) +out_dir=${1:-$PWD} +work=$(mktemp -d /tmp/lmcp-msi-XXXXXX) +trap "rm -rf $work" EXIT + +# Versions — bump as upstream releases. +LUA_VER=5.4.2 +LUASOCKET_VER=3.1.0 + +# Pull current lmcp version from the WiX manifest. +lmcp_ver=$(sed -n 's/.*Version="\([^"]*\)".*/\1/p' "$here/lmcp.wxs" | head -1) +[ -n "$lmcp_ver" ] || { echo "build-msi.sh: cannot parse Version from lmcp.wxs" >&2; exit 1; } + +echo "build-msi.sh: lmcp $lmcp_ver, lua $LUA_VER, luasocket $LUASOCKET_VER" + +echo "==> 1/5 sync lmcp .lua sources into pkg/" +"$here/sync.sh" + +echo "==> 2/5 fetch lua $LUA_VER win64 binaries + dev library" +cd "$work" +curl -sSLf -o lua-bin.zip \ + "https://downloads.sourceforge.net/project/luabinaries/${LUA_VER}/Tools%20Executables/lua-${LUA_VER}_Win64_bin.zip" +curl -sSLf -o lua-lib.zip \ + "https://downloads.sourceforge.net/project/luabinaries/${LUA_VER}/Windows%20Libraries/Dynamic/lua-${LUA_VER}_Win64_dllw6_lib.zip" +mkdir -p luabin lualib include/lua/54 include/lua54 bin/lua/54 bin/lua54 lib/lua/54 lib/lua54 +unzip -q -o lua-bin.zip -d luabin +unzip -q -o lua-lib.zip -d lualib +cp lualib/include/*.h include/lua/54/ +cp lualib/include/*.h include/lua54/ +cp lualib/liblua54.a lib/lua/54/ +cp lualib/liblua54.a lib/lua54/ +cp lualib/lua54.dll bin/lua/54/ +cp lualib/lua54.dll bin/lua54/ + +echo "==> 3/5 cross-compile LuaSocket $LUASOCKET_VER for win64" +curl -sSLf -o luasocket.tar.gz \ + "https://github.com/lunarmodules/luasocket/archive/refs/tags/v${LUASOCKET_VER}.tar.gz" +tar xzf luasocket.tar.gz +cd "luasocket-${LUASOCKET_VER}" +make -s PLAT=mingw \ + CC=x86_64-w64-mingw32-gcc \ + LD=x86_64-w64-mingw32-gcc \ + LUAV=54 \ + LUAINC_mingw_base="$work/include" \ + LUALIB_mingw_base="$work/bin" \ + > /dev/null + +echo "==> 4/5 stage pkg/lua/" +pkg_lua="$here/pkg/lua" +rm -rf "$pkg_lua" +mkdir -p "$pkg_lua/socket" "$pkg_lua/mime" + +# WiX manifest expects "lua.exe" (not "lua54.exe"). +cp "$work/luabin/lua54.exe" "$pkg_lua/lua.exe" +cp "$work/luabin/lua54.dll" "$pkg_lua/lua54.dll" +cp src/socket.lua "$pkg_lua/" +cp src/mime.lua "$pkg_lua/" +cp src/ltn12.lua "$pkg_lua/" +cp src/socket-3.0.0.dll "$pkg_lua/socket/core.dll" +cp src/ftp.lua "$pkg_lua/socket/" +cp src/headers.lua "$pkg_lua/socket/" +cp src/http.lua "$pkg_lua/socket/" +cp src/smtp.lua "$pkg_lua/socket/" +cp src/tp.lua "$pkg_lua/socket/" +cp src/url.lua "$pkg_lua/socket/" +cp src/mime-1.0.3.dll "$pkg_lua/mime/core.dll" + +echo "==> 5/5 wixl: produce MSI" +# wixl wants forward slashes; rewrite Windows-style backslashes in Source=. +wxs_tmp="$work/lmcp.wxs" +sed 's|Source="pkg\\|Source="pkg/|g; s|\\\([a-zA-Z]\)|/\1|g' "$here/lmcp.wxs" > "$wxs_tmp" +mkdir -p "$out_dir" +out_msi="$out_dir/lmcp-${lmcp_ver}.msi" +(cd "$here" && wixl -v "$wxs_tmp" -o "$out_msi") + +echo "" +echo "==> done: $out_msi" +ls -la "$out_msi" +sha256sum "$out_msi"