[PATCH v9] Generate file lists from a script

Demi Marie Obenour
2025-10-02T17:38:18Z

Right now, the makefiles in host/rootfs, vm/sys/net, and img/app have
manually-maintained lists of files and symlinks. These duplicate the
information in the git repository and can easily get out of sync or
cause unnecessary merge conflicts. Fix all of these issues by having
the git repository be the source of truth, and using a script to
generate the file lists. Developers can regenerate the lists before
every commit, or even add a git hook to do that.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
---
This actually reduces the amount of code that has to be written by hand.
---
Changes in v9:
- Actually change to the toplevel directory of the git repository.
- Link to v8: https://spectrum-os.org/lists/archives/spectrum-devel/20251001-genfiles-v8-1-171a29081ab7@gmail.com

Changes in v8:
- Re-add missing DIRS in vm/sys/net/Makefile.
- Re-add missing "$dir" in scripts/genfiles.sh.
- Link to v7: https://spectrum-os.org/lists/archives/spectrum-devel/20250929-genfiles-v7-1-5a702ee452b1@gmail.com

Changes in v7:
- Do not break sorting of directories in host/rootfs/Makefile.
- Add newline after generated copyright headers.
- Add comment to scripts/genfiles.awk explaining use of exit_code variable.
- Check filename instead of $0 to see if a file is a license file.
This has no functional impact.
- Add missing copyright to scripts/genfiles.sh.
- Prevent reuse from tripping over scripts/genfiles.awk.
- Link to v6: https://spectrum-os.org/lists/archives/spectrum-devel/20250927-genfiles-v6-1-2111a981596d@gmail.com

Changes in v6:
- Do not include comment in generated makefiles
- Change makefile license to CC0.
- Link to v5: https://spectrum-os.org/lists/archives/spectrum-devel/20250926-genfiles-v5-1-3394dfb3b330@gmail.com

Changes in v5:
- Use 'print ""' instead of 'print' in awk to print a newline. 'print'
with no arguments implicitly prints $0 instead. This caused the
generated makefiles to be incorrect.
- Use S6_RC_FILES instead of VM_S6_RC_FILES in vm/sys/net/Makefile and
img/app/Makefile. This prevented the image from being built.
- Do not check for git repository being in a directory with a name
ending in a newline.
- Use shell redirection instead of awk redirection.
- Do not include trailing DONE line in input to awk.
- Link to v4: https://spectrum-os.org/lists/archives/spectrum-devel/20250921-genfiles-v4-1-4375bda78707@gmail.com

Changes in v4:
- Use /bin/sh instead of bash.
- Do not assume that negated awk character classes match all bytes.
- Do not check the mode of license files.
- Use implicit awk variable initialization.
- Use 'git rev-parse --show-toplevel' to find the repository root.
- Remove wrongly added copyright header.
- Improve documentation.
- Remove git hooks.
- Add missing copyright header.
- Avoid non-portable /usr/bin/env -S.
- Avoid assuming that awk is GNU awk.
- Avoid non-portable awk -E.
- Do not check for git bugs.
- Fix link in v3 changelog.
- Link to v3: https://spectrum-os.org/lists/archives/spectrum-devel/20250920-genfiles-v3-1-d6c2b6767b42@gmail.com

Changes in v3:
- Only include the file list generator. Move the rest to separate patch
series.
- Remove the update-file-list make targets from img/app/Makefile and
vm/sys/net/Makefile.
- Link to v2: https://spectrum-os.org/lists/archives/spectrum-devel/20250910-genfiles-v2-0-37ebe07a3cdc@gmail.com

Changes in v2:
- Drop the last patch (switching to /etc/s6-rc/compiled) as it is
controversial and should be reviewed separately.
- Add missing copyright notices.
- Use a wrapper shell script to make the awk code easier to read.
- Improve documentation.
- Add helper scripts for use in git hooks and rebasing.
- Link to v1: https://spectrum-os.org/lists/archives/spectrum-devel/20250903-genfiles-v1-0-cc993fcb1e4c@gmail.com/
---
Documentation/development/built-in-vms.adoc | 7 ++
host/rootfs/Makefile | 107 +---------------------------
host/rootfs/file-list.mk | 102 ++++++++++++++++++++++++++
img/app/Makefile | 84 ++++------------------
img/app/file-list.mk | 65 +++++++++++++++++
scripts/genfiles.awk | 85 ++++++++++++++++++++++
scripts/genfiles.sh | 27 +++++++
vm/sys/net/Makefile | 55 +++-----------
vm/sys/net/file-list.mk | 41 +++++++++++
9 files changed, 352 insertions(+), 221 deletions(-)

diff --git a/Documentation/development/built-in-vms.adoc b/Documentation/development/built-in-vms.adoc
index e90009ee5a3c2c254a7ae11e36121576b819eee7..d044e7509a43df4b015f312dac61be6c74cc7feb 100644
--- a/Documentation/development/built-in-vms.adoc
+++ b/Documentation/development/built-in-vms.adoc
@@ -3,6 +3,7 @@
:page-nav_order: 3

