Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

win-dll-links: also copy dll from dependencies #252459

Merged
merged 1 commit into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
)
}