On CI the inherited Path runs past cmd's 8191 characters, so vcvars
loaded nothing and said nothing.
Load it on the system directories and put the caller's Path back
after. What it keeps as __VSCMD_PREINIT_PATH is then the same each
time, so the build no longer drops it.
SwiftPM rebuilds everything when the environment changes. Each run
grew LIB in the caller's shell, and each terminal brings its own
variables, so no build was ever a no-op.
Build from an allowlisted environment and hand the caller's back
afterwards.
Sync-Checkout deleted subprojects/<name> for every wrap whenever the
checkout moved, which also took frida-gum, a wrap rather than a
submodule. capstone.wrap redirects into that tree, and meson resolves
every redirect while parsing, before it downloads anything, so the
next `subprojects update` failed outright:
ERROR: wrap-redirect subprojects\frida-gum\subprojects\capstone.wrap
filename does not exist
Bumping FRIDA_CORE_REF could therefore never work on Windows. The
deletion was redundant anyway -- `update --reset` already brings each
subproject to the revision its wrap names -- so only the build
directory goes now.
Bootstrap provisions QEMU into a prefix of its own, and nothing told
the app about it -- the search goes by PATH, which named every other
prefix but that one.
Only git's own submodules were being updated, and a wrap subproject
is invisible to git -- so radare2 kept an sdb from whatever ref was
built before, and the build failed on symbols the pinned one has.
The check ran the emulator without vcpkg on the path, so it could not
load GLib and answered nothing -- and the build failed saying the
dbus display was missing from a QEMU that has it.
MSYS2's profile exports a PKG_CONFIG_PATH of its own, which is read
before PKG_CONFIG_LIBDIR, so zlib, bzip2 and zstd came from the
compiler's own prefix while glib and pixman came from vcpkg. A second
zlib then travelled beside the emulators under the same name as the
app's, and won by sitting closer.
Let go of that path, and point the linker at vcpkg for bzip2, which
asks the compiler rather than pkg-config. Bring zstd along so nothing
is given up for it, say outright that both are wanted so a build
stops rather than quietly dropping them, and leave behind only what
vcpkg has no answer for.
The sweep that picks them out of ntldd's output asked for a literal
bracket after the drive letter, so it matched nothing and not one
library travelled. The emulator then found vcpkg's zlib beside the
app instead of the one it was built against, and died on a missing
entry point before it could say anything.
contrib/plugins is a C++ sample that GCC 15's libstdc++ cannot
compile against the win32 thread model, and nothing here loads a TCG
plugin -- the backend drives QEMU over QMP and the gdbstub.
vcpkg keeps it in tools/glib, beside glib-mkenums, and only bin was
on the path -- so the dbus display the app draws from could not be
configured at all.
The emulators were downloaded from a qemu-<build> release built in
MSYS2 against MinGW's GLib and pixman, so the installer carried
those beside the vcpkg ones the app links: two copies of GLib and
friends.
Build them in the Windows job instead, against the vcpkg prefix,
cached like the other dependencies on the QEMU version and the
build script. QEMU builds under MinGW and nowhere else on Windows,
but UCRT64 and CLANGARM64 sit on the same C runtime as the MSVC
DLLs they now link, so a descriptor means the same on both sides.
The x86_64 environment moves from MINGW64 for that reason.
The emulators live a directory below the app, where nothing looks
for its GLib on its own, so they are started with the installation
root on PATH.
Windows drops out of the QEMU workflow, which macOS still uses.
Off Apple the xz container was unpacked by running /usr/bin/env xz,
which Windows has no answer for: Process reports that the file does
not exist, and the message reaches the user as it stands.
Decode in-process instead, the way Compression already does on
Apple, and ask for the library the platforms package.
The webview shims went with the native editors; the libraries and
packages they pulled in stayed. Let go of the WebView2 loader and
its COM and WinRT companions, the vcpkg port, and webkitgtk in the
AppImage.
Extract with shortcuts, since the tarball's dangling symlinks defeat
MSYS2's habit of copying what one points at, and install base-devel
for the make and diff that meson and the build itself want.
Build the arm64 flavour on an arm64 runner under CLANGARM64, name the
archive after the arch -- the MSYS2 runtime is x86_64 whatever it
builds for -- and stage it into that installer too.
Unpack the published archive beside the LumaCore bundle, where the
app already looks, and sign every emulator for the hardened runtime;
TCG needs the JIT entitlements.
Name the build once now that more than Windows uses it, and keep a
checksum per platform.
Drop the Monaco webview. Edit in a GtkSourceView on GTK and in a
native text view on Apple, both speaking to Frida.Compiler's
in-process language server for completions, signature help,
hovers and diagnostics.
Color, complete and indent code natively, matching the GitHub
theme in light and dark.
Fetch the qemu-<build> archive our workflow publishes instead of
unpacking the upstream installer, dropping the 7-Zip requirement and
the list of graphics libraries that only existed to leave that
installer's cruft behind. The checksum pin moves to the new archive;
fill it in from the release notes once qemu-11.1.0-luma.1 has built.
The upstream Windows build carries the GTK, SDL, VNC and curses
frontends, none of which the app drives, and with them a second copy
of GTK in the installer. Build the four emulators and qemu-img from
the unmodified source in MSYS2 with those frontends disabled, keeping
the dbus display, QMP and the gdb stub, and publish the archive
against a qemu-<build> tag the way the shader toolchain is published.
The release notes carry the checksum and name the corresponding
source, which is what GPLv2 asks of a distributor.
Carries the four emulators the barebone agents target, the libraries
they import and the firmware the pc and virt machines load -- 208 MB
of the 1.2 GB QEMU installs, since nothing reaches the edk2 images.
The arm64 installer skips it, QEMU publishing no build for it.
QEMU stays a separate GPL program we start over QMP, never linked,
and ships with its licences and an offer of its source.
Nothing packages a QEMU to build against, so the official build is
taken as it comes, pinned by date and by hash. Upstream publishes no
checksum, so the hash is of the bytes this was verified with.
Its installer wants administrator rights, which this script does not
ask for, so 7-Zip unpacks it instead. A QEMU built without the D-Bus
display cannot draw a guest, and is refused here rather than at boot.
Nothing refreshed the Meson subprojects, so a new FRIDA_CORE_REF built
against whatever Frida-gum the tree happened to hold, and failed in
Vala as if the API had gone missing.
Configure ran unconditionally too, which Frida refuses once a build
directory is there, so a run that died compiling could never resume.
Bootstrap provisions Frida, radare2 and the Pharo VM at their pinned
revisions and skips what is present, so a re-run resumes. The Windows
CI job drives it a component at a time. Build from a plain shell via
vcvars, and order the prefixes so a stale Frida cannot win the load.
Resolve PHARO_PREFIX alongside the vcpkg, frida-core and radare2 ones,
so pkg-config finds pharo-vm and run.ps1 finds its DLLs. Document
building it from frida/pharo-vm.
Drop build.ps1's agent bundle pre-run: it writes into
Sources/LumaCore/Generated, which the manifest excludes outside Xcode,
so LumaBundlePlugin is what feeds the build.
No distribution packages a Pharo VM, so what a package leaves out is
what the app goes without: the .rpm and the .deb both installed a
binary whose libPharoVMCore.so nothing on the system provides, and
the flatpak, the snap and the MSI were each short of it in their own
way.
The VM's prefix now ships whole in each of them -- plugins included,
since the image asks for a plugin by name and the VM answers by
looking beside libPharoVMCore, so half a directory is no better than
none.
Point frida-swift at the devkit this build made, the way every other
platform does, and drop the xcframework that stood between them.
Leave V8 out of it, sign the dylibs inside the frameworks before the
frameworks themselves, and add the GtkSourceView port Windows needs.
Stops after producing build/stage-release, the exact layout the
MSI wraps, so the deployed resource resolution can be smoke-tested
without WiX installed. Also defers the WiX lookup to the point it
is used, so staging no longer aborts up front when WiX is absent.
run.ps1 now stages the Adwaita icon theme so a locally-built
app renders icons instead of falling back to blanks. Factor
the fetch/extract out of package-msi.ps1 into a shared
fetch-adwaita.ps1 that both call.
Compile the GSettings schemas (vcpkg ships only the XML, so
GTK aborted on a null schema source), put the shared radare2
DLLs on PATH, and export GIO_EXTRA_MODULES for TLS.
SwiftPM lists a -L/-l pair per library, repeating the few
search paths hundreds of times, which pushed the final link
past Windows' command-line limit. Strip -L from a private
pkg-config mirror and put the lib dirs on LIB instead, so the
link fits and swift build succeeds without subst or a relink.
Also run luma-bundle-compiler and export the GIO/frida env the
build needs.
System32\tar.exe wedges for hours extracting the Adwaita tarball on the
hosted runner, even with the lone symlink excluded and a 120s cap (it
just hit the cap). The contents are unremarkable (1848 plain files, no
hardlinks), so it's a bsdtar/runner quirk. Switch to 7-Zip, a separate
implementation that extracts symlinks as plain files and doesn't
stall, in two passes (xz then tar).
The [stage] markers pinned the real culprit behind the multi-hour
Package MSI hangs: bsdtar extracting the Adwaita tarball wedged on its
lone relative symlink (folder-drag-accept-symbolic.svg), since
creating a symlink on the runner needs a privilege it lacks. DLL
staging was instant all along.
Exclude that symlink (the icon falls back to folder-symbolic) and run
tar through Start-Process with a 120s cap and redirected streams, so a
stall can't hang the build or deadlock on stderr.
Each staging step (DLL copy, frida-1.0, GTK data, TLS module, Adwaita,
schema compile, heat) now prints a [stage] marker, with per-search-path
DLL counts and timings, so a slow or wedged step is identifiable from
the build log instead of a silent gap.
The MSI staging walked LumaGtk.exe's import closure with one
dumpbin /DEPENDENTS spawn per dependency. Those spawns are
minutes-slow on the hosted runners, and one reliably wedged
indefinitely (confirmed by elimination: the adwaita download right
after is served from the front-loaded cache, so the stall sat in the
dumpbin walk, never the fetch).
Drop dumpbin entirely and copy every DLL from the runtime search
paths instead. It's a superset of the closure, so nothing is missed,
and it's instant and cannot hang. A handful of unused DLLs is
negligible beside the GTK/Swift runtime already shipped.
Package MSI wedged for hours on the x86_64 runner (it normally takes
under a minute, and arm64 still does). The stage step walks the DLL
import closure by spawning dumpbin /DEPENDENTS per binary; a DLL
pulled in by the bumped frida-core SDK makes one of those calls stall
indefinitely, and nothing bounded it.
Run dumpbin via Start-Process with a 60s cap, skipping a binary's
dependents (with a warning) if it hangs, and log each staged DLL so
the offending one is visible in the build output.
The Adwaita icon-theme fetch used Invoke-WebRequest with no timeout,
so a stalled download.gnome.org connection wedged Package MSI for
hours (the step normally runs in under a minute). Bound each attempt
to 120s and retry up to five times, in both the front-load step and
package-msi.ps1.
Use the dedicated luma.frida.re site (not frida.re or the GitHub
repo) for the homepage field across packaging metadata: .rpm URL,
.deb Homepage, snap website, and the MSI's ARPURLINFOABOUT entry
in Add/Remove Programs. Snap also gets an explicit issues URL and
the MSI an ARPHELPLINK pointing at the GitHub issue tracker.
Use the website's tagline ("The official Frida GUI") as the
short summary across all surfaces — .desktop Comment, .rpm
Summary, snap summary, .deb synopsis — and paraphrase the
hero lede for the matching long-form description fields. Also
update the welcome-screen subtitle in both the SwiftUI and
GTK frontends to match.
vcpkg's glib-networking portfile relocates GIO modules from
lib\gio\modules to plugins\glib-networking and then deletes lib\
outright. Our Copy-Tree against the old path silently produced
nothing, so Luma shipped without a TLS backend and libsoup HTTPS
just failed at runtime. Stage gioopenssl.dll explicitly from the
new vcpkg location and re-run the DLL closure walk against it so
libssl/libcrypto land alongside Luma.exe — they're imported by the
module, not by Luma.exe, so the exe-seeded walk never saw them.
Drop the equally pointless lib\gdk-pixbuf-2.0 copy: vcpkg builds
that port with -Dbuiltin_loaders=all, so the loaders live inside
gdk_pixbuf-2.0-0.dll and the lib\ subtree is just a stub cache.
Hard-fail if gioopenssl.dll isn't there rather than producing
another silently-broken MSI, and copy it by name (not a glob) so a
future glib-networking[gnutls] rebuild can't smuggle giognutls.dll
into the package and trade Windows' trust store for whatever CA
bundle gnutls happens to scrape up.
setup-env.ps1 prepends MSYS2's bin to PATH so sed/awk resolve to
their GnuWin32 ports (the cygwin-based Git for Windows ones crash
during gir2swift post-processing). The side effect: an unqualified
"tar" call lands on MSYS2's GNU tar, which reads the colon in a
path like D:\a\luma\luma\LumaGtk\build\adwaita-icon-theme-50.0
.tar.xz as an rcp host:path separator, fails with "Cannot connect
to D: resolve failed", and aborts the MSI staging step.
Invoke %WINDIR%\System32\tar.exe (Windows' libarchive build) by
full path. libarchive doesn't try to interpret colons as remote
specs, supports xz natively on the Server 2022/2025 runners we
use, and stays out of the MSYS2 vs GnuWin32 PATH dance.
vcpkg has no adwaita-icon-theme port and libadwaita's GResource
fallback only covers what its own widgets reference. So GTK calls
like Image(iconName: "accessories-dictionary-symbolic") in
NotebookPane's empty-state walkthrough resolved to nothing inside
the MSI even though the named-icon API is meant to be the standard
way to ship platform-agnostic UI.
Fetch adwaita-icon-theme 50.0 from download.gnome.org during the
MSI staging step, verify against a pinned SHA-256, and merge
Adwaita/{16x16,scalable,symbolic} plus index.theme into share/
icons/Adwaita next to vcpkg's existing icon trees. Drop cursors/
— 15 MB of X bitmap cursor data that Windows replaces with native
cursors anyway, leaving roughly 4 MB of icons that the runtime
actually consults.
GTK on Windows couldn't find the Luma logo (welcome window's
Image(iconName: "re.frida.Luma")) because nothing was copying our
data/icons/hicolor tree into the staged share/icons. The Linux
packagers (deb, rpm, flatpak, snap) all install it; the MSI step
quietly skipped it.
GIO on Windows also lacked TLS — Image and HTTP fetches over
https failed silently. glib-networking installs giognutls.dll
(plus gioopenssl.dll when openssl is on) into VCPKG_PREFIX/lib/
gio/modules, but the staging only mirrored share/glib-2.0/schemas,
share/icons and lib/gdk-pixbuf-2.0, so libgio-2.0-0.dll loaded
without a TLS backend at runtime.
Mirror both directories so the MSI ships them next to Luma.exe
the same way the Linux bundles do.
WiX's <Product Version=...> requires a strict numeric form
(major.minor.build[.revision], each 0-65535), which rejects the
"-dev.N" suffix our LUMA_VERSION can carry from git-describe.
Translate "X.Y.Z-dev.N" to "X.Y.Z.N" (and "X.Y.Z" to "X.Y.Z.0")
inside package-msi.ps1, used only for the MSI Product field. The
output filename keeps the original $Version so artifacts read the
same as the rpm/deb side.
The demo was a scaffolding shim for bringing up the Monaco editor
on GTK; MonacoEditor is now exercised by the real editor surfaces,
so the standalone window, the --monaco-demo argv filter, and the
frida-init guard that went with it can all go away.
Hook a release job into the build workflow that fires on bare
semver tags (e.g. 1.0.0), gathers every platform artifact, and
publishes them as a draft GitHub release for manual review.
Derive the version from the tag throughout: .rpm, .deb, .snap,
.msi, and the release asset filenames all carry it, so every
artifact in the release is self-identifying. Non-tag builds fall
back to 0.1.0.
Rename the Windows artifact's arch suffix from x64 to x86_64 to
match Frida's convention and align with the macOS DMGs. The WiX
candle -arch flag still needs x64, so package-msi.ps1 maps the
public name back internally.
SwiftPM invokes swiftc.exe as the final link driver with every
object file, -L path and -l ref on the command line. The
cross-platform build pulls in GTK, libadwaita, WebKit, Frida and
radare2, which blows past Windows' ~32 KB CreateProcess cap and
aborts with "The filename or extension is too long".
Steer the Windows CI build into a short --build-path
($RUNNER_TEMP\b) so the per-argument overhead shrinks enough for
the final link to fit. Teach package-msi.ps1 to locate the exe
under that path by adding a -BuildPath switch and using the
per-arch Swift target triplet (SwiftPM emits under
.build/<triplet>/<config>/, which the old hard-coded
.build/<config>/ path never matched).