// SPDX-FileCopyrightText: 2023-2025 Alyssa Ross <hi@alyssa.is>
+// SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
// SPDX-License-Identifier: GFDL-1.3-no-invariants-or-later OR CC-BY-SA-4.0

Spectrum comes with some built-in application VMs, to demonstrate
@@ -44,6 +45,12 @@ NOTE: As a special convenience, it's not necessary to run `make clean`
if the only change to the Nix files is modifying the packages
installed in the VM.

+The list of files used for images is stored in a separate file,
+file-list.mk. To update it, run scripts/genfiles.sh, which will
+regenerate it from the output of `git ls-files`. This script uses
+Git's index to generate the list, so only staged changes will be
+reflected in its output.
+
By default, `make run` runs the VM in Cloud Hypervisor, to match the
Spectrum environment. You can also add `VMM=crosvm` or `VMM=qemu` to
your command line to run the VM in a different VMM. One reason to do
diff --git a/host/rootfs/Makefile b/host/rootfs/Makefile
index a0b32dc6af554d4122974e214e5ba2b648608bae..aa45ca1d5c18d0dfb78d19267f263cc4222e8e84 100644
--- a/host/rootfs/Makefile
+++ b/host/rootfs/Makefile
@@ -4,67 +4,10 @@
.POSIX:

include ../../lib/common.mk
+include file-list.mk

dest = build/rootfs.erofs

-FILES = \
- image/etc/fonts/fonts.conf \
- image/etc/fstab \
- image/etc/group \
- image/etc/init \
- image/etc/login \
- image/etc/mdev.conf \
- image/etc/mdev/listen \
- image/etc/mdev/net/add \
- image/etc/mdev/wait \
- image/etc/parse-devname \
- image/etc/passwd \
- image/etc/s6-linux-init/env/WAYLAND_DISPLAY \
- image/etc/s6-linux-init/env/XDG_RUNTIME_DIR \
- image/etc/s6-linux-init/run-image/service/dbus/notification-fd \
- image/etc/s6-linux-init/run-image/service/dbus/run \
- image/etc/s6-linux-init/run-image/service/dbus/template/notification-fd \
- image/etc/s6-linux-init/run-image/service/dbus/template/run \
- image/etc/s6-linux-init/run-image/service/getty-tty1/run \
- image/etc/s6-linux-init/run-image/service/getty-tty2/run \
- image/etc/s6-linux-init/run-image/service/getty-tty3/run \
- image/etc/s6-linux-init/run-image/service/getty-tty4/run \
- image/etc/s6-linux-init/run-image/service/s6-svscan-log/notification-fd \
- image/etc/s6-linux-init/run-image/service/s6-svscan-log/run \
- image/etc/s6-linux-init/run-image/service/serial-getty-generator/run \
- image/etc/s6-linux-init/run-image/service/serial-getty/notification-fd \
- image/etc/s6-linux-init/run-image/service/serial-getty/run \
- image/etc/s6-linux-init/run-image/service/serial-getty/template/run \
- image/etc/s6-linux-init/run-image/service/vhost-user-fs/notification-fd \
- image/etc/s6-linux-init/run-image/service/vhost-user-fs/run \
- image/etc/s6-linux-init/run-image/service/vhost-user-fs/template/notification-fd \
- image/etc/s6-linux-init/run-image/service/vhost-user-fs/template/run \
- image/etc/s6-linux-init/run-image/service/vhost-user-gpu/notification-fd \
- image/etc/s6-linux-init/run-image/service/vhost-user-gpu/run \
- image/etc/s6-linux-init/run-image/service/vhost-user-gpu/template/data/check \
- image/etc/s6-linux-init/run-image/service/vhost-user-gpu/template/notification-fd \
- image/etc/s6-linux-init/run-image/service/vhost-user-gpu/template/run \
- image/etc/s6-linux-init/run-image/service/vmm/notification-fd \
- image/etc/s6-linux-init/run-image/service/vmm/run \
- image/etc/s6-linux-init/run-image/service/vmm/template/notification-fd \
- image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/notification-fd \
- image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/run \
- image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd \
- image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/run \
- image/etc/s6-linux-init/scripts/rc.init \
- image/etc/xdg/weston/autolaunch \
- image/etc/xdg/weston/weston.ini \
- image/usr/bin/assign-devices \
- image/usr/bin/create-vm-dependencies \
- image/usr/bin/run-appimage \
- image/usr/bin/run-vmm \
- image/usr/bin/vm-console \
- image/usr/bin/vm-import \
- image/usr/bin/vm-start \
- image/usr/bin/vm-stop \
- image/usr/bin/xdg-open \
- image/usr/share/dbus-1/services/org.freedesktop.portal.Documents.service
-
DIRS = \
dev \
etc/s6-linux-init/env \
@@ -101,18 +44,9 @@ DIRS = \

FIFOS = etc/s6-linux-init/run-image/service/s6-svscan-log/fifo

-# These are separate because they need to be included, but putting
-# them as make dependencies would confuse make.
-LINKS = \
- image/bin \
- image/etc/s6-linux-init/run-image/opengl-driver \
- image/etc/s6-linux-init/run-image/service/vmm/template/run \
- image/lib \
- image/sbin
-
BUILD_FILES = build/etc/s6-rc

