From cbb96285de25e950fa9cc63f797cfd08202aea1a Mon Sep 17 00:00:00 2001 From: David McFarland Date: Tue, 21 Oct 2025 14:15:21 -0300 Subject: [PATCH] cygwin-dll-link: allow linkDLLs to be used on individual dirs/files --- .../cygwin-dll-link-hook/cygwin-dll-link.sh | 104 +++++++++++++----- 1 file changed, 75 insertions(+), 29 deletions(-) diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh index 3711ef494ed1..f5d1ae8e0a9c 100644 --- a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh @@ -24,39 +24,74 @@ _dllDeps() { | sort -u } +declare -Ag _linkDeps_visited + _linkDeps() { - local target="$1" dir="$2" check="$3" - [[ ! -x "$target" ]] && echo "_linkDeps: $target is not executable, skipping." 1>&2 && return + local target="$1" dir="$2" + if [[ -v _linkDeps_visited[$target] ]]; then + echo "_linkDeps: $target was already linked." 1>&2 + return + fi + if [[ ! -f "$target" || ! -x "$target" ]]; then + echo "_linkDeps: $target is not an executable file, skipping." 1>&2 + return + fi + + local realTarget output inOutput + realTarget="$(readlink -f "$target")" + for output in $outputs ; do + if [[ $realTarget == "${!output}"* ]]; then + echo "_linkDeps: $target ($realTarget) is in $output (${!output})." 1>&2 + inOutput=1 + break + fi + done + echo 'target:' "$target" local dll - _dllDeps "$target" | while read -r dll; do + while read -r dll; do echo ' dll:' "$dll" - if [[ -L "$dir/$dll" || -e "$dir/$dll" ]]; then continue; fi - if [[ $dll = cygwin1.dll ]]; then - CYGWIN+=\ winsymlinks:nativestrict ln -sr /bin/cygwin1.dll "$dir" + if [[ -v _linkDeps_visited[$dir/$dll] ]]; then + echo "_linkDeps: $dll was already linked into $dir." 1>&2 continue fi - # Locate the DLL - it should be an *executable* file on $HOST_PATH. - local dllPath searchPath - searchPath=$(dirname "$target"):$_linkDeps_outputPath:$_linkDeps_inputPath:$HOST_PATH - if ! dllPath="$(PATH="$searchPath" type -P "$dll")"; then - if [[ -z "$check" || -n "${allowedImpureDLLsMap[$dll]}" ]]; then - continue + if [[ -L "$dir/$dll" || -e "$dir/$dll" ]]; then + echo ' already exists' + _linkDeps "$dir/$dll" "$dir" + else + local dllPath + if [[ $dll = cygwin1.dll ]]; then + dllPath=/bin/cygwin1.dll + else + local searchPath + searchPath=$(dirname "$target"):$_linkDeps_outputPath:$_linkDeps_inputPath:$HOST_PATH + # Locate the DLL - it should be an *executable* file on $HOST_PATH. + # This intentionally doesn't use $dir because we want to prefer dependencies + # that are already linked next to the target. + if ! dllPath="$(PATH="$searchPath" type -P "$dll")"; then + if [[ -z "$inOutput" || -n "${allowedImpureDLLsMap[$dll]}" ]]; then + continue + fi + echo unable to find "$dll" in "$searchPath" >&2 + exit 1 + fi fi - echo unable to find "$dll" in "$searchPath" >&2 - exit 1 + echo ' linking to:' "$dllPath" + CYGWIN+=\ winsymlinks:nativestrict ln -sr "$dllPath" "$dir" + _linkDeps "$dllPath" "$dir" fi - echo ' linking to:' "$dllPath" - CYGWIN+=\ winsymlinks:nativestrict ln -sr "$dllPath" "$dir" - # That DLL might have its own (transitive) dependencies, - # so add also all DLLs from its directory to be sure. - _linkDeps "$dllPath" "$dir" "" - done + _linkDeps_visited[$dir/$dll]=1 + done < <(_dllDeps "$target") } +# linkDLLsDir can be used to override the destination path for links. This is +# useful if you're trying to link dependencies of libraries into the directory +# containing an executable. +# +# Arguments can be a file or directory path. Directories are searched +# recursively. linkDLLs() { # shellcheck disable=SC2154 - if [ ! -d "$prefix" ]; then return; fi ( set -e shopt -s globstar nullglob @@ -70,15 +105,26 @@ linkDLLs() { allowedImpureDLLsMap[$dll]=1 done - cd "$prefix" - - # Iterate over any DLL that we depend on. - local target - for target in {bin,lib,libexec}/**/*.{exe,dll}; do - [[ ! -f "$target" || ! -x "$target" ]] || - _linkDeps "$target" "$(dirname "$target")" "1" + local target file + for target in "$@"; do + # Iterate over any DLL that we depend on. + if [[ -f "$target" ]]; then + _linkDeps "$target" "${linkDLLsDir-$(dirname "$target")}" + elif [[ -d "$target" ]]; then + for file in "${target%/}"/**/*.{exe,dll}; do + _linkDeps "$file" "${linkDLLsDir-$(dirname "$file")}" + done + else + echo "linkDLLs: $target is not a file or directory, skipping." 1>&2 + fi done ) } -fixupOutputHooks+=(linkDLLs) +_linkDLLs() { + # shellcheck disable=SC2154 + if [[ ! -d $prefix || ${dontLinkDLLs-} ]]; then return; fi + linkDLLs "$prefix"/{bin,lib,libexec}/ +} + +fixupOutputHooks+=(_linkDLLs)