win-dll-links: also copy dll from dependencies

Fixes running `pkgsCross.mingwW64._7zz` in wine.

Fixes issue 38451

```
tree result/bin
result/bin
├── 7zz.exe
└── mcfgthread-12.dll -> ../../wmgj476qjfw26f9aij1d64lxrjfv6kk0-mcfgthreads-x86_64-w64-mingw32-git/bin/mcfgthread-12.dll
```

Co-authored-by: marius david <marius@mariusdavid.fr>
This commit is contained in:
brano543 2023-08-31 01:40:11 +02:00 committed by Artturin
parent a736ca31c5
commit 1086f093a9
2 changed files with 94 additions and 41 deletions

View file

@ -332,7 +332,16 @@ stdenv.mkDerivation {
setupHooks = [
../setup-hooks/role.bash
] ++ lib.optional (cc.langC or true) ./setup-hook.sh
++ lib.optional (cc.langFortran or false) ./fortran-hook.sh;
++ lib.optional (cc.langFortran or false) ./fortran-hook.sh
++ lib.optional (targetPlatform.isWindows) (stdenv.mkDerivation {
name = "win-dll-hook.sh";
dontUnpack = true;
installPhase = ''
echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_solib}/lib" > $out
echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_solib}/lib64" >> $out
echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_solib}/lib32" >> $out
'';
});
postFixup =
# Ensure flags files exists, as some other programs cat them. (That these

View file

@ -1,45 +1,89 @@
fixupOutputHooks+=(_linkDLLs)
# For every *.{exe,dll} in $output/bin/ we try to find all (potential)
# transitive dependencies and symlink those DLLs into $output/bin
# so they are found on invocation.
# (DLLs are first searched in the directory of the running exe file.)
# The links are relative, so relocating whole /nix/store won't break them.
_linkDLLs() {
(
if [ ! -d "$prefix/bin" ]; then exit; fi
cd "$prefix/bin"
addEnvHooks "$targetOffset" linkDLLGetFolders
# Compose path list where DLLs should be located:
# prefix $PATH by currently-built outputs
local DLLPATH=""
local outName
for outName in $(getAllOutputNames); do
addToSearchPath DLLPATH "${!outName}/bin"
done
DLLPATH="$DLLPATH:$PATH"
echo DLLPATH="'$DLLPATH'"
linkCount=0
# Iterate over any DLL that we depend on.
local dll
for dll in $($OBJDUMP -p *.{exe,dll} | sed -n 's/.*DLL Name: \(.*\)/\1/p' | sort -u); do
if [ -e "./$dll" ]; then continue; fi
# Locate the DLL - it should be an *executable* file on $DLLPATH.
local dllPath="$(PATH="$DLLPATH" type -P "$dll")"
if [ -z "$dllPath" ]; then continue; fi
# That DLL might have its own (transitive) dependencies,
# so add also all DLLs from its directory to be sure.
local dllPath2
for dllPath2 in "$dllPath" "$(dirname $(readlink "$dllPath" || echo "$dllPath"))"/*.dll; do
if [ -e ./"$(basename "$dllPath2")" ]; then continue; fi
CYGWIN+=\ winsymlinks:nativestrict ln -sr "$dllPath2" .
linkCount=$(($linkCount+1))
done
done
echo "Created $linkCount DLL link(s) in $prefix/bin"
)
linkDLLGetFolders() {
addToSearchPath "LINK_DLL_FOLDERS" "$1/lib"
addToSearchPath "LINK_DLL_FOLDERS" "$1/bin"
}
_linkDLLs() {
linkDLLsInfolder "$prefix/bin"
}
# Try to links every known dependency of exe/dll in the folder of the 1str input
# into said folder, so they are found on invocation.
# (DLLs are first searched in the directory of the running exe file.)
# The links are relative, so relocating whole /nix/store won't break them.
linkDLLsInfolder() {
(
local folder
folder="$1"
if [ ! -d "$folder" ]; then
echo "Not linking DLLs in the non-existent folder $folder"
return
fi
cd "$folder" || exit
# Use associative arrays as set
local filesToChecks
local filesDone
declare -A filesToChecks # files that still needs to have their dependancies checked
declare -A filesDone # files that had their dependancies checked and who is copied to the bin folder if found
markFileAsDone() {
if [ ! "${filesDone[$1]+a}" ]; then filesDone[$1]=a; fi
if [ "${filesToChecks[$1]+a}" ]; then unset 'filesToChecks[$1]'; fi
}
addFileToLink() {
if [ "${filesDone[$1]+a}" ]; then return; fi
if [ ! "${filesToChecks[$1]+a}" ]; then filesToChecks[$1]=a; fi
}
# Compose path list where DLLs should be located:
# prefix $PATH by currently-built outputs
local DLLPATH=""
local outName
for outName in $(getAllOutputNames); do
addToSearchPath DLLPATH "${!outName}/bin"
done
DLLPATH="$DLLPATH:$LINK_DLL_FOLDERS"
echo DLLPATH="'$DLLPATH'"
for peFile in *.{exe,dll}; do
if [ -e "./$peFile" ]; then
addFileToLink "$peFile"
fi
done
local searchPaths
readarray -td: searchPaths < <(printf -- "%s" "$DLLPATH")
local linkCount=0
while [ ${#filesToChecks[*]} -gt 0 ]; do
local listOfDlls=("${!filesToChecks[@]}")
local file=${listOfDlls[0]}
markFileAsDone "$file"
if [ ! -e "./$file" ]; then
local pathsFound
readarray -d '' pathsFound < <(find "${searchPaths[@]}" -name "$file" -type f -print0)
if [ ${#pathsFound[@]} -eq 0 ]; then continue; fi
local dllPath
dllPath="${pathsFound[0]}"
CYGWIN+=" winsymlinks:nativestrict" ln -sr "$dllPath" .
echo "linking $dllPath"
file="$dllPath"
linkCount=$((linkCount + 1))
fi
# local dep_file
# Look at the files dependancies
for dep_file in $($OBJDUMP -p "$file" | sed -n 's/.*DLL Name: \(.*\)/\1/p' | sort -u); do
addFileToLink "$dep_file"
done
done
echo "Created $linkCount DLL link(s) in $folder"
)
}