Skip to content

Commit

Permalink
Merge pull request #252459 from brano543/win-dll-link
Browse files Browse the repository at this point in the history
  • Loading branch information
Artturin authored Aug 31, 2023
2 parents 75a4bcc + 1086f09 commit 1c574b4
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 40 deletions.
11 changes: 10 additions & 1 deletion pkgs/build-support/cc-wrapper/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
122 changes: 83 additions & 39 deletions pkgs/build-support/setup-hooks/win-dll-link.sh
Original file line number Diff line number Diff line change
@@ -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.
addEnvHooks "$targetOffset" linkDLLGetFolders

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.
_linkDLLs() {
(
if [ ! -d "$prefix/bin" ]; then exit; fi
cd "$prefix/bin"

# 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))
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
done
echo "Created $linkCount DLL link(s) in $prefix/bin"
)
}
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"
)
}

0 comments on commit 1c574b4

Please sign in to comment.