Packaging: deb, rpm, AppImage and Flatpak for simd - #35
Merged
Conversation
Some distro packagings -- the freedesktop Flatpak SDK among them -- ship a libproc2.pc whose Version field is the literal string UNKNOWN. Compared against that, VERSION_GREATER_EQUAL is always false, so the build fell through to the pre-4.0.5 four-argument PIDS_VAL API and failed to compile against a current libproc2 whose header moved to three arguments long ago. Take the old-API branch only when the version string really parses and really is old. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
simapi identifies a running sim by scanning /proc for known process names
and reads /proc/<pid>/environ for the Steam compat variables behind the
auto-bridge. Flatpak gives every sandbox its own PID namespace and offers no
way to share the host's: measured 634 processes on the host against 4 inside
a sandbox, unchanged by --allow=devel or --filesystem=host, and /proc cannot
be bound in either ("Path /proc is reserved by Flatpak"). simd has no flag
to be told which sim to use, so a sandboxed simd simply never finds one.
The sim lookup, the environ read, both liveness checks, notify-send and the
bridge exec now route through `flatpak-spawn --host`, guarded on the
presence of /.flatpak-info so a native build takes the original code paths
unchanged.
Three things this had to work around:
- The host process table is read with two tagged `ps` runs (args and comm)
rather than one combined format, because a process whose name contains
spaces -- "AC: main thread" -- cannot be parsed unambiguously from a
single line.
- Output comes back through a file staged in $XDG_RUNTIME_DIR/app/$FLATPAK_ID
and renamed into place, not through popen: a pipe to `flatpak-spawn --host`
never reaches EOF, so the read hangs.
- The redirection is written as `{ ...; } > out.part` because in `sh -c`
a trailing redirect binds only to the last command of a sequence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
No upstream CMake changes were needed. SYSTEMD_USER_UNIT_DIR and
SIMD_CONFIG_DIR are already CACHE PATH variables, so packaging redirects
them off $ENV{HOME} rather than patching the install rules.
Each format ships /usr/bin/simd, the libsimapi SONAME chain simd links at
runtime, and simd.config as an example -- a package must not write into a
user's home, which is what upstream's defaults do.
packaging/simd.service replaces the shipped unit for deb and rpm. Upstream's
conf/simd.service sets Type=simple against a daemon that double-forks, so
systemd would take the parent's exit as the service failing while the real
daemon carried on unsupervised, and it hardcodes ExecStart=%h/.local/bin/simd
with LD_LIBRARY_PATH=%h/.local/lib, which is only correct for a build
installed into ~/.local. The packaged unit runs /usr/bin/simd -n under
systemd's own supervision.
The Flatpak has no unit -- autostart is a ~/.config/autostart entry running
`flatpak run`, the same pattern other Flatpak background services use. Its
desktop entry passes -n for the same reason: the sandbox is torn down when
the process flatpak started exits, so a double-forking simd would take its
own daemon down with it.
The manifest documents plainly that this bundle can run arbitrary host
commands and is therefore not a security boundary. That is inherent to what
simd does: it identifies a sim by reading the host process table, which a
sandbox cannot see (634 host processes against 4 inside, /proc unbindable),
and flatpak-spawn --host is the only mechanism that reaches it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Builds on the packaging already in tools/distro rather than a parallel tree:
the per-distro control files and the two Fedora specs are used as they
stand, with only the version stamped from the tag. The split into libsimapi
and simd is upstream's and is kept.
Both specs' %prep cloned the default branch unconditionally, so an rpm's
contents tracked master rather than the tag being built -- a fix on the
branch being released would be absent from its own release. They now build a
staged tree when CI provides one and clone only as a fallback, so a bare
`rpmbuild -ba` still behaves as before.
simd.spec additionally ships the packaged systemd unit and simd.config as an
example, and redirects SYSTEMD_USER_UNIT_DIR and SIMD_CONFIG_DIR off
$ENV{HOME}. Neither needed an upstream code change; both are already CACHE
PATH variables.
The AppImage carries the config but not the unit -- it cannot install one
onto the host. tools/distro/simd.svg is a placeholder, present only because
linuxdeploy requires an icon and this repository has none.
The Flatpak job installs flatpak-builder with Recommends left enabled. Those
recommends are the helpers it shells out to at build time -- tar's
decompressors, eu-strip, patch -- and stripping them produces a builder that
runs, builds several modules, then fails on whichever helper the next module
happens to need.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three unrelated failures from the first run; the deb legs passed unchanged. rpm: Fedora packages neither yder nor orcania -- `dnf install yder-devel` fails with "No match for argument" on 43 and 44, while Debian and Ubuntu ship libyder-dev. Both are now vendored and built as static libraries, so simd links them without leaving a runtime dependency Fedora cannot satisfy and the spec's Requires: stays as upstream wrote it. AppImage: ubuntu-22.04 cannot build this at all. It ships libprocps rather than libproc2, so getpid.c fails on a missing <libproc2/pids.h>, and its GCC predates the C23 fixed-underlying-type enums in include/F12018/*.h. Moved to 24.04, which is now the oldest base that works. Flatpak: orcania and yder both installed to /app/lib64, so when yder configured, pkg-config did not find liborcania.pc under /app/lib/pkgconfig and yder linked without orcania. That surfaced much later and looked like something else entirely -- simd failing to link with undefined references to o_malloc, o_free, o_strdup and split_string attributed to libyder.so. Both modules now install to /app/lib. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Second round; the deb legs stayed green throughout and the previous fixes all held -- each of these is a new failure further along. rpm: CMAKE_CXX_COMPILER not set. simd/CMakeLists.txt uses CheckCXXCompilerFlag, which enables the CXX language, and Fedora's `gcc` package does not provide a C++ compiler. Added gcc-c++. AppImage: "Could not find dependency: libsimapi.so.1". simd links a library that exists only inside the AppDir, so linuxdeploy's ldd-based resolution could not find it. LD_LIBRARY_PATH now points at the staged AppDir. Flatpak: appstreamcli compose failed. The desktop entry named an icon that was never installed, so the component referenced an icon that did not exist, and NoDisplay=true would have had compose discard the component anyway, leaving the bundle with no metadata. The icon is now installed and the entry is listed -- which also suits how this is meant to be started, since a desktop's "startup applications" picker will not offer a NoDisplay entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two of the three remaining failures were the same mistake made twice, so they are fixed generally rather than per-leg. CMake's GNUInstallDirs picks a different library directory on every platform -- lib64 in the freedesktop runtime, lib/x86_64-linux-gnu on Ubuntu -- and both guesses were already wrong once. In the Flatpak, orcania and yder landed in lib64 where yder's own configure could not find liborcania.pc; in the AppImage, libsimapi landed under the multiarch triplet while linuxdeploy looked in AppDir/usr/lib. CMAKE_INSTALL_LIBDIR is now pinned to lib wherever this project controls the build. simd links yder, and yder calls into orcania. A shared libyder.so records that dependency, which is why Debian and Ubuntu never needed anything: their link just follows it. A static libyder.a records nothing, so o_malloc, o_free, o_strdup, o_strnullempty and split_string all come back undefined -- which is what the Fedora rpm hit, since Fedora packages neither library and the job vendors both as static archives. simd/CMakeLists.txt now names orcania explicitly when it is present, after yder, as a static link needs. appstreamcli compose was failing on the metainfo: no developer element, a description opening on a lowercase word, and an icon that existed only as SVG. A 128x128 raster is now installed alongside it. Verified with appstreamcli validate in the freedesktop SDK -- the only remaining finding is url-not-reachable, which is the sandbox having no network. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Flatpak's "appstreamcli compose failed" gave no detail in the build log,
so this run was diagnosed by staging a prefix and running compose directly
in the freedesktop SDK. It names the cause outright:
E: file-read-error
Unable to read data from file
`/usr/share/icons/hicolor/scalable/apps/io.github.spacefreak18.simd.svg`:
Unrecognized image file format
appstreamcli in that runtime cannot decode the SVG at all, so installing it
alongside the PNG was not a fallback, it was the failure. Removing it makes
compose report Success against the same staged prefix. The bundle now ships
the 128x128 raster only.
AppImage: "Could not find suitable icon for Icon entry". linuxdeploy takes an
icon's name from its filename, so simd-128.png was deployed as "simd-128"
while the desktop entry names io.github.spacefreak18.simd. Copied to the
matching filename before deploying.
rpm: check-rpaths rejected /usr/bin/simd for a RUNPATH pointing into the
build tree, which CMake bakes in because simd links libsimapi from there.
The spec's %global __brp_check_rpaths %{nil} no longer suppresses that on
Fedora 43/44, so CMAKE_SKIP_RPATH stops it being emitted instead. Nothing
needs it: libsimapi installs to a standard system library directory.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…esult The bundle built, packaged and uploaded cleanly, then failed the moment it was installed and started: simd: error while loading shared libraries: libuv.so.1: cannot open shared object file: No such file or directory libuv and libconfig installed to /app/lib64 while everything else was in /app/lib, and only /app/lib is on the runtime's library search path. The previous fix pinned CMAKE_INSTALL_LIBDIR on three modules; it needed to be all six. The reason CI was green is that nothing in that job ever executed what it built. It now installs the bundle and runs `simd --version`, which is enough to prove every shared library resolves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Owner
|
we can merge this although, having code for different build types scares me for long term maintainability |
Owner
|
the other issue is in the "stage" directory.
|
Owner
same goes for the config and .pc files which exist elsewhere |
Spacefreak18
pushed a commit
that referenced
this pull request
Sep 10, 2026
Owner
nevermind, it was just an artifact that got checked in by mistake. I fixed it retroactively, but it might force you to blow away your repo next time you work on it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds simd and libsimapi as .deb, .rpm, AppImage and Flatpak in CI, keeping the
libsimapi/simdsplit and the per-distro control files already intools/distro. All seven jobs green, and the result has been installed and run on a real system.This exists because simd was being bundled into monocoque's Flatpak, and you'd rather it stayed separate. Agreed — but a Flatpak monocoque still needs a simd it can reach, so simd needs packaging of its own. On an immutable host a .deb or .rpm isn't installable, which is why the Flatpak matters.
Read this part before the rest
The Flatpak can run arbitrary commands on the host, and its sandbox is not a security boundary.
That's inherent to what simd does. It identifies a running sim by scanning
/procfor known process names and reads/proc/<pid>/environfor the Steam compat variables behind the auto-bridge. Flatpak gives every sandbox its own PID namespace with no way to share the host's — measured 640 processes visible via the host against 4 inside — and/proccan't be bound in ("Path /proc is reserved by Flatpak").flatpak-spawn --hostis the only mechanism that reaches it, and it's general purpose by nature.So
--talk-name=org.freedesktop.Flatpakis doing real work and is the reason the bundle isolates nothing. The manifest and metainfo both say so plainly. If you'd rather not carry that, the honest alternative is a--simflag so simd can be told what to map, which would let a sandboxed simd work with only--device=shm— at the cost of auto-detection and the auto-bridge.No source changes were needed for packaging
SYSTEM_USER_UNIT_DIRandSIMD_CONFIG_DIRare alreadyCACHE PATH, so packaging redirects them off$ENV{HOME}with-Dflags rather than patching install rules.The one runtime change is host routing: the sim lookup, environ read, both liveness checks, notify-send and the bridge exec go through
flatpak-spawn --host, guarded on/.flatpak-infoso a native build takes the original paths untouched. Three things it had to work around are documented in that commit — a process namedAC: main threadcan't be parsed from a combinedpsline, a pipe toflatpak-spawnnever reaches EOF, and a trailing redirect insh -cbinds only to the last command of a sequence.Changes to your existing packaging
%prepcloned the default branch unconditionally, so an rpm's contents tracked master rather than the tag being built. They now use a staged tree when CI provides one and clone only as a fallback, so a barerpmbuild -bais unchanged.simd.specships the unit andsimd.configas an example, and setsCMAKE_SKIP_RPATH—check-rpathsrejects the binary for a build-tree RUNPATH, and%global __brp_check_rpaths %{nil}no longer suppresses that on Fedora 43/44.tools/distro/simd.serviceis a packaged unit. Yours setsType=simpleagainst a daemon that double-forks, so systemd takes the parent's exit as the service failing while the real daemon carries on unsupervised, and it hardcodesExecStart=%h/.local/bin/simd. The packaged one runs/usr/bin/simd -n.tools/distro/simd.svgandsimd-128.pngare placeholder artwork, present only because linuxdeploy and appstream require an icon. Replace whenever.Verified
All seven jobs green. On a Bazzite host, installed alongside the monocoque and monocoque-builder Flatpaks: simd starts, loads its config, opens the universal shared memory, publishes
/dev/shm/SIMAPI.DATvisible to both other sandboxes, sees the host process table, detects a running sim, and the shakers went active.Contains the two commits from #34; happy to rebase once that lands.
🤖 Generated with Claude Code