From a61e4dbb180c7dabcc932ac03fc6d4d724b9178e Mon Sep 17 00:00:00 2001 From: David McFarland Date: Mon, 29 Dec 2025 14:53:21 -0400 Subject: [PATCH 01/11] cygwin-dll-link: move from make-derivation to cross stdenv --- .../cygwin/cygwin-dll-link-hook}/cygwin-dll-link.sh | 2 +- .../cygwin/cygwin-dll-link-hook/default.nix | 13 +++++++++++++ pkgs/os-specific/cygwin/default.nix | 2 ++ pkgs/stdenv/cross/default.nix | 5 ++++- pkgs/stdenv/generic/make-derivation.nix | 1 - 5 files changed, 20 insertions(+), 3 deletions(-) rename pkgs/{build-support/setup-hooks => os-specific/cygwin/cygwin-dll-link-hook}/cygwin-dll-link.sh (98%) create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/default.nix diff --git a/pkgs/build-support/setup-hooks/cygwin-dll-link.sh b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh similarity index 98% rename from pkgs/build-support/setup-hooks/cygwin-dll-link.sh rename to pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh index a9309925e658..371ba633edc8 100644 --- a/pkgs/build-support/setup-hooks/cygwin-dll-link.sh +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh @@ -18,7 +18,7 @@ addOutputDLLPaths() { postInstallHooks+=(addOutputDLLPaths) _dllDeps() { - "$OBJDUMP" -p "$1" \ + @objdump@ -p "$1" \ | sed -n 's/.*DLL Name: \(.*\)/\1/p' \ | sort -u } diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/default.nix b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/default.nix new file mode 100644 index 000000000000..b3185140f2ea --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/default.nix @@ -0,0 +1,13 @@ +{ + lib, + makeSetupHook, + binutils-unwrapped, + stdenv, + callPackage, +}: +makeSetupHook { + name = "cygwin-dll-link-hook"; + substitutions = { + objdump = "${lib.getBin binutils-unwrapped}/${stdenv.targetPlatform.config}/bin/objdump"; + }; +} ./cygwin-dll-link.sh diff --git a/pkgs/os-specific/cygwin/default.nix b/pkgs/os-specific/cygwin/default.nix index 09f69ff9f9b2..d1217f7880c1 100644 --- a/pkgs/os-specific/cygwin/default.nix +++ b/pkgs/os-specific/cygwin/default.nix @@ -21,5 +21,7 @@ makeScopeWithSplicing' { # this is here to avoid symlinks being made to cygwin1.dll in /nix/store newlib-cygwin-nobin = callPackage ./newlib-cygwin/nobin.nix { }; newlib-cygwin-headers = callPackage ./newlib-cygwin { headersOnly = true; }; + + cygwinDllLinkHook = callPackage ./cygwin-dll-link-hook { }; }; } diff --git a/pkgs/stdenv/cross/default.nix b/pkgs/stdenv/cross/default.nix index cc53957eee0d..b194c780a319 100644 --- a/pkgs/stdenv/cross/default.nix +++ b/pkgs/stdenv/cross/default.nix @@ -83,7 +83,10 @@ lib.init bootStages || p.isGenode; in f hostPlatform && !(f buildPlatform) - ) buildPackages.updateAutotoolsGnuConfigScriptsHook; + ) buildPackages.updateAutotoolsGnuConfigScriptsHook + ++ lib.optional ( + hostPlatform.isCygwin && !buildPlatform.isCygwin + ) buildPackages.cygwin.cygwinDllLinkHook; }) ); in diff --git a/pkgs/stdenv/generic/make-derivation.nix b/pkgs/stdenv/generic/make-derivation.nix index cdfeb34a2411..d66fbf71035e 100644 --- a/pkgs/stdenv/generic/make-derivation.nix +++ b/pkgs/stdenv/generic/make-derivation.nix @@ -475,7 +475,6 @@ let nativeBuildInputs ++ optional separateDebugInfo' ../../build-support/setup-hooks/separate-debug-info.sh ++ optional isWindows ../../build-support/setup-hooks/win-dll-link.sh - ++ optional isCygwin ../../build-support/setup-hooks/cygwin-dll-link.sh ++ optionals doCheck nativeCheckInputs ++ optionals doInstallCheck nativeInstallCheckInputs; From c345cee8a133e44e881225f57ad3d8eecdcb9644 Mon Sep 17 00:00:00 2001 From: David McFarland Date: Fri, 10 Oct 2025 13:17:27 -0300 Subject: [PATCH 02/11] cygwin-dll-link: use link for cygwin1.dll --- .../cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh | 6 +++++- pkgs/stdenv/generic/make-derivation.nix | 1 - 2 files changed, 5 insertions(+), 2 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 371ba633edc8..2d2023d49a71 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 @@ -29,7 +29,11 @@ _linkDeps() { local dll _dllDeps "$target" | while read -r dll; do echo ' dll:' "$dll" - if [[ -e "$dir/$dll" ]]; then continue; fi + if [[ -L "$dir/$dll" || -e "$dir/$dll" ]]; then continue; fi + if [[ $dll = cygwin1.dll ]]; then + CYGWIN+=\ winsymlinks:nativestrict ln -sr /bin/cygwin1.dll "$dir" + continue + fi # Locate the DLL - it should be an *executable* file on $LINK_DLL_FOLDERS. local dllPath if ! dllPath="$(PATH="$(dirname "$target"):$LINK_DLL_FOLDERS" type -P "$dll")"; then diff --git a/pkgs/stdenv/generic/make-derivation.nix b/pkgs/stdenv/generic/make-derivation.nix index d66fbf71035e..4f5ee160dbcf 100644 --- a/pkgs/stdenv/generic/make-derivation.nix +++ b/pkgs/stdenv/generic/make-derivation.nix @@ -704,7 +704,6 @@ let allowedImpureDLLs ++ lib.optionals isCygwin [ "KERNEL32.dll" - "cygwin1.dll" ]; } // ( From ae16ae18f5f6faee70de642adf10ea2cf59efe40 Mon Sep 17 00:00:00 2001 From: David McFarland Date: Tue, 21 Oct 2025 13:55:40 -0300 Subject: [PATCH 03/11] cygwin-dll-link: skip non-executable files --- pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh | 1 + 1 file changed, 1 insertion(+) 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 2d2023d49a71..49cbde49174c 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 @@ -25,6 +25,7 @@ _dllDeps() { _linkDeps() { local target="$1" dir="$2" check="$3" + [[ ! -x "$target" ]] && echo "_linkDeps: $target is not executable, skipping." 1>&2 && return echo 'target:' "$target" local dll _dllDeps "$target" | while read -r dll; do From 0beb3436f0156f57fe3e892d7f900b098e2e1981 Mon Sep 17 00:00:00 2001 From: David McFarland Date: Tue, 21 Oct 2025 14:15:44 -0300 Subject: [PATCH 04/11] cygwin-dll-link: include /lib/ when searching for PEs --- pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 49cbde49174c..2fddfc349b94 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 @@ -72,7 +72,7 @@ linkDLLs() { # Iterate over any DLL that we depend on. local target - for target in {bin,libexec}/**/*.{exe,dll}; do + for target in {bin,lib,libexec}/**/*.{exe,dll}; do [[ ! -f "$target" || ! -x "$target" ]] || _linkDeps "$target" "$(dirname "$target")" "1" done From f42d59445bf3fa8b37c1203158e0ba48dec84761 Mon Sep 17 00:00:00 2001 From: David McFarland Date: Tue, 21 Oct 2025 20:59:52 -0300 Subject: [PATCH 05/11] cygwin-dll-link: move DLLs to $lib/bin by default This allows us to remove LINK_DLL_FOLDERS and use HOST_PATH instead. --- pkgs/build-support/cc-wrapper/default.nix | 2 +- .../cygwin-dll-link-hook/cygwin-dll-link.sh | 21 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pkgs/build-support/cc-wrapper/default.nix b/pkgs/build-support/cc-wrapper/default.nix index 9bb07456e8f2..058b2d7a2dec 100644 --- a/pkgs/build-support/cc-wrapper/default.nix +++ b/pkgs/build-support/cc-wrapper/default.nix @@ -603,7 +603,7 @@ stdenvNoCC.mkDerivation { installPhase = if targetPlatform.isCygwin then '' - echo addToSearchPath "LINK_DLL_FOLDERS" "${cc_bin}/lib" >> $out + echo addToSearchPath "HOST_PATH" "${cc_bin}/lib" >> $out # Work around build failure caused by the gnulib workaround for # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114870. remove after # gnulib is updated in core packages (e.g. iconv, gnupatch, gnugrep) 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 2fddfc349b94..6fe69347861e 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 @@ -1,21 +1,20 @@ # shellcheck shell=bash -addLinkDLLPaths() { - addToSearchPath "LINK_DLL_FOLDERS" "$1/lib" - addToSearchPath "LINK_DLL_FOLDERS" "$1/bin" +_moveDLLsToLib() { + if [[ "${dontMoveDLLsToLib-}" ]]; then return; fi + # shellcheck disable=SC2154 + moveToOutput "bin/*.dll" "${!outputLib}" } -# shellcheck disable=SC2154 -addEnvHooks "$targetOffset" addLinkDLLPaths +preFixupHooks+=(_moveDLLsToLib) addOutputDLLPaths() { for output in $(getAllOutputNames); do - addToSearchPath "LINK_DLL_FOLDERS" "${!output}/lib" - addToSearchPath "LINK_DLL_FOLDERS" "${!output}/bin" + addToSearchPath "HOST_PATH" "${!output}/bin" done } -postInstallHooks+=(addOutputDLLPaths) +preFixupHooks+=(addOutputDLLPaths) _dllDeps() { @objdump@ -p "$1" \ @@ -35,13 +34,13 @@ _linkDeps() { CYGWIN+=\ winsymlinks:nativestrict ln -sr /bin/cygwin1.dll "$dir" continue fi - # Locate the DLL - it should be an *executable* file on $LINK_DLL_FOLDERS. + # Locate the DLL - it should be an *executable* file on $HOST_PATH. local dllPath - if ! dllPath="$(PATH="$(dirname "$target"):$LINK_DLL_FOLDERS" type -P "$dll")"; then + if ! dllPath="$(PATH="$(dirname "$target"):$HOST_PATH" type -P "$dll")"; then if [[ -z "$check" || -n "${allowedImpureDLLsMap[$dll]}" ]]; then continue fi - echo unable to find "$dll" in "$LINK_DLL_FOLDERS" >&2 + echo unable to find "$dll" in "$HOST_PATH" >&2 exit 1 fi echo ' linking to:' "$dllPath" From f2ac4eef529fc8235167790f786859f048040c23 Mon Sep 17 00:00:00 2001 From: David McFarland Date: Sun, 8 Feb 2026 18:34:46 -0400 Subject: [PATCH 06/11] gcc: move cygwin DLLs to $target/bin --- pkgs/build-support/cc-wrapper/default.nix | 2 +- pkgs/development/compilers/gcc/common/builder.nix | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkgs/build-support/cc-wrapper/default.nix b/pkgs/build-support/cc-wrapper/default.nix index 058b2d7a2dec..89cc7c4ce4fd 100644 --- a/pkgs/build-support/cc-wrapper/default.nix +++ b/pkgs/build-support/cc-wrapper/default.nix @@ -603,7 +603,7 @@ stdenvNoCC.mkDerivation { installPhase = if targetPlatform.isCygwin then '' - echo addToSearchPath "HOST_PATH" "${cc_bin}/lib" >> $out + echo addToSearchPath "HOST_PATH" "${cc_solib}/bin" >> $out # Work around build failure caused by the gnulib workaround for # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114870. remove after # gnulib is updated in core packages (e.g. iconv, gnupatch, gnugrep) diff --git a/pkgs/development/compilers/gcc/common/builder.nix b/pkgs/development/compilers/gcc/common/builder.nix index b1b5daeb00f1..1e90828a6643 100644 --- a/pkgs/development/compilers/gcc/common/builder.nix +++ b/pkgs/development/compilers/gcc/common/builder.nix @@ -363,6 +363,13 @@ originalAttrs: fi done '' + + lib.optionalString stdenv.targetPlatform.isCygwin '' + targetBinDir="''${targetConfig+$targetConfig/}bin" + for i in "''${!outputBin}/$targetLibDir"/cyg*.dll; do + mkdir -p "''${!outputLib}/$targetBinDir" + mv "$i" "''${!outputLib}/$targetBinDir"/ + done + '' # if cross-compiling, link from $lib/lib to $lib/${targetConfig}. # since native-compiles have $lib/lib as a directory (not a # symlink), this ensures that in every case we can assume that From f5353dd50e51524cb3367010647e70662991343f Mon Sep 17 00:00:00 2001 From: David McFarland Date: Sun, 8 Feb 2026 18:51:59 -0400 Subject: [PATCH 07/11] cygwin-dll-link: improve search path order The search order will now be: 1. link target path 2. output bin paths 3. explicit input paths (e.g. libc) 4. HOST_PATH --- pkgs/build-support/cc-wrapper/default.nix | 2 +- .../cygwin-dll-link-hook/cygwin-dll-link.sh | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pkgs/build-support/cc-wrapper/default.nix b/pkgs/build-support/cc-wrapper/default.nix index 89cc7c4ce4fd..808fe5db2780 100644 --- a/pkgs/build-support/cc-wrapper/default.nix +++ b/pkgs/build-support/cc-wrapper/default.nix @@ -603,7 +603,7 @@ stdenvNoCC.mkDerivation { installPhase = if targetPlatform.isCygwin then '' - echo addToSearchPath "HOST_PATH" "${cc_solib}/bin" >> $out + echo addToSearchPath "_linkDeps_inputPath" "${cc_solib}/bin" >> $out # Work around build failure caused by the gnulib workaround for # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114870. remove after # gnulib is updated in core packages (e.g. iconv, gnupatch, gnugrep) 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 6fe69347861e..3711ef494ed1 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 @@ -8,13 +8,15 @@ _moveDLLsToLib() { preFixupHooks+=(_moveDLLsToLib) -addOutputDLLPaths() { +declare _linkDeps_inputPath _linkDeps_outputPath + +_addOutputDLLPaths() { for output in $(getAllOutputNames); do - addToSearchPath "HOST_PATH" "${!output}/bin" + addToSearchPath _linkDeps_outputPath "${!output}/bin" done } -preFixupHooks+=(addOutputDLLPaths) +preFixupHooks+=(_addOutputDLLPaths) _dllDeps() { @objdump@ -p "$1" \ @@ -35,12 +37,13 @@ _linkDeps() { continue fi # Locate the DLL - it should be an *executable* file on $HOST_PATH. - local dllPath - if ! dllPath="$(PATH="$(dirname "$target"):$HOST_PATH" type -P "$dll")"; then + 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 fi - echo unable to find "$dll" in "$HOST_PATH" >&2 + echo unable to find "$dll" in "$searchPath" >&2 exit 1 fi echo ' linking to:' "$dllPath" From cbb96285de25e950fa9cc63f797cfd08202aea1a Mon Sep 17 00:00:00 2001 From: David McFarland Date: Tue, 21 Oct 2025 14:15:21 -0300 Subject: [PATCH 08/11] 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) From dcebaab6239280506ba1d54d1d22f3f2c8fb5579 Mon Sep 17 00:00:00 2001 From: David McFarland Date: Mon, 29 Dec 2025 14:53:21 -0400 Subject: [PATCH 09/11] cygwin-dll-link: improve symlink handling --- .../cygwin-dll-link-hook/cygwin-dll-link.sh | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 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 f5d1ae8e0a9c..006dc0408cd0 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 @@ -28,20 +28,24 @@ declare -Ag _linkDeps_visited _linkDeps() { local target="$1" dir="$2" + + # canonicalise these for the dictionary + target=$(realpath -s "$target") + dir=$(realpath -s "$dir") + if [[ -v _linkDeps_visited[$target] ]]; then echo "_linkDeps: $target was already linked." 1>&2 return fi - if [[ ! -f "$target" || ! -x "$target" ]]; then + 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")" + local output inOutput for output in $outputs ; do - if [[ $realTarget == "${!output}"* ]]; then - echo "_linkDeps: $target ($realTarget) is in $output (${!output})." 1>&2 + if [[ $target == "${!output}"* && ! -L $target ]]; then + echo "_linkDeps: $target is in $output (${!output})." 1>&2 inOutput=1 break fi @@ -51,25 +55,32 @@ _linkDeps() { local dll while read -r dll; do echo ' dll:' "$dll" - if [[ -v _linkDeps_visited[$dir/$dll] ]]; then + local dllPath=$dir/$dll + if [[ -v _linkDeps_visited[$dllPath] ]]; then echo "_linkDeps: $dll was already linked into $dir." 1>&2 continue fi - if [[ -L "$dir/$dll" || -e "$dir/$dll" ]]; then - echo ' already exists' - _linkDeps "$dir/$dll" "$dir" + if [[ -L $dllPath ]]; then + echo ' already linked' + dllPath=$(realpath -s "$(readlink "$dllPath")") + _linkDeps "$dllPath" "$dir" + elif [[ -e $dllPath ]]; then + echo ' already exits' + _linkDeps "$dllPath" "$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 + # This intentionally doesn't use $dir because we want to prefer + # dependencies that are already linked next to the target. + local searchPath realTarget + searchPath=$_linkDeps_outputPath:$_linkDeps_inputPath:$HOST_PATH + if [[ -L $target ]]; then + searchPath=$searchPath:$(dirname "$(readlink "$target")") + fi + searchPath=$(dirname "$target"):$searchPath + 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 @@ -82,6 +93,7 @@ _linkDeps() { fi _linkDeps_visited[$dir/$dll]=1 done < <(_dllDeps "$target") + wait $! } # linkDLLsDir can be used to override the destination path for links. This is @@ -94,7 +106,7 @@ linkDLLs() { # shellcheck disable=SC2154 ( set -e - shopt -s globstar nullglob + shopt -s globstar nullglob dotglob local -a allowedImpureDLLsArray concatTo allowedImpureDLLsArray allowedImpureDLLs From f1d9560d4032c5df9c46fe57cb1e38d8b8f9fec3 Mon Sep 17 00:00:00 2001 From: David McFarland Date: Sun, 8 Feb 2026 19:40:32 -0400 Subject: [PATCH 10/11] cygwin.cygwinDllLinkHook: add tests --- .../cygwin/cygwin-dll-link-hook/default.nix | 2 + .../cygwin-dll-link-hook/tests/default.nix | 130 ++++++++++++++++++ .../cygwin-dll-link-hook/tests/dll/Makefile | 16 +++ .../cygwin-dll-link-hook/tests/dll/hello.c | 7 + .../cygwin-dll-link-hook/tests/dll/hello.h | 2 + .../cygwin-dll-link-hook/tests/dll2/Makefile | 16 +++ .../cygwin-dll-link-hook/tests/dll2/hello2.c | 6 + .../cygwin-dll-link-hook/tests/dll2/hello2.h | 2 + .../cygwin-dll-link-hook/tests/exe/Makefile | 13 ++ .../cygwin-dll-link-hook/tests/exe/main.c | 6 + .../tests/user32-dll/Makefile | 16 +++ .../tests/user32-dll/peek.c | 7 + .../tests/user32-dll/peek.h | 2 + .../tests/user32-exe/Makefile | 13 ++ .../tests/user32-exe/main.c | 6 + .../tests/user32/Makefile | 13 ++ .../cygwin-dll-link-hook/tests/user32/main.c | 7 + 17 files changed, 264 insertions(+) create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/default.nix create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/Makefile create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/hello.c create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/hello.h create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/Makefile create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/hello2.c create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/hello2.h create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/exe/Makefile create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/exe/main.c create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/Makefile create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/peek.c create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/peek.h create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-exe/Makefile create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-exe/main.c create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32/Makefile create mode 100644 pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32/main.c diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/default.nix b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/default.nix index b3185140f2ea..ff9eed6bdeef 100644 --- a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/default.nix +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/default.nix @@ -10,4 +10,6 @@ makeSetupHook { substitutions = { objdump = "${lib.getBin binutils-unwrapped}/${stdenv.targetPlatform.config}/bin/objdump"; }; + + passthru.tests = callPackage ./tests { }; } ./cygwin-dll-link.sh diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/default.nix b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/default.nix new file mode 100644 index 000000000000..a8744001dc2d --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/default.nix @@ -0,0 +1,130 @@ +{ + dieHook, + lib, + stdenv, + testers, + runCommand, +}: + +rec { + dll = stdenv.mkDerivation { + name = "dll"; + src = ./dll; + outputs = [ + "out" + "dev" + ]; + buildInputs = [ dll2 ]; + strictDeps = true; + }; + + dll2 = stdenv.mkDerivation { + name = "dll2"; + src = ./dll2; + outputs = [ + "out" + "dev" + ]; + }; + + exe = stdenv.mkDerivation { + name = "exe"; + src = ./exe; + buildInputs = [ dll ]; + nativeBuildInputs = [ dieHook ]; + strictDeps = true; + doCheck = true; + postFixup = ''[[ -e "$out"/bin/cyghello2.dll ]] || die missing indirect dependency''; + }; + + link-dll = exe.overrideAttrs { + name = "link-dll"; + preFixup = '' + ln -s ${lib.getLib dll}/bin/cyghello.dll "$out"/bin/ + ''; + }; + + user32 = stdenv.mkDerivation { + name = "user32"; + src = ./user32; + allowedImpureDLLs = [ "USER32.dll" ]; + }; + + impure-dll = testers.testBuildFailure ( + user32.overrideAttrs { + name = "impure-dll"; + allowedImpureDLLs = [ ]; + } + ); + + user32-dll = stdenv.mkDerivation { + name = "user32-dll"; + src = ./user32-dll; + outputs = [ + "out" + "dev" + ]; + allowedImpureDLLs = [ "USER32.dll" ]; + }; + + user32-exe = stdenv.mkDerivation { + name = "user32-exe"; + src = ./user32-exe; + buildInputs = [ user32-dll ]; + strictDeps = true; + doCheck = true; + }; + + link-dir-dll = exe.overrideAttrs { + name = "link-dir-dll"; + preFixup = '' + mkdir "$out"/libexec + ln -s ${lib.getLib user32-dll}/bin/cygpeek.dll "$out"/libexec/ + linkDLLsDir="$out"/bin linkDLLs "$out"/libexec/cygpeek.dll + ''; + }; + + link-dir-exe = exe.overrideAttrs { + name = "link-dir-exe"; + preFixup = '' + mkdir "$out"/libexec + ln -s ${lib.getLib user32-exe}/bin/{peek.exe,cygpeek.dll} "$out"/libexec/ + linkDLLsDir="$out"/bin linkDLLs "$out"/libexec/peek.exe + ''; + }; + + link-user32-dll = exe.overrideAttrs { + name = "link-user32-dll"; + preFixup = '' + ln -s ${lib.getLib user32-dll}/bin/cygpeek.dll "$out"/bin/ + ''; + }; + + copy-dll-impure = testers.testBuildFailure ( + user32-exe.overrideAttrs { + name = "copy-dll-impure"; + preFixup = '' + cp ${lib.getLib user32-dll}/bin/cygpeek.dll "$out"/bin/ + ''; + } + ); + + copy-dll = user32-exe.overrideAttrs { + name = "copy-dll"; + preFixup = '' + cp ${lib.getLib user32-dll}/bin/cygpeek.dll "$out"/bin/ + linkDLLs "$out"/bin/cygpeek.dll + ''; + allowedImpureDLLs = [ "USER32.dll" ]; + }; + + double-link = user32-exe.overrideAttrs { + name = "double-link"; + preFixup = ''linkDLLs "$out"''; + }; + + utf8-glob = runCommand "utf8-glob" { } '' + touch NetLock_Arany_=Class_Gold=_Főtanstvny:49412ce40010.crt + ls -l NetLock* > "$out" + ''; +} diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/Makefile b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/Makefile new file mode 100644 index 000000000000..25e1e53c38e4 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/Makefile @@ -0,0 +1,16 @@ +.PHONY: all install +all: cyghello.dll + +install: $(out)/bin/cyghello.dll $(out)/lib/libhello.dll.a $(out)/include/hello.h + +cyghello.dll libhello.dll.a: hello.c + $(CC) -o $@ $^ -shared -Wl,--out-implib,libhello.dll.a -Wl,--export-all-symbols -lhello2 + +$(out)/bin/cyghello.dll: cyghello.dll + install -m755 -D $< $@ + +$(out)/lib/libhello.dll.a: libhello.dll.a + install -m644 -D $< $@ + +$(out)/include/hello.h: hello.h + install -m644 -D $< $@ diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/hello.c b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/hello.c new file mode 100644 index 000000000000..db1a301c91a4 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/hello.c @@ -0,0 +1,7 @@ +#include "hello.h" +#include +#include + +void hello() { + hello2(); +} diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/hello.h b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/hello.h new file mode 100644 index 000000000000..a4a0b54c6052 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll/hello.h @@ -0,0 +1,2 @@ +#pragma once +void hello(); diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/Makefile b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/Makefile new file mode 100644 index 000000000000..87c5c1a9b9cc --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/Makefile @@ -0,0 +1,16 @@ +.PHONY: all install +all: cyghello2.dll + +install: $(out)/bin/cyghello2.dll $(out)/lib/libhello2.dll.a $(out)/include/hello2.h + +cyghello2.dll libhello2.dll.a: hello2.c + $(CC) -o $@ $^ -shared -Wl,--out-implib,libhello2.dll.a -Wl,--export-all-symbols + +$(out)/bin/cyghello2.dll: cyghello2.dll + install -m755 -D $< $@ + +$(out)/lib/libhello2.dll.a: libhello2.dll.a + install -m644 -D $< $@ + +$(out)/include/hello2.h: hello2.h + install -m644 -D $< $@ diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/hello2.c b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/hello2.c new file mode 100644 index 000000000000..d23050fbb776 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/hello2.c @@ -0,0 +1,6 @@ +#include +#include "hello2.h" + +void hello2() { + printf("Hello, World!\n"); +} diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/hello2.h b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/hello2.h new file mode 100644 index 000000000000..34e200a07e95 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/dll2/hello2.h @@ -0,0 +1,2 @@ +#pragma once +void hello2(); diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/exe/Makefile b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/exe/Makefile new file mode 100644 index 000000000000..da16b5d08c79 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/exe/Makefile @@ -0,0 +1,13 @@ +.PHONY: all check install +all: hello.exe + +install: $(out)/bin/hello.exe + +hello.exe: main.c + $(CC) -o $@ $^ -lhello + +check: hello.exe + ./hello.exe + +$(out)/bin/hello.exe: hello.exe + install -m755 -D $< $@ diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/exe/main.c b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/exe/main.c new file mode 100644 index 000000000000..9dbc882ea430 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/exe/main.c @@ -0,0 +1,6 @@ +#include + +int main() { + hello(); + return 0; +} diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/Makefile b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/Makefile new file mode 100644 index 000000000000..483ad8854a06 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/Makefile @@ -0,0 +1,16 @@ +.PHONY: all install +all: cygpeek.dll + +install: $(out)/bin/cygpeek.dll $(out)/lib/libpeek.dll.a $(out)/include/peek.h + +cygpeek.dll libpeek.dll.a: peek.c + $(CC) -o $@ $^ -shared -Wl,--out-implib,libpeek.dll.a -Wl,--export-all-symbols + +$(out)/bin/cygpeek.dll: cygpeek.dll + install -m755 -D $< $@ + +$(out)/lib/libpeek.dll.a: libpeek.dll.a + install -m644 -D $< $@ + +$(out)/include/peek.h: peek.h + install -m644 -D $< $@ diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/peek.c b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/peek.c new file mode 100644 index 000000000000..715adb003c15 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/peek.c @@ -0,0 +1,7 @@ +#include + +int peek() +{ + MSG msg; + PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); +} diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/peek.h b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/peek.h new file mode 100644 index 000000000000..800f23f77da5 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-dll/peek.h @@ -0,0 +1,2 @@ +#pragma once +void peek(); diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-exe/Makefile b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-exe/Makefile new file mode 100644 index 000000000000..231601818914 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-exe/Makefile @@ -0,0 +1,13 @@ +.PHONY: all check install +all: peek.exe + +install: $(out)/bin/peek.exe + +peek.exe: main.c + $(CC) -o $@ $^ -lpeek + +check: peek.exe + ./peek.exe + +$(out)/bin/peek.exe: peek.exe + install -m755 -D $< $@ diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-exe/main.c b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-exe/main.c new file mode 100644 index 000000000000..cddc78c91cab --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32-exe/main.c @@ -0,0 +1,6 @@ +#include + +int main() { + peek(); + return 0; +} diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32/Makefile b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32/Makefile new file mode 100644 index 000000000000..6c696a2e8cd0 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32/Makefile @@ -0,0 +1,13 @@ +.PHONY: all check install +all: hello.exe + +install: $(out)/bin/hello.exe + +hello.exe: main.c + $(CC) -o $@ $^ + +check: hello.exe + ./hello.exe + +$(out)/bin/hello.exe: hello.exe + install -m755 -D $< $@ diff --git a/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32/main.c b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32/main.c new file mode 100644 index 000000000000..9cc135770d21 --- /dev/null +++ b/pkgs/os-specific/cygwin/cygwin-dll-link-hook/tests/user32/main.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + MSG msg; + PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); +} From a934e8a9e5a701efa45279def48d7aa097fe2db8 Mon Sep 17 00:00:00 2001 From: David McFarland Date: Sun, 8 Feb 2026 19:51:57 -0400 Subject: [PATCH 11/11] cygwin-dll-link: replace escaped space with quoting --- pkgs/os-specific/cygwin/cygwin-dll-link-hook/cygwin-dll-link.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 006dc0408cd0..95e63b5458ab 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 @@ -88,7 +88,7 @@ _linkDeps() { fi fi echo ' linking to:' "$dllPath" - CYGWIN+=\ winsymlinks:nativestrict ln -sr "$dllPath" "$dir" + CYGWIN+=' winsymlinks:nativestrict' ln -sr "$dllPath" "$dir" _linkDeps "$dllPath" "$dir" fi _linkDeps_visited[$dir/$dll]=1