-$(dest): ../../scripts/make-erofs.sh $(PACKAGES_FILE) $(FILES) $(BUILD_FILES) build/empty build/fifo
+$(dest): ../../scripts/make-erofs.sh $(PACKAGES_FILE) $(FILES) $(BUILD_FILES) build/empty build/fifo file-list.mk
{ \
cat $(PACKAGES_FILE) ;\
for file in $(FILES) $(LINKS); do printf '%s\n%s\n' $$file "$${file#image/}"; done ;\
@@ -128,41 +62,6 @@ build/fifo:
build/empty:
mkdir -p $@

-S6_RC_FILES = \
- image/etc/s6-rc/card0/type \
- image/etc/s6-rc/card0/up \
- image/etc/s6-rc/core/type \
- image/etc/s6-rc/core/up \
- image/etc/s6-rc/kvm/timeout-up \
- image/etc/s6-rc/kvm/type \
- image/etc/s6-rc/kvm/up \
- image/etc/s6-rc/mdevd-coldplug/dependencies.d/mdevd \
- image/etc/s6-rc/mdevd-coldplug/type \
- image/etc/s6-rc/mdevd-coldplug/up \
- image/etc/s6-rc/mdevd/notification-fd \
- image/etc/s6-rc/mdevd/run \
- image/etc/s6-rc/mdevd/type \
- image/etc/s6-rc/ok-all/contents.d/mdevd-coldplug \
- image/etc/s6-rc/ok-all/contents.d/sys-vmms \
- image/etc/s6-rc/ok-all/contents.d/vm-env \
- image/etc/s6-rc/ok-all/type \
- image/etc/s6-rc/static-nodes/type \
- image/etc/s6-rc/static-nodes/up \
- image/etc/s6-rc/sys-vmms/dependencies.d/vmm-env \
- image/etc/s6-rc/sys-vmms/type \
- image/etc/s6-rc/sys-vmms/up \
- image/etc/s6-rc/vm-env/contents.d/static-nodes \
- image/etc/s6-rc/vm-env/contents.d/weston \
- image/etc/s6-rc/vm-env/type \
- image/etc/s6-rc/vmm-env/contents.d/core \
- image/etc/s6-rc/vmm-env/contents.d/kvm \
- image/etc/s6-rc/vmm-env/contents.d/static-nodes \
- image/etc/s6-rc/vmm-env/type \
- image/etc/s6-rc/weston/dependencies.d/card0 \
- image/etc/s6-rc/weston/notification-fd \
- image/etc/s6-rc/weston/run \
- image/etc/s6-rc/weston/type
-
# s6-rc-compile's input is a directory, but that doesn't play nice
# with Make, because it won't know to update if some file in the
# directory is changed, or a file is created or removed in a
@@ -170,7 +69,7 @@ S6_RC_FILES = \
# including files that aren't intended to be part of the input, like
# temporary editor files or .license files. So for all these reasons,
# only explicitly listed files are made available to s6-rc-compile.
-build/etc/s6-rc: $(S6_RC_FILES)
+build/etc/s6-rc: $(S6_RC_FILES) file-list.mk
mkdir -p $$(dirname $@)
rm -rf $@
set -uo pipefail && dir=$$(mktemp -d) && \
diff --git a/host/rootfs/file-list.mk b/host/rootfs/file-list.mk
new file mode 100644
index 0000000000000000000000000000000000000000..3694dc472a013fff931847171145465329fb800f
--- /dev/null
+++ b/host/rootfs/file-list.mk
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+FILES = \
+ image/etc/fonts/fonts.conf \
+ image/etc/fstab \
+ image/etc/group \
+ image/etc/init \
+ image/etc/login \
+ image/etc/mdev.conf \
+ image/etc/mdev/listen \
+ image/etc/mdev/net/add \
+ image/etc/mdev/wait \
+ image/etc/parse-devname \
+ image/etc/passwd \
+ image/etc/s6-linux-init/env/WAYLAND_DISPLAY \
+ image/etc/s6-linux-init/env/XDG_RUNTIME_DIR \
+ image/etc/s6-linux-init/run-image/service/dbus/notification-fd \
+ image/etc/s6-linux-init/run-image/service/dbus/run \
+ image/etc/s6-linux-init/run-image/service/dbus/template/notification-fd \
+ image/etc/s6-linux-init/run-image/service/dbus/template/run \
+ image/etc/s6-linux-init/run-image/service/getty-tty1/run \
+ image/etc/s6-linux-init/run-image/service/getty-tty2/run \
+ image/etc/s6-linux-init/run-image/service/getty-tty3/run \
+ image/etc/s6-linux-init/run-image/service/getty-tty4/run \
+ image/etc/s6-linux-init/run-image/service/s6-svscan-log/notification-fd \
+ image/etc/s6-linux-init/run-image/service/s6-svscan-log/run \
+ image/etc/s6-linux-init/run-image/service/serial-getty-generator/run \
+ image/etc/s6-linux-init/run-image/service/serial-getty/notification-fd \
+ image/etc/s6-linux-init/run-image/service/serial-getty/run \
+ image/etc/s6-linux-init/run-image/service/serial-getty/template/run \
+ image/etc/s6-linux-init/run-image/service/vhost-user-fs/notification-fd \
+ image/etc/s6-linux-init/run-image/service/vhost-user-fs/run \
+ image/etc/s6-linux-init/run-image/service/vhost-user-fs/template/notification-fd \
+ image/etc/s6-linux-init/run-image/service/vhost-user-fs/template/run \
+ image/etc/s6-linux-init/run-image/service/vhost-user-gpu/notification-fd \
+ image/etc/s6-linux-init/run-image/service/vhost-user-gpu/run \
+ image/etc/s6-linux-init/run-image/service/vhost-user-gpu/template/data/check \
+ image/etc/s6-linux-init/run-image/service/vhost-user-gpu/template/notification-fd \
+ image/etc/s6-linux-init/run-image/service/vhost-user-gpu/template/run \
+ image/etc/s6-linux-init/run-image/service/vmm/notification-fd \
+ image/etc/s6-linux-init/run-image/service/vmm/run \
+ image/etc/s6-linux-init/run-image/service/vmm/template/notification-fd \
+ image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/notification-fd \
+ image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/run \
+ image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd \
+ image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/run \
+ image/etc/s6-linux-init/scripts/rc.init \
+ image/etc/xdg/weston/autolaunch \
+ image/etc/xdg/weston/weston.ini \
+ image/usr/bin/assign-devices \
+ image/usr/bin/create-vm-dependencies \
+ image/usr/bin/run-appimage \
+ image/usr/bin/run-vmm \
+ image/usr/bin/vm-console \
+ image/usr/bin/vm-import \
+ image/usr/bin/vm-start \
+ image/usr/bin/vm-stop \
+ image/usr/bin/xdg-open \
+ image/usr/share/dbus-1/services/org.freedesktop.portal.Documents.service
+
+LINKS = \
+ image/bin \
+ image/etc/s6-linux-init/run-image/opengl-driver \
+ image/etc/s6-linux-init/run-image/service/vmm/template/run \
+ image/lib \
+ image/sbin
+
+S6_RC_FILES = \
+ image/etc/s6-rc/card0/type \
+ image/etc/s6-rc/card0/up \
+ image/etc/s6-rc/core/type \
+ image/etc/s6-rc/core/up \
+ image/etc/s6-rc/kvm/timeout-up \
+ image/etc/s6-rc/kvm/type \
+ image/etc/s6-rc/kvm/up \
+ image/etc/s6-rc/mdevd-coldplug/dependencies.d/mdevd \
+ image/etc/s6-rc/mdevd-coldplug/type \
+ image/etc/s6-rc/mdevd-coldplug/up \
+ image/etc/s6-rc/mdevd/notification-fd \
+ image/etc/s6-rc/mdevd/run \
+ image/etc/s6-rc/mdevd/type \
+ image/etc/s6-rc/ok-all/contents.d/mdevd-coldplug \
+ image/etc/s6-rc/ok-all/contents.d/sys-vmms \
+ image/etc/s6-rc/ok-all/contents.d/vm-env \
+ image/etc/s6-rc/ok-all/type \
+ image/etc/s6-rc/static-nodes/type \
+ image/etc/s6-rc/static-nodes/up \
+ image/etc/s6-rc/sys-vmms/dependencies.d/vmm-env \
+ image/etc/s6-rc/sys-vmms/type \
+ image/etc/s6-rc/sys-vmms/up \
+ image/etc/s6-rc/vm-env/contents.d/static-nodes \
+ image/etc/s6-rc/vm-env/contents.d/weston \
+ image/etc/s6-rc/vm-env/type \
+ image/etc/s6-rc/vmm-env/contents.d/core \
+ image/etc/s6-rc/vmm-env/contents.d/kvm \
+ image/etc/s6-rc/vmm-env/contents.d/static-nodes \
+ image/etc/s6-rc/vmm-env/type \
+ image/etc/s6-rc/weston/dependencies.d/card0 \
+ image/etc/s6-rc/weston/notification-fd \
+ image/etc/s6-rc/weston/run \
+ image/etc/s6-rc/weston/type
diff --git a/img/app/Makefile b/img/app/Makefile
index 93eeefb53e23f250812039a4d28a7ccd59e79446..981889ebe55d9ba03228977f3dc0ea3f26d5c4fb 100644
--- a/img/app/Makefile
+++ b/img/app/Makefile
@@ -5,6 +5,7 @@
.POSIX:

include ../../lib/common.mk
+include file-list.mk

prefix = build/host
libdir = $(prefix)/lib
@@ -29,45 +30,13 @@ $(imgdir)/appvm/blk/root.img: ../../scripts/make-gpt.sh ../../scripts/sfdisk-fie
build/rootfs.erofs:root:5460386f-2203-4911-8694-91400125c604:root
mv $@.tmp $@

-VM_FILES = \
- image/etc/dbus-1/session.conf \
- image/etc/fonts/fonts.conf \
- image/etc/fstab \
- image/etc/init \
- image/etc/mdev.conf \
- image/etc/mdev/iface \
- image/etc/mdev/listen \
- image/etc/mdev/virtiofs \
- image/etc/mdev/wait \
- image/etc/passwd \
- image/etc/pipewire/pipewire.conf \
- image/etc/resolv.conf \
- image/etc/s6-linux-init/env/DBUS_SESSION_BUS_ADDRESS \
- image/etc/s6-linux-init/env/DISPLAY \
- image/etc/s6-linux-init/env/GTK_USE_PORTAL \
- image/etc/s6-linux-init/env/NIX_XDG_DESKTOP_PORTAL_DIR \
- image/etc/s6-linux-init/env/WAYLAND_DISPLAY \
- image/etc/s6-linux-init/env/XDG_RUNTIME_DIR \
- image/etc/s6-linux-init/run-image/service/getty-hvc0/run \
- image/etc/s6-linux-init/run-image/service/s6-linux-init-shutdownd/notification-fd \
- image/etc/s6-linux-init/run-image/service/s6-linux-init-shutdownd/run \
- image/etc/s6-linux-init/scripts/rc.init \
- image/etc/s6-linux-init/scripts/rc.shutdown \
- image/etc/s6-linux-init/scripts/rc.shutdown.final \
- image/etc/wireplumber/wireplumber.conf.d/99_spectrum.conf \
- image/etc/xdg/xdg-desktop-portal/portals.conf
-
-VM_DIRS = dev run proc sys tmp \
+DIRS = dev run proc sys tmp \
etc/s6-linux-init/run-image/service \
etc/s6-linux-init/run-image/user \
etc/s6-linux-init/run-image/wait
-VM_FIFOS = etc/s6-linux-init/run-image/service/s6-linux-init-shutdownd/fifo
+FIFOS = etc/s6-linux-init/run-image/service/s6-linux-init-shutdownd/fifo

-# These are separate because they need to be included, but putting
-# them as make dependencies would confuse make.
-VM_LINKS = image/bin image/etc/ssl/certs/ca-certificates.crt image/sbin
-
-VM_BUILD_FILES = build/etc/s6-rc
+BUILD_FILES = build/etc/s6-rc

build/fifo:
mkdir -p build
@@ -76,50 +45,21 @@ build/fifo:
build/empty:
mkdir -p $@

-build/rootfs.erofs: ../../scripts/make-erofs.sh $(PACKAGES_FILE) $(VM_FILES) $(VM_BUILD_FILES) build/empty build/fifo
+build/rootfs.erofs: ../../scripts/make-erofs.sh $(PACKAGES_FILE) $(FILES) $(BUILD_FILES) build/empty build/fifo file-list.mk
{ \
cat $(PACKAGES_FILE) ;\
- for file in $(VM_FILES) $(VM_LINKS); do printf '%s\n%s\n' $$file "$${file#image/}"; done ;\
- for file in $(VM_BUILD_FILES); do printf '%s\n%s\n' $$file $${file#build/}; done ;\
- printf 'build/empty\n%s\n' $(VM_DIRS) ;\
- printf 'build/fifo\n%s\n' $(VM_FIFOS) ;\
+ for file in $(FILES) $(LINKS); do printf '%s\n%s\n' $$file "$${file#image/}"; done ;\
+ for file in $(BUILD_FILES); do printf '%s\n%s\n' $$file $${file#build/}; done ;\
+ printf 'build/empty\n%s\n' $(DIRS) ;\
+ printf 'build/fifo\n%s\n' $(FIFOS) ;\
} | ../../scripts/make-erofs.sh $@

-VM_S6_RC_FILES = \
- image/etc/s6-rc/app/dependencies.d/dbus \
- image/etc/s6-rc/app/dependencies.d/pipewire \
- image/etc/s6-rc/app/dependencies.d/wayland-proxy-virtwl \
- image/etc/s6-rc/app/run \
- image/etc/s6-rc/app/type \
- image/etc/s6-rc/dbus/notification-fd \
- image/etc/s6-rc/dbus/run \
- image/etc/s6-rc/dbus/type \
- image/etc/s6-rc/mdevd-coldplug/dependencies.d/mdevd \
- image/etc/s6-rc/mdevd-coldplug/type \
- image/etc/s6-rc/mdevd-coldplug/up \
- image/etc/s6-rc/mdevd/notification-fd \
- image/etc/s6-rc/mdevd/run \
- image/etc/s6-rc/mdevd/type \
- image/etc/s6-rc/ok-all/contents.d/app \
- image/etc/s6-rc/ok-all/contents.d/mdevd-coldplug \
- image/etc/s6-rc/ok-all/contents.d/wireplumber \
- image/etc/s6-rc/ok-all/type \
- image/etc/s6-rc/pipewire/notification-fd \
- image/etc/s6-rc/pipewire/run \
- image/etc/s6-rc/pipewire/type \
- image/etc/s6-rc/wayland-proxy-virtwl/notification-fd \
- image/etc/s6-rc/wayland-proxy-virtwl/run \
- image/etc/s6-rc/wayland-proxy-virtwl/type \
- image/etc/s6-rc/wireplumber/dependencies.d/dbus \
- image/etc/s6-rc/wireplumber/dependencies.d/pipewire \
- image/etc/s6-rc/wireplumber/run \
- image/etc/s6-rc/wireplumber/type
-
-build/etc/s6-rc: $(VM_S6_RC_FILES)
+
+build/etc/s6-rc: $(S6_RC_FILES) file-list.mk
mkdir -p $$(dirname $@)
rm -rf $@
set -uo pipefail && dir=$$(mktemp -d) && \
- { tar -c $(VM_S6_RC_FILES) | tar -C $$dir -x --strip-components 3; } && \
+ { tar -c $(S6_RC_FILES) | tar -C $$dir -x --strip-components 3; } && \
s6-rc-compile $@ $$dir; \
exit=$$?; rm -r $$dir; exit $$exit

diff --git a/img/app/file-list.mk b/img/app/file-list.mk
new file mode 100644
index 0000000000000000000000000000000000000000..9967285ca9972b675713de36859850fed609f15f
--- /dev/null
+++ b/img/app/file-list.mk
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+FILES = \
+ image/etc/dbus-1/session.conf \
+ image/etc/fonts/fonts.conf \
+ image/etc/fstab \
+ image/etc/init \
+ image/etc/mdev.conf \
+ image/etc/mdev/iface \
+ image/etc/mdev/listen \
+ image/etc/mdev/virtiofs \
+ image/etc/mdev/wait \
+ image/etc/passwd \
+ image/etc/pipewire/pipewire.conf \
+ image/etc/resolv.conf \
+ image/etc/s6-linux-init/env/DBUS_SESSION_BUS_ADDRESS \
+ image/etc/s6-linux-init/env/DISPLAY \
+ image/etc/s6-linux-init/env/GTK_USE_PORTAL \
+ image/etc/s6-linux-init/env/NIX_XDG_DESKTOP_PORTAL_DIR \
+ image/etc/s6-linux-init/env/WAYLAND_DISPLAY \
+ image/etc/s6-linux-init/env/XDG_RUNTIME_DIR \
+ image/etc/s6-linux-init/run-image/service/getty-hvc0/run \
+ image/etc/s6-linux-init/run-image/service/s6-linux-init-shutdownd/notification-fd \
+ image/etc/s6-linux-init/run-image/service/s6-linux-init-shutdownd/run \
+ image/etc/s6-linux-init/scripts/rc.init \
+ image/etc/s6-linux-init/scripts/rc.shutdown \
+ image/etc/s6-linux-init/scripts/rc.shutdown.final \
+ image/etc/wireplumber/wireplumber.conf.d/99_spectrum.conf \
+ image/etc/xdg/xdg-desktop-portal/portals.conf
+
+LINKS = \
+ image/bin \
+ image/etc/ssl/certs/ca-certificates.crt \
+ image/sbin
+
+S6_RC_FILES = \
+ image/etc/s6-rc/app/dependencies.d/dbus \
+ image/etc/s6-rc/app/dependencies.d/pipewire \
+ image/etc/s6-rc/app/dependencies.d/wayland-proxy-virtwl \
+ image/etc/s6-rc/app/run \
+ image/etc/s6-rc/app/type \
+ image/etc/s6-rc/dbus/notification-fd \
+ image/etc/s6-rc/dbus/run \
+ image/etc/s6-rc/dbus/type \
+ image/etc/s6-rc/mdevd-coldplug/dependencies.d/mdevd \
+ image/etc/s6-rc/mdevd-coldplug/type \
+ image/etc/s6-rc/mdevd-coldplug/up \
+ image/etc/s6-rc/mdevd/notification-fd \
+ image/etc/s6-rc/mdevd/run \
+ image/etc/s6-rc/mdevd/type \
+ image/etc/s6-rc/ok-all/contents.d/app \
+ image/etc/s6-rc/ok-all/contents.d/mdevd-coldplug \
+ image/etc/s6-rc/ok-all/contents.d/wireplumber \
+ image/etc/s6-rc/ok-all/type \
+ image/etc/s6-rc/pipewire/notification-fd \
+ image/etc/s6-rc/pipewire/run \
+ image/etc/s6-rc/pipewire/type \
+ image/etc/s6-rc/wayland-proxy-virtwl/notification-fd \
+ image/etc/s6-rc/wayland-proxy-virtwl/run \
+ image/etc/s6-rc/wayland-proxy-virtwl/type \
+ image/etc/s6-rc/wireplumber/dependencies.d/dbus \
+ image/etc/s6-rc/wireplumber/dependencies.d/pipewire \
+ image/etc/s6-rc/wireplumber/run \
+ image/etc/s6-rc/wireplumber/type
diff --git a/scripts/genfiles.awk b/scripts/genfiles.awk
new file mode 100644
index 0000000000000000000000000000000000000000..2d32016b5e6d6773685b71cc04e49cf24e842ba9
--- /dev/null
+++ b/scripts/genfiles.awk
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+BEGIN {
+ RS = "\n";
+ FS = "\t";
+ modes["120000"] = "symlink";
+ modes["100644"] = "regular";
+ modes["100755"] = "regular";
+}
+
+function fail(msg) {
+ # Awk will run END blocks even after exit.
+ # The END block checks this variable and
+ # immediately exits if it is nonzero.
+ exit_code = 1;
+ print msg > "/dev/stderr";
+ exit 1;
+}
+
+# Extract data from built-in variables.
+{
+ filename = $2;
+ raw_mode = $1;
+ # Awk autocreates empty string entries if the key is invalid,
+ # but the code exits in this case so that is okay.
+ mode = modes[raw_mode];
+}
+
+filename !~ /^[[:alnum:]_./-]+$/ {
+ fail("filename '" filename "' has forbidden characters");
+}
+
+# Skip license files
+filename ~ /\.license$/ { next }
+
+filename ~ /^image\/etc\/s6-rc\// {
+ if (mode != "regular") {
+ fail("s6-rc-compile input '" filename "' isn't a regular file");
+ }
+ rc_files[rc_count++] = filename;
+ next;
+}
+
+mode == "symlink" {
+ symlinks[symlink_count++] = filename;
+ next;
+}
+
+mode == "regular" {
+ files[file_count++] = filename;
+ next;
+}
+
+{ fail("File '" filename "' is not regular file or symlink (mode " raw_mode ")"); }
+
+END {
+ if (exit_code) {
+ exit exit_code;
+ }
+ printf ("# SPDX-" \
+"License-Identifier: CC0-1.0\n" \
+"# SPDX-" \
+"FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>\n" \
+"\n" \
+"FILES =");
+ for (array_index = 0; array_index < file_count; array_index += 1) {
+ printf " \\\n\t%s", files[array_index];
+ }
+ # GNU Make uses the modification time of the *target* of a symlink,
+ # rather than the modification time of the symlink itself. It can be told
+ # to *also* use the symlink's modification time, but not to *only* use
+ # the symlink's modification time. However, these symlinks will generally
+ # be broken, so make will not be able to dereference the symlink.
+ # Therefore, using these symlinks as make dependencies won't work.
+ printf ("\n\n" \
+"LINKS =");
+ for (array_index = 0; array_index < symlink_count; array_index += 1) {
+ printf " \\\n\t%s", symlinks[array_index];
+ }
+ printf "\n\nS6_RC_FILES =";
+ for (array_index = 0; array_index < rc_count; array_index += 1) {
+ printf " \\\n\t%s", rc_files[array_index];
+ }
+ print "";
+}
diff --git a/scripts/genfiles.sh b/scripts/genfiles.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c93d2e7d5982adba9cd5e1d720d6d378174ef171
--- /dev/null
+++ b/scripts/genfiles.sh
@@ -0,0 +1,27 @@
+#!/bin/sh --
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+set -euo pipefail
+export LC_ALL=C LANGUAGE=C
+dir=$(git rev-parse --show-toplevel)
+cd -- "$dir"
+for i in host/rootfs img/app vm/sys/net; do
+ output_file=$i/file-list.mk
+ {
+ git -C "$i" -c core.quotePath=true ls-files $'--format=%(objectmode)\t%(path)' -- image |
+ sort -t $'\t' -k 2
+ } |
+ awk -f scripts/genfiles.awk > "$output_file.tmp"
+ if [ -f "$output_file" ]; then
+ # Avoid changing output file if it is up to date, as that
+ # would cause unnecessary rebuilds.
+ if cmp -s -- "$output_file.tmp" "$output_file"; then
+ rm -- "$output_file.tmp"
+ continue
+ else
+ astatus=$?
+ if [ "$astatus" != 1 ]; then exit "$astatus"; fi
+ fi
+ fi
+ mv -- "$output_file.tmp" "$output_file"
+done
diff --git a/vm/sys/net/Makefile b/vm/sys/net/Makefile
index 8a59faca2451ca3d404a62d76def15dec67add51..b377e12bba8f062026e997de18e19c9af8e07cb5 100644
--- a/vm/sys/net/Makefile
+++ b/vm/sys/net/Makefile
@@ -4,6 +4,7 @@
.POSIX:

include ../../../lib/common.mk
+include file-list.mk

prefix = build/host
libdir = $(prefix)/lib
@@ -28,62 +29,26 @@ $(vmdir)/netvm/blk/root.img: ../../../scripts/make-gpt.sh ../../../scripts/sfdis
build/rootfs.erofs:root:ea21da27-0391-48da-9235-9d2ab2ca7844:root
mv $@.tmp $@

-VM_FILES = \
- image/etc/dbus-1/system.conf \
- image/etc/fstab \
- image/etc/init \
- image/etc/mdev.conf \
- image/etc/mdev/iface \
- image/etc/nftables.conf \
- image/etc/passwd \
- image/etc/s6-linux-init/run-image/service/getty-hvc0/run \
- image/etc/s6-linux-init/scripts/rc.init \
- image/etc/sysctl.conf
-VM_DIRS = dev etc/s6-linux-init/env proc run sys var/lib/connman
-
-# These are separate because they need to be included, but putting
-# them as make dependencies would confuse make.
-VM_LINKS = image/bin image/lib image/sbin image/var/run
-
-VM_BUILD_FILES = build/etc/s6-rc
+DIRS = dev etc/s6-linux-init/env proc run sys var/lib/connman
+
+BUILD_FILES = build/etc/s6-rc

build/empty:
mkdir -p $@

-build/rootfs.erofs: ../../../scripts/make-erofs.sh $(PACKAGES_FILE) $(VM_FILES) $(VM_BUILD_FILES) build/empty
+build/rootfs.erofs: ../../../scripts/make-erofs.sh $(PACKAGES_FILE) $(FILES) $(BUILD_FILES) build/empty file-list.mk
{ \
cat $(PACKAGES_FILE) ;\
- for file in $(VM_FILES) $(VM_LINKS); do printf '%s\n%s\n' $$file "$${file#image/}"; done ;\
- for file in $(VM_BUILD_FILES); do printf '%s\n%s\n' $$file $${file#build/}; done ;\
- printf 'build/empty\n%s\n' $(VM_DIRS) ;\
+ for file in $(FILES) $(LINKS); do printf '%s\n%s\n' $$file "$${file#image/}"; done ;\
+ for file in $(BUILD_FILES); do printf '%s\n%s\n' $$file $${file#build/}; done ;\
+ printf 'build/empty\n%s\n' $(DIRS) ;\
} | ../../../scripts/make-erofs.sh $@

-VM_S6_RC_FILES = \
- image/etc/s6-rc/connman/dependencies.d/dbus \
- image/etc/s6-rc/connman/run \
- image/etc/s6-rc/connman/type \
- image/etc/s6-rc/dbus/notification-fd \
- image/etc/s6-rc/dbus/run \
- image/etc/s6-rc/dbus/type \
- image/etc/s6-rc/mdevd-coldplug/dependencies.d/mdevd \
- image/etc/s6-rc/mdevd-coldplug/type \
- image/etc/s6-rc/mdevd-coldplug/up \
- image/etc/s6-rc/mdevd/notification-fd \
- image/etc/s6-rc/mdevd/run \
- image/etc/s6-rc/mdevd/type \
- image/etc/s6-rc/nftables/type \
- image/etc/s6-rc/nftables/up \
- image/etc/s6-rc/ok-all/contents.d/mdevd-coldplug \
- image/etc/s6-rc/ok-all/contents.d/sysctl \
- image/etc/s6-rc/ok-all/type \
- image/etc/s6-rc/sysctl/type \
- image/etc/s6-rc/sysctl/up
-
-build/etc/s6-rc: $(VM_S6_RC_FILES)
+build/etc/s6-rc: $(S6_RC_FILES) file-list.mk
mkdir -p $$(dirname $@)
rm -rf $@
set -uo pipefail && dir=$$(mktemp -d) && \
- { tar -c $(VM_S6_RC_FILES) | tar -C $$dir -x --strip-components 3; } && \
+ { tar -c $(S6_RC_FILES) | tar -C $$dir -x --strip-components 3; } && \
s6-rc-compile $@ $$dir; \
exit=$$?; rm -r $$dir; exit $$exit

diff --git a/vm/sys/net/file-list.mk b/vm/sys/net/file-list.mk
new file mode 100644
index 0000000000000000000000000000000000000000..a6f1a41c9a30b769a508f6c486cced0c8daaa833
--- /dev/null
+++ b/vm/sys/net/file-list.mk
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+FILES = \
+ image/etc/dbus-1/system.conf \
+ image/etc/fstab \
+ image/etc/init \
+ image/etc/mdev.conf \
+ image/etc/mdev/iface \
+ image/etc/nftables.conf \
+ image/etc/passwd \
+ image/etc/s6-linux-init/run-image/service/getty-hvc0/run \
+ image/etc/s6-linux-init/scripts/rc.init \
+ image/etc/sysctl.conf
+
+LINKS = \
+ image/bin \
+ image/lib \
+ image/sbin \
+ image/var/run
+
+S6_RC_FILES = \
+ image/etc/s6-rc/connman/dependencies.d/dbus \
+ image/etc/s6-rc/connman/run \
+ image/etc/s6-rc/connman/type \
+ image/etc/s6-rc/dbus/notification-fd \
+ image/etc/s6-rc/dbus/run \
+ image/etc/s6-rc/dbus/type \
+ image/etc/s6-rc/mdevd-coldplug/dependencies.d/mdevd \
+ image/etc/s6-rc/mdevd-coldplug/type \
+ image/etc/s6-rc/mdevd-coldplug/up \
+ image/etc/s6-rc/mdevd/notification-fd \
+ image/etc/s6-rc/mdevd/run \
+ image/etc/s6-rc/mdevd/type \
+ image/etc/s6-rc/nftables/type \
+ image/etc/s6-rc/nftables/up \
+ image/etc/s6-rc/ok-all/contents.d/mdevd-coldplug \
+ image/etc/s6-rc/ok-all/contents.d/sysctl \
+ image/etc/s6-rc/ok-all/type \
+ image/etc/s6-rc/sysctl/type \
+ image/etc/s6-rc/sysctl/up

---
base-commit: cce9827490a788cd65273fde8cff5597fb200287
change-id: 20250831-genfiles-46787fc10869
--
Sincerely,
Demi Marie Obenour (she/her/hers)