Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-09-16 14:12:30 +03:00
parent 249b59a026
commit 53a1c56847

184
flake.nix
View File

@@ -11,112 +11,104 @@
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:
pkgs.stdenvNoCC.mkDerivation {
name = "myApp.run";
nativeBuildInputs = [
pkgs.nix
pkgs.coreutils
pkgs.findutils
pkgs.gnutar
pkgs.gzip
pkgs.patchelf
bundlers.proot-bundler =
drv:
let
ci = pkgs.closureInfo {
rootPaths = [
drv
prootPatched
];
# patch proot as you described
prootPatched = (pkgs.proot.override { enablePython = false; }).overrideAttrs (_: {
postFixup = ''
exe=$out/bin/proot
${pkgs.patchelf}/bin/patchelf \
--set-interpreter ."$(${pkgs.patchelf}/bin/patchelf --print-interpreter $exe)" \
--set-rpath "$(${pkgs.patchelf}/bin/patchelf --print-rpath $exe | sed 's|/nix/store/|./nix/store/|g')" \
$exe
'';
});
buildCommand = ''
set -euo pipefail
# 1) Make payload dir that mirrors the bundle runtime layout
PAY="$PWD/payload"
mkdir -p "$PAY/nix/store"
# include app + patched proot in the payload closure
app=${drv}
proot=${
self.bundlers.${system}.selfExtracting.prootPatched or ""
} # not callable; we want the attr above
proot=${
pkgs.proot.overrideAttrs (_: {
postFixup = "${pkgs.patchelf}/bin/patchelf --set-interpreter .\"$(${pkgs.patchelf}/bin/patchelf --print-interpreter $out/bin/proot)\" --set-rpath \"$( ${pkgs.patchelf}/bin/patchelf --print-rpath $out/bin/proot | sed 's|/nix/store/|./nix/store/|g')\" $out/bin/proot";
})
}
# copy closure of app + proot into ./nix/store
paths=$( ${pkgs.nix}/bin/nix-store --query --requisites "$app" "$proot" )
for p in $paths; do
cp -a --no-preserve=ownership "$p" "$PAY/nix/store/"
done
# discover targets for the launcher
APP_BIN=$(find "$app/bin" -maxdepth 1 -type f -perm -111 | head -n1)
APP_REL="/nix/store/$(basename "$(dirname "$APP_BIN")")/$(basename "$APP_BIN")"
PROOT_REL="/nix/store/$(basename "$proot")/bin/proot"
# 2) Tar.gz the payload
( cd "$PAY" && tar -czf "$PWD/payload.tar.gz" . )
# 3) Assemble a self-extracting script at $out
cat > $out <<'SH'
#!/bin/sh
set -euf
# extract to temp dir
: "''${TMPDIR:=/tmp}"
EXTRACT_DIR="$(mktemp -d "''${TMPDIR%/}/nxbdl.XXXXXX")"
cleanup() { [ -n "''${KEEP_BUNDLE:-}" ] || rm -rf "$EXTRACT_DIR"; }
trap cleanup EXIT INT TERM
# locate embedded archive (line after marker)
ARCHIVE_LINE=$(awk '/^__ARCHIVE_BELOW__/ {print NR+1; exit 0}' "$0")
tail -n +$ARCHIVE_LINE "$0" | tar -xzf - -C "$EXTRACT_DIR"
cd "$EXTRACT_DIR"
# vars substituted by bundler at build time:
APP_REL='__APP_REL__'
PROOT_REL='__PROOT_REL__'
# run via proot
BUNDLE_PWD="''${BUNDLE_PWD:-$PWD}"
exec ".$PROOT_REL" \
-b ./nix:nix \
-R / \
-w "$BUNDLE_PWD" \
".$APP_REL" "$@"
__ARCHIVE_BELOW__
SH
# substitute the program + proot paths into the stub
sed -i \
-e "s|__APP_REL__|$APP_REL|g" \
-e "s|__PROOT_REL__|$PROOT_REL|g" \
$out
chmod +x $out
# 4) Append the payload bytes after the marker
cat payload.tar.gz >> $out
'';
};
# resolve these now so we can hardcode into the runner
appBinGuess = "${drv}/bin";
prootBin = "${prootPatched}/bin/proot";
PROOT_REL = "/nix/store/${builtins.baseNameOf (builtins.dirOf prootBin)}/bin/proot";
in
pkgs.stdenvNoCC.mkDerivation {
name = "myApp.run";
nativeBuildInputs = [
pkgs.coreutils
pkgs.findutils
pkgs.gnutar
pkgs.gzip
];
buildCommand = ''
set -euo pipefail
PAY="$PWD/payload"
mkdir -p "$PAY/nix/store"
default = proot-bundler;
};
# copy closure paths from closureInfo (no nix calls here)
while IFS= read -r p; do
cp -a --no-preserve=ownership "$p" "$PAY/nix/store/"
done < ${ci}/store-paths
# pick an app binary
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")"
( cd "$PAY" && tar -czf "$PWD/payload.tar.gz" . )
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
sed -i \
-e "s|__APP_REL__|$APP_REL|g" \
-e "s|__PROOT_REL__|${PROOT_REL}|g" \
$out
chmod +x $out
cat payload.tar.gz >> $out
'';
};
default = self.bundlers.${system}.proot-bundler;
}
);
}