Skip to content

Instantly share code, notes, and snippets.

@Hogeyama
Last active February 19, 2023 08:26
Show Gist options
  • Select an option

  • Save Hogeyama/21312b3f3144083555519de938b3ccc4 to your computer and use it in GitHub Desktop.

Select an option

Save Hogeyama/21312b3f3144083555519de938b3ccc4 to your computer and use it in GitHub Desktop.
Poor man's standalone executable
# usage:
# import ./bundle.nix {
# inherit pkgs;
# name = "foo";
# target = "${pkgs.foo}/bin/foo";
# }
{ name
, target
, tempDir ? "/tmp"
, pkgs
}:
pkgs.runCommandCC name { buildInputs = [ pkgs.patchelf pkgs.zip ]; }
''
hash=${builtins.head (builtins.match "/nix/store/([a-zA-Z0-9]*)-.*" target)}
extract_base=$hash-${name}
mkdir -p $out/$extract_base
pushd $out
pushd $extract_base
# copy & patchelf executable
cp ${target} ${name}
# copy & patchelf dynamic libraries
mapfile -t LIBS < <(ldd ${name} | grep -F '=> /' | awk '{print $3}')
for so in "''${LIBS[@]}"; do
cp "$so" .
so=$(basename "$so")
if [[ "$so" =~ ld-.* ]]; then
interpreter="$so"
continue
fi
chmod +w "$so"
patchelf --set-rpath '$ORIGIN' --force-rpath "$so"
chmod -w "$so"
done
# patchelf
chmod +w ${name}
interpreter=${tempDir}/$extract_base/$interpreter
patchelf --set-interpreter $interpreter --set-rpath '$ORIGIN' --force-rpath ${name}
chmod -w ${name}
popd # $extract_base
# zip
zip -qr bundle.zip .
# create single script that self-extracts and runs
# TODO is this posix-compliant?
bundled=${name}-bundled
cat - bundle.zip > $bundled <<EOF
#!/usr/bin/env bash
set -eu
TEMP="\$(mktemp -d)"
trap 'rm -rf \$TEMP' EXIT
N=\$(grep -an "^#START_OF_ZIP#" "\$0" | cut -d: -f1)
tail -n +"\$((N + 1))" <"\$0" > "\$TEMP/self.zip"
[[ -d ${tempDir}/$extract_base ]] || unzip -nqqd ${tempDir} "\$TEMP/self.zip" >/dev/null
${tempDir}/$extract_base/${name} "\$@"
exit 0
#START_OF_ZIP#
EOF
chmod +x $bundled
# cleanup
find . ! -name $bundled -type f -exec rm -f {} +
mv $bundled ${name}
popd # $out
''
# * usage of this expression:
# import ./bundle.nix {
# inherit pkgs;
# name = "foo";
# target = "${pkgs.foo}/bin/foo";
# }
# * usage of the generated file:
# * `result/$name --extract /path/to/dir` extracts the target
# and it's dependencies to `/path/to/dir`. It also creates a wrapper
# scripts `/path/to/dir/bin/$name` that runs the target.
# * `result/$name [--] ARGS` extract the bundle to temporary directory
# and runs the target with `ARGS`.
{ name
, target
, pkgs
}:
pkgs.runCommandCC name { buildInputs = [ pkgs.patchelf pkgs.zip ]; }
''
mkdir -p $out
pushd $out
mkdir orig lib
# copy & patchelf executable
cp ${target} orig/${name}
# copy & patchelf dynamic libraries
pushd lib
mapfile -t LIBS < <(ldd ../orig/${name} | grep -F '=> /' | awk '{print $3}')
for so in "''${LIBS[@]}"; do
cp "$so" .
so=$(basename "$so")
if [[ "$so" =~ ld-.* ]]; then
interpreter="$so"
continue
fi
chmod +w $so
patchelf --set-rpath '$ORIGIN' --force-rpath $so
chmod -w $so
done
popd #lib
# patchelf
chmod +w orig/${name}
patchelf --set-rpath '$ORIGIN/../lib' --force-rpath orig/${name}
chmod -w orig/${name}
# zip
zip -qr bundle.zip .
# create single script that self-extracts and runs
# TODO is this posix-compliant?
bundled=${name}-bundled
cat - bundle.zip > $bundled <<EOF
#!/usr/bin/env bash
set -eu
TEMP="\$(mktemp -d /tmp/${name}.XXXXXX)"
trap 'rm -rf \$TEMP' EXIT
N=\$(grep -an "^#START_OF_ZIP#" "\$0" | cut -d: -f1)
tail -n +"\$((N + 1))" <"\$0" > "\$TEMP/self.zip"
if [[ "\''${1:-}" == "--extract" ]]; then
if [[ -z "\''${2:-}" ]]; then
echo "Usage: \$0 --extract <path>"
exit 1
fi
if [[ -e "\$2" ]]; then
echo "Error: \$2 already exists"
exit 1
fi
TARGET=\$(realpath "\$2")
unzip -qd "\$TARGET" "\$TEMP/self.zip"
mkdir -p "\$TARGET/bin"
cat - >"\$TARGET/bin/${name}" <<EOF2
#!/usr/bin/env bash
exec "\$TARGET/lib/$interpreter" "\$TARGET/orig/${name}" "\\\$@"
EOF2
chmod +x "\$TARGET/bin/${name}"
echo "successfully extracted to \$2"
else
if [[ "\''${1:-}" == "--" ]]; then
shift
fi
unzip -qqd "\$TEMP" "\$TEMP/self.zip" >/dev/null
"\$TEMP/lib/$interpreter" "\$TEMP/orig/${name}" "\$@"
fi
exit 0
#START_OF_ZIP#
EOF
chmod +x $bundled
# cleanup
find . ! -name $bundled -type f -exec rm -f {} +
mv $bundled ${name}
popd # $out
''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment