diff options
author | aszlig <aszlig@redmoonstudios.org> | 2017-09-13 05:06:32 +0200 |
---|---|---|
committer | aszlig <aszlig@redmoonstudios.org> | 2017-09-13 15:26:28 +0200 |
commit | ef6c66560175e4b5a798bd09caa31cd5ebad8127 (patch) | |
tree | ffa44ce33da4d3f28c52248076c948e3aee1b63e /pkgs/games | |
parent | 370f8a5e5215e4f1ff34e05b7094bf08eb5cf643 (diff) |
pkgs/games: Add a new buildGame function
The main functionality for this function is to gather missing dependencies in ELF executables and shared libraries and using patchelf to set the right RPATH. All of the dependencies are searched based on what we have in one of the buildInputs variables, so all we need to do is list them in there. One thing that's still left to solve is adding libraries to the RPATH which are only required at runtime. An example for this would be the pulseaudio library. Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Diffstat (limited to 'pkgs/games')
-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) |