From 212963e197436d1655f242dc3a805e91be75514f 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 2225a3a63d02c3960d63e0becfc230cc193a4e77 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 6d8c8477d..6710efca4 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1867,8 +1867,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/./"; }