diff --git a/pkgs/build-support/cc-wrapper/default.nix b/pkgs/build-support/cc-wrapper/default.nix index f52ac48a1a87..8412aabfe5fa 100644 --- a/pkgs/build-support/cc-wrapper/default.nix +++ b/pkgs/build-support/cc-wrapper/default.nix @@ -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 diff --git a/pkgs/build-support/setup-hooks/win-dll-link.sh b/pkgs/build-support/setup-hooks/win-dll-link.sh index ca4cbb349b6c..14594bcba937 100644 --- a/pkgs/build-support/setup-hooks/win-dll-link.sh +++ b/pkgs/build-support/setup-hooks/win-dll-link.sh @@ -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 file’s 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" + ) +}