From 4d9efa45e252c6f237637830fd0f054fa5f4f0eb Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Sun, 9 Nov 2025 18:55:11 +0300 Subject: [PATCH 1/2] tests/functional: Add tests for builtins.dirOf These will change in the next commit to fix the silent regression from 2.23 in the handling of multiple subsequent path separators. (cherry picked from commit 86f090837b07e8dacf8f839f603a44768b2ed999) --- .../lang/eval-okay-builtins-dirOf.exp | 1 + .../lang/eval-okay-builtins-dirOf.nix | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/functional/lang/eval-okay-builtins-dirOf.exp create mode 100644 tests/functional/lang/eval-okay-builtins-dirOf.nix diff --git a/tests/functional/lang/eval-okay-builtins-dirOf.exp b/tests/functional/lang/eval-okay-builtins-dirOf.exp new file mode 100644 index 000000000..1130b1510 --- /dev/null +++ b/tests/functional/lang/eval-okay-builtins-dirOf.exp @@ -0,0 +1 @@ +{ pathDoesntExistNested1 = /totallydoesntexistreally; pathDoesntExistNested2 = /totallydoesntexistreally/subdir1; pathDoesntExistRoot = /; pathRoot = /; stringEmpty = "."; stringMultipleSeps = "a"; stringNoSep = "."; stringRoot = "/"; stringRootA = "/"; stringRootSlash = "//"; stringRootSlashSlash = "///"; stringSingleDir = "a"; stringWithDot = "a/b/c/."; stringWithDotAndDotDot = "a/b/c/../."; stringWithDotAndDotDotSep2 = "a/b/c/../."; stringWithDotDot = "a/b/c/.."; stringWithDotDotSep2 = "a/b/c/.."; stringWithDotSep2 = "a/b/c/."; } diff --git a/tests/functional/lang/eval-okay-builtins-dirOf.nix b/tests/functional/lang/eval-okay-builtins-dirOf.nix new file mode 100644 index 000000000..d2eae1c4e --- /dev/null +++ b/tests/functional/lang/eval-okay-builtins-dirOf.nix @@ -0,0 +1,21 @@ +{ + stringEmpty = dirOf ""; + stringNoSep = dirOf "filename"; + stringSingleDir = dirOf "a/b"; + stringMultipleSeps = dirOf "a///b"; + stringRoot = dirOf "/"; + stringRootSlash = dirOf "//"; + stringRootSlashSlash = dirOf "///"; + stringRootA = dirOf "/a"; + stringWithDot = dirOf "a/b/c/./d"; + stringWithDotSep2 = dirOf "a/b/c/.//d"; + stringWithDotDot = dirOf "a/b/c/../d"; + stringWithDotDotSep2 = dirOf "a/b/c/..//d"; + stringWithDotAndDotDot = dirOf "a/b/c/.././d"; + stringWithDotAndDotDotSep2 = dirOf "a/b/c/.././/d"; + + pathRoot = dirOf /.; + pathDoesntExistRoot = dirOf /totallydoesntexistreally; + pathDoesntExistNested1 = dirOf /totallydoesntexistreally/subdir1; + pathDoesntExistNested2 = dirOf /totallydoesntexistreally/subdir1/subdir2; +} From 19839dbd153614308b2e9f3d459004cb66523ec8 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Sun, 9 Nov 2025 18:33:03 +0300 Subject: [PATCH 2/2] libexpr: Don't use nix::dirOf in prim_dirOf This gets us back to pre-2.23 behavior of this primop. Done by inlining the code of `nix::dirOf` from 2.2-maintenance. (cherry picked from commit a33fccf55a8450f54c91c56a54d462ada57c7712) --- src/libexpr/primops.cc | 9 +++++++-- tests/functional/lang/eval-okay-builtins-dirOf.exp | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0e0b23bca..ffd9f6996 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1937,8 +1937,13 @@ static void prim_dirOf(EvalState & state, const PosIdx pos, Value ** args, Value NixStringContext context; auto path = state.coerceToString( pos, *args[0], context, "while evaluating the first argument passed to 'builtins.dirOf'", false, false); - auto dir = dirOf(*path); - v.mkString(dir, context); + auto pos = path->rfind('/'); + if (pos == path->npos) + v.mkStringMove(".", context); + else if (pos == 0) + v.mkStringMove("/", context); + else + v.mkString(path->substr(0, pos), context); } } diff --git a/tests/functional/lang/eval-okay-builtins-dirOf.exp b/tests/functional/lang/eval-okay-builtins-dirOf.exp index 1130b1510..e0093e93a 100644 --- a/tests/functional/lang/eval-okay-builtins-dirOf.exp +++ b/tests/functional/lang/eval-okay-builtins-dirOf.exp @@ -1 +1 @@ -{ pathDoesntExistNested1 = /totallydoesntexistreally; pathDoesntExistNested2 = /totallydoesntexistreally/subdir1; pathDoesntExistRoot = /; pathRoot = /; stringEmpty = "."; stringMultipleSeps = "a"; stringNoSep = "."; stringRoot = "/"; stringRootA = "/"; stringRootSlash = "//"; stringRootSlashSlash = "///"; stringSingleDir = "a"; stringWithDot = "a/b/c/."; stringWithDotAndDotDot = "a/b/c/../."; stringWithDotAndDotDotSep2 = "a/b/c/../."; stringWithDotDot = "a/b/c/.."; stringWithDotDotSep2 = "a/b/c/.."; stringWithDotSep2 = "a/b/c/."; } +{ pathDoesntExistNested1 = /totallydoesntexistreally; pathDoesntExistNested2 = /totallydoesntexistreally/subdir1; pathDoesntExistRoot = /; pathRoot = /; stringEmpty = "."; stringMultipleSeps = "a//"; stringNoSep = "."; stringRoot = "/"; stringRootA = "/"; stringRootSlash = "/"; stringRootSlashSlash = "//"; stringSingleDir = "a"; stringWithDot = "a/b/c/."; stringWithDotAndDotDot = "a/b/c/../."; stringWithDotAndDotDotSep2 = "a/b/c/.././"; stringWithDotDot = "a/b/c/.."; stringWithDotDotSep2 = "a/b/c/../"; stringWithDotSep2 = "a/b/c/./"; }