Skip to content

Packaging

packaging/

build/dist/ — Native AOT's idea of "self-contained" — is a directory, not a file: GlavaSharp (the pwshim/x11shim Rust static libs are linked in here), GlavaSharp.dbg (debug symbols, split out separately), and two files Native AOT doesn't and can't statically link — libglfw.so.3.3 / libglfw-wayland.so.3.3, OpenTK's GLFW native package, dynamically loaded at startup — plus shaders/, deliberately shipped alongside rather than embedded (see Shader Module Pipeline — modules are loaded from disk by directory, not compiled in). None of that is a bug; it just means "single self-contained executable" was only ever true of the one GlavaSharp file itself, not the directory you actually need to hand someone.

cmake --build build --target appimage (packaging/build-appimage.sh, not part of the default ALL build since it needs network access on first run) packs all of it into one real single-file .AppImage:

  • Assembles build/AppDir/ from build/dist/ verbatim (minus GlavaSharp.dbg — debug symbols aren't needed to run, and bloat the AppImage by more than the rest of it combined) under usr/bin/, plus packaging/appimage/'s AppRun (a thin argv-passthrough script), GlavaSharp.desktop, and glavasharp.png — a static 2D reduction of the aurora module's own look (layered wavy curtains colored by altitude, soft bloom, a starfield, atmospheric haze — see GlavaSharp-Original Modules for what it's echoing), generated by packaging/appimage/generate-icon.py (numpy + Pillow, not a build dependency otherwise — rerun by hand and re-commit the PNG to retune it, it isn't regenerated as part of cmake --build build --target appimage).
  • Fetches appimagetool from its GitHub releases on first run (cached under build/tools/ afterwards) and runs it against build/AppDir/.
  • Runs appimagetool itself with APPIMAGE_EXTRACT_AND_RUN=1 — it's shipped as an AppImage too, which would otherwise try to FUSE-mount itself, and not every machine running this build has /dev/fuse available (many CI containers don't). This only affects how appimagetool runs during packaging; the GlavaSharp AppImage this produces supports both FUSE-mount and --appimage-extract-and-run for whoever runs it later, same as any AppImage.

Why AppRun needs no logic beyond an argv passthrough: Program.cs resolves the shader tree via AppContext.BaseDirectory (wherever the running executable actually lives), and libglfw*.so resolution is already relative to that same directory today (confirmed live — running build/dist/GlavaSharp directly, with no LD_LIBRARY_PATH set, already finds libglfw.so.3.3 sitting right next to it). Since packaging/build-appimage.sh preserves that exact same relative layout inside AppDir/usr/bin/, both keep resolving correctly once mounted — verified live: GlavaSharp-x86_64.AppImage --module aurora, run from /tmp (nowhere near the actual repo), logged shader paths resolving under its own FUSE mount point (/tmp/.mount_.../usr/bin/shaders/...) and compiled/linked normally.

The .desktop file exists because appimagetool requires (and validates) one — Categories= values have to be real freedesktop.org registered categories or prefixed X-; an early attempt using Visualization unprefixed failed validation. Terminal=true since GlavaSharp is fundamentally CLI-flag-driven (--desktop, --module, ...), not a double-click GUI app with no arguments to pass.