---
name: genmkfile
description: "Use when building Kicksecure/derivative-maker Debian packages with genmkfile (targets like 'genmkfile deb-pkg', cowbuilder chroot builds, the output/dist folder as a .deb cache, dist/debdist/debdsc tarballs, deb-install, lintian, debsign). Covers the build sequence, env-var knobs, cowbuilder base creation, and override hooks."
license: MIT
---

# genmkfile: building derivative-maker / Kicksecure Debian packages

- `genmkfile`: generic Makefile generator for Kicksecure / Whonix / derivative-maker packages.
- Replaces hand-written `debian/*.install` and `make install` targets.
- Files laid out as `usr/...`, `etc/...` in the package root install to `/usr/...`, `/etc/...`.
- Engine: big bash script `/usr/share/genmkfile/make-helper-one.bsh`.
- Thin `/usr/bin/genmkfile` wrapper locates `GENMKFILE_PATH` (`./packages/kicksecure/genmkfile/usr/share/genmkfile` in a dm checkout, else `/usr/share/genmkfile`).
- Run it from a package's root (the dir containing `debian/`).

## Build sequence (cowbuilder)

- `deb-pkg` with cowbuilder needs the source tarballs built FIRST, in order:

```
genmkfile dist       # -> upstream tarball  (excludes .git and ./debian)
genmkfile debdist     # -> debian tarball
genmkfile debdsc      # -> .dsc
genmkfile deb-pkg     # -> builds the .deb(s)
```

- `genmkfile distclean` removes generated tarballs/build cruft.
- Plain `genmkfile deb-pkg` with no cowbuilder uses `debuild` in-place (needs build-deps installed; `sudo genmkfile deb-all-dep` installs them).

## cowbuilder mode (chroot build)

- **In a derivative-maker checkout, do NOT hand-roll the cowbuilder invocation.**
- Use dm's own build steps: they set up the base, the approx mirror, the pbuilder config, and the TMPDIR removal canonically (and call the dm-submodule genmkfile, not the installed one):

```
make_cross_build_platform_list=amd64 \
  ./build-steps.d/*_cowbuilder-setup       --allow-untagged true --allow-uncommitted true --flavor source --target root
make_cross_build_platform_list=amd64 \
  ./build-steps.d/*_create-debian-packages --allow-untagged true --allow-uncommitted true --flavor source --target root
```

- dm handles libpam-tmpdir/pbuilder breakage via `help-steps/variables` (`chroot_env_vars_var_remove_list="TEMP TEMPDIR TMP TMPDIR"`).
- dm sets the mirror via the approx apt-cacher on `127.0.0.1:9977` (`APPROX_PROXY_ENABLE=yes`).
- dm auto-generates the pbuilder config to `~/derivative-binary/pbuilder.conf`.
- dm's `2100` step only iterates dm's own package set + does `reprepro-add`.
- Raw knobs below are for understanding / building a standalone package OUTSIDE a dm checkout.

### Building standalone packages WITH dm's cowbuilder env (verified recipe)

- For packages NOT in dm's package set (so dm's `2100` won't iterate them) but that should still build with dm's machinery: reuse dm's base + config instead of hand-rolling.
- Run dm's setup ONCE, then build each package via genmkfile pointed at the dm pbuilder config:

```
# once: dm build machine + base (1200 brings up the approx 9977 mirror; 1300
# creates the base whose in-chroot `mkdir $TMPDIR` fixes the libpam-tmpdir break)
cd ~/derivative-maker
./build-steps.d/1200_prepare-build-machine       --allow-untagged true --allow-uncommitted true --flavor source --target root
make_cross_build_platform_list=amd64 ./build-steps.d/1300_cowbuilder-setup --allow-untagged true --allow-uncommitted true --flavor source --target root

# per package (in its root): the dm config is the ONLY extra knob -- no
# COWBUILDER_PREFIX, no TMPDIR=/tmp override
export make_use_cowbuilder=true make_use_lintian=false
export make_cowbuilder_dist_folder=<dist-folder>   # where built .debs land
export dist_build_pbuilder_config_file=~/derivative-binary/pbuilder.conf
genmkfile dist && genmkfile debdist && genmkfile debdsc && genmkfile deb-pkg
```

Gotchas learned:
- dm's `1300` fails standalone WITHOUT `1200` first (debootstrap).
- The TMPDIR break is fixed by dm's base (in-chroot `mkdir $TMPDIR`), NOT by passing the config alone -- a config-only build still fails `dpkg-deb: failed to make temporary file`.
- If a package's source tree has an empty dir, `genmkfile dist` errors "Empty directory found!"; fix with an executable `./make-helper-overrides.bsh` defining `make_dist_hook_pre` to drop a placeholder -- don't move the package off genmkfile.
- A small wrapper that loops over the package dirs keeps a multi-package build DRY.

### Raw genmkfile cowbuilder knobs (standalone, outside dm)

- Enable with env vars (the README knobs):

```
make_use_cowbuilder=true \
make_cowbuilder_dist_folder=<dist-folder> \
dist_build_pbuilder_config_file=~/derivative-binary/pbuilder.conf \
make_cross_build_platform_list=amd64 \
genmkfile deb-pkg
```

- `make_use_cowbuilder=true` -- build inside a cowbuilder chroot.
- `make_cowbuilder_dist_folder=<dir>` -- REQUIRED with cowbuilder; the **output / dist folder** where the built `.deb` / `.changes` / `.dsc` land (`--buildresult`). This folder is the natural **package cache**. Must already exist.
- `make_cross_build_platform_list='amd64 arm64'` -- one build per arch.
- `make_cowbuilder_distribution` -- defaults to `$dist_build_apt_stable_release` or `lsb_release -sc` (e.g. `trixie`).
- `make_cowbuilder_mirror` -- apt mirror for the chroot; derivative-maker sets it to its apt-cacher (`http://127.0.0.1:9977/debian`); falls back to `https://deb.debian.org/debian`. (In dm, `buildconfig.d` exports it.)
- `dist_build_pbuilder_config_file` -- `--configfile` for cowbuilder/pbuilder.
- `make_use_lintian=true|false`, `make_use_debsign=true` + `make_debsign_opts` (debsign with `sq verify` against `$DEBEMAIL`).
- genmkfile passes to cowbuilder: `--build <dsc> --basepath /var/cache/pbuilder/base.cow_<arch> --buildplace /var/cache/pbuilder/cow.cow_<arch> --buildresult <dist_folder> --debbuildopts=-sa`. **It does NOT create the base** -- create it once first.

### Create the cowbuilder base (once per arch)

- cowbuilder `--build` needs `--basepath` to already exist. Create it:

```
sudo cowbuilder --create \
  --basepath /var/cache/pbuilder/base.cow_amd64 \
  --distribution trixie \
  --architecture amd64 \
  --mirror https://deb.debian.org/debian \
  [--configfile ~/derivative-binary/pbuilder.conf]
```

- Update it later with `--update --basepath ...`.
- Build-deps (e.g. `debhelper`) must be reachable from `--mirror`; the package's *runtime* deps are NOT needed at build time.
- So `Architecture: all` packages whose only build-dep is debhelper build against a plain `deb.debian.org` base even if runtime deps (signal-cli, etc.) live elsewhere.
- Caveat: `libpam-tmpdir` breaks cowbuilder (sets TMPDIR) -- keep it out of the build host.

## Other useful targets

- `install` / `uninstall` (to a live system or `DESTDIR`).
- `installsim` / `uninstallcheck` (dry runs).
- `deb-install` (build-deps via dpkg).
- `deb-icup` (install just-built current package).
- `deb-remove` / `deb-purge`.
- `lintian`.
- `deb-chl-bumpup-*` (changelog bump).
- `git-tag-sign/-verify`.
- List all: `genmkfile help`.

## Extending: overrides (no forking the engine)

- Drop an executable `./make-helper-overrides.bsh` or files in `./make-helper-overrides.d/` to add pre/post hooks or replace any target.
- Non-executable override files are skipped.
- Prefer this over editing the engine.

## Gotchas

- Run from the package root; tarballs/build outputs are written to `../` (`dpkg-source` can't target another dir), but cowbuilder results go to `--buildresult` (the dist folder).
- `deb-pkg` (cowbuilder) hard-fails if `dist`/`debdist`/`debdsc` weren't run -- the error tells you which tarball is missing.
- The dm apt-cacher (`127.0.0.1:9977`) is only up during a dm build session; when it's down, set `make_cowbuilder_mirror=https://deb.debian.org/debian`.

### cowbuilder `--execute`: passing variables into a chroot script

- `cowbuilder --execute -- <script>` runs `<script>` inside the chroot with the *caller's* environment, as far as sudo lets it through.
- derivative-maker's `$SUDO_TO_ROOT` already appends `--preserve-env=$env_vars_keep_list` (`help-steps/variables`), and `cowbuilder` forwards that environment into the `--execute` script (verified: a `--preserve-env`'d, exported variable is visible inside the chroot).
- `--preserve-env=<list>` only carries **exported** variables. A set-but-unexported shell variable named in the list is silently dropped.
- So to hand a chroot script its inputs, `export` them and rely on `--preserve-env` -- no need to serialize them to a file and `source` it (which would be runtime-generated code).
- Several `env_vars_keep_list` entries are set but not exported, which is exactly why the old vbox chroot scripts ferried them through a generated `declare -p` env-file; exporting them is the fix.

### cowdancer COW + the hardlink-bypass trap

- The buildplace is a fresh per-invocation `cp -al <base>` into `cow.<PID>` (hardlinks into the base), torn down afterwards unless `--save-after-login`.
- cowdancer (`libcowdancer` via `LD_PRELOAD` + `COWDANCER_ILISTFILE`) breaks a file's hardlink before a write so the base is preserved.
- But `sudo`'s `env_reset` strips both, so anything run via `sudo` inside the chroot runs **without** cowdancer -- its writes/unlinks hit the hardlinked-into-base inode directly and **corrupt the shared base**.
- Harmless for a one-shot `--execute` (the buildplace is discarded), but do NOT reuse a base across runs if a non-cowdancer step mutated files in it (e.g. `VBoxManage unregistervm --delete` deleting a VM whose disk/registry are hardlinked into the base).
- This is also why a root command run via `sudo` inside the chroot can fail with `cp: ... Cannot allocate memory`: `libcowdancer` stays `LD_PRELOAD`'ed while `COWDANCER_ILISTFILE` is gone, leaving the COW layer half-initialized -- run such commands directly (without sudo) so the inherited cowdancer env stays intact.
