diff options
-rw-r--r-- | pkgs/games/build-support/build-game.nix | 42 | ||||
-rw-r--r-- | pkgs/games/build-support/default.nix | 1 | ||||
-rw-r--r-- | pkgs/games/build-support/setup-hooks/auto-patchelf.sh | 135 |
3 files changed, 178 insertions, 0 deletions
diff --git a/pkgs/games/build-support/build-game.nix b/pkgs/games/build-support/build-game.nix new file mode 100644 index 00000000..2ada5eb7 --- /dev/null +++ b/pkgs/games/build-support/build-game.nix @@ -0,0 +1,42 @@ +{ stdenv, lib, file }: + +{ buildInputs ? [] +, nativeBuildInputs ? [] +, installCheckPhase ? "" +, ... +}@attrs: + +stdenv.mkDerivation ({ + buildInputs = [ stdenv.cc.cc ] ++ buildInputs; + + nativeBuildInputs = [ + file ./setup-hooks/auto-patchelf.sh + ] ++ nativeBuildInputs; + + doInstallCheck = true; + + installCheckPhase = '' + runHook preInstallCheck + + echo "checking dependencies for libraries and executables" >&2 + + local errors="$( + IFS=$'\n' + for elf in $(findElfs "$prefix"); do checkElfDep "$elf"; done + )" + + if [ -n "$errors" ]; then + echo "$errors" >&2 + exit 1 + fi + + ${installCheckPhase} + + runHook postInstallCheck + ''; + + dontStrip = true; + dontPatchELF = true; +} // removeAttrs attrs [ + "buildInputs" "nativeBuildInputs" "installCheckPhase" +]) diff --git a/pkgs/games/build-support/default.nix b/pkgs/games/build-support/default.nix index 8b6b466d..23e03c3b 100644 --- a/pkgs/games/build-support/default.nix +++ b/pkgs/games/build-support/default.nix @@ -1,5 +1,6 @@ { callPackage, ... }: { + buildGame = callPackage ./build-game.nix {}; buildUnity = callPackage ./build-unity.nix {}; } diff --git a/pkgs/games/build-support/setup-hooks/auto-patchelf.sh b/pkgs/games/build-support/setup-hooks/auto-patchelf.sh new file mode 100644 index 00000000..b0982a45 --- /dev/null +++ b/pkgs/games/build-support/setup-hooks/auto-patchelf.sh @@ -0,0 +1,135 @@ +declare -a autoPatchelfLibs + +gatherLibraries() { + autoPatchelfLibs+=("$1/lib") +} + +envHooks+=(gatherLibraries) + +isExecutable() { + [ "$(file -b -N --mime-type "$1")" = application/x-executable ] +} + +findElfs() { + find "$1" -type f -exec "$SHELL" -c ' + while [ -n "$1" ]; do + mimeType="$(file -b -N --mime-type "$1")" + if [ "$mimeType" = application/x-executable \ + -o "$mimeType" = application/x-sharedlib ]; then + echo "$1" + fi + shift + done + ' -- {} + +} + +declare -a cachedDependencies + +addToDepCache() { + local existing + for existing in "${cachedDependencies[@]}"; do + if [ "$existing" = "$1" ]; then return; fi + done + cachedDependencies+=("$1") +} + +declare -gi depCacheInitialised=0 +declare -gi doneRecursiveSearch=0 +declare -g foundDependency + +getDepsFromSo() { + ldd "$1" 2> /dev/null | sed -n -e 's/[^=]*=> *\(.\+\) \+([^)]*)$/\1/p' +} + +checkElfDep() { + local errors ldout="$(ldd "$1")" + if errors="$(echo "$ldout" | grep -F "not found")"; then + echo -e "Library dependencies missing for $1:\n$errors" + fi +} + +populateCacheWithRecursiveDeps() { + local so found foundso + for so in "${cachedDependencies[@]}"; do + local IFS=$'\n' + for found in $(getDepsFromSo "$so"); do + local libdir="${found%/*}" + local base="${found##*/}" + local soname="${base%.so*}" + for foundso in "${found%/*}/$soname".so*; do + addToDepCache "$foundso" + done + done + done +} + +findDependency() { + local filename="$1" + local lib dep + + if [ $depCacheInitialised -eq 0 ]; then + for lib in "${autoPatchelfLibs[@]}"; do + for so in "$lib/"*.so*; do addToDepCache "$so"; done + done + depCacheInitialised=1 + fi + + for dep in "${cachedDependencies[@]}"; do + if [ "$filename" = "${dep##*/}" ]; then + foundDependency="$dep" + return 0 + fi + done + + if [ $doneRecursiveSearch -eq 0 ]; then + populateCacheWithRecursiveDeps + doneRecursiveSearch=1 + findDependency "$filename" || return 1 + return 0 + fi + return 1 +} + +autoPatchelfFile() { + local toPatch="$1" + local dep + + local interpreter="$(cat $NIX_CC/nix-support/dynamic-linker)" + if isExecutable "$toPatch"; then + patchelf --set-interpreter "$interpreter" "$toPatch" + fi + + patchelf --remove-rpath "$toPatch" + + local rpath="" + local missing="$( + ldd "$toPatch" | sed -n -e 's/^[\t ]*\([^ ]\+\) => not found.*/\1/p' + )" + local IFS=$'\n' + for dep in $missing; do + echo -n "searching for dependency $dep..." >&2 + if findDependency "$dep"; then + rpath="$rpath${rpath:+:}${foundDependency%/*}" + echo " found: $foundDependency" >&2 + else + echo " not found" >&2 + fi + done + + if [ -n "$rpath" ]; then + echo "setting RPATH of $toPatch to $rpath" >&2 + patchelf --set-rpath "$rpath" "$toPatch" + fi +} + +autoPatchelf() { + echo "automatically fixing dependencies for ELF files" >&2 + + local i IFS=$'\n' + cachedDependencies+=( + $(find "$out" \! -type d \( -name '*.so' -o -name '*.so.*' \)) + ) + for i in $(findElfs "$prefix"); do autoPatchelfFile "$i"; done +} + +postInstallHooks+=(autoPatchelf) |