{ description = "My flake"; inputs = { nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem ( system: let pkgs = import nixpkgs { inherit system; }; # patched proot (relative interp + rpath) prootPatched = (pkgs.proot.override { enablePython = false; }).overrideAttrs (prev: { postFixup = (prev.postFixup or "") + '' exe=$out/bin/proot if interp="$(${pkgs.patchelf}/bin/patchelf --print-interpreter "$exe" 2>/dev/null)"; then ${pkgs.patchelf}/bin/patchelf --set-interpreter ."$interp" "$exe" fi if rpath="$(${pkgs.patchelf}/bin/patchelf --print-rpath "$exe" 2>/dev/null)"; then ${pkgs.patchelf}/bin/patchelf \ --set-rpath "$(printf %s "$rpath" | sed 's|/nix/store/|./nix/store/|g')" "$exe" fi ''; }); in { bundlers = rec { proot-bundler = drv: let ci = pkgs.closureInfo { rootPaths = [ drv prootPatched ]; }; appBinGuess = "${drv}/bin"; prootStoreBase = builtins.baseNameOf prootPatched; PROOT_REL = "/nix/store/${prootStoreBase}/bin/proot"; in pkgs.stdenvNoCC.mkDerivation { name = "myApp.run"; nativeBuildInputs = [ pkgs.coreutils pkgs.findutils pkgs.gnutar pkgs.gzip ]; dontFixup = true; buildCommand = '' set -euo pipefail PAY="$PWD/payload" mkdir -p "$PAY/nix/store" # 1) copy closure (no nix calls; uses closureInfo) while IFS= read -r p; do cp -a --no-preserve=ownership "$p" "$PAY/nix/store/" done < ${ci}/store-paths # 2) pick app binary and make relative path used by the runner APP_BIN=$(find ${appBinGuess} -maxdepth 1 -type f -perm -111 | head -n1) if [ -z "''${APP_BIN:-}" ]; then echo "no executable found in ${appBinGuess}" >&2 exit 1 fi APP_REL="/nix/store/$(basename "$(dirname "$APP_BIN")")/$(basename "$APP_BIN")" # 3) tar the payload OUTSIDE $PAY to avoid self-inclusion _TMP="$(mktemp -d)" ( cd "$PAY" && tar \ --sort=name \ --owner=0 --group=0 --numeric-owner \ -czf "$_TMP/payload.tar.gz" . ) # 4) write the self-extracting stub cat > "$out" <<'SH' #!/bin/sh set -euf : "''${TMPDIR:=/tmp}" EXTRACT_DIR="$(mktemp -d "''${TMPDIR%/}/nxbdl.XXXXXX")" cleanup() { [ -n "''${KEEP_BUNDLE:-}" ] || rm -rf "$EXTRACT_DIR"; } trap cleanup EXIT INT TERM ARCHIVE_LINE=$(awk '/^__ARCHIVE_BELOW__/ {print NR+1; exit 0}' "$0") tail -n +"$ARCHIVE_LINE" "$0" | tar -xzf - -C "$EXTRACT_DIR" cd "$EXTRACT_DIR" APP_REL='__APP_REL__' PROOT_REL='__PROOT_REL__' BUNDLE_PWD="''${BUNDLE_PWD:-$PWD}" exec ".${PROOT_REL}" \ -b ./nix:nix \ -R / \ -w "$BUNDLE_PWD" \ ".$APP_REL" "$@" __ARCHIVE_BELOW__ SH # 5) inject paths, chmod, and append payload sed -i \ -e "s|__APP_REL__|$APP_REL|g" \ -e "s|__PROOT_REL__|${PROOT_REL}|g" \ "$out" chmod +x "$out" cat "$_TMP/payload.tar.gz" >> "$out" ''; }; default = proot-bundler; }; } ); }