diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b3116856613..58001c6142f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2376,6 +2376,26 @@ static RegisterPrimOp primop_readFileType({ .fun = prim_readFileType, }); +/* Read the target of a symlink. */ +static void prim_readSymlink(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + const auto path = state.realisePath(pos, *args[0], SymlinkResolution::Ancestors); + const auto target = path.readLink(); + const auto parent = path.parent(); + v.mkPath(SourcePath(path.accessor, CanonPath(target, parent.path)), state.mem); +} + +static RegisterPrimOp primop_readSymlink({ + .name = "__readSymlink", + .args = {"path"}, + .doc = R"( + Return the target of the symlink at *path*. + + If *path* does not refer to a symlink, an error is thrown. + )", + .fun = prim_readSymlink, +}); + /* Read a directory (without . or ..) */ static void prim_readDir(EvalState & state, const PosIdx pos, Value ** args, Value & v) { diff --git a/tests/functional/lang/eval-fail-readSymlink-not-a-symlink.err.exp b/tests/functional/lang/eval-fail-readSymlink-not-a-symlink.err.exp new file mode 100644 index 00000000000..1610db4536b --- /dev/null +++ b/tests/functional/lang/eval-fail-readSymlink-not-a-symlink.err.exp @@ -0,0 +1 @@ +error: filesystem error: read_symlink: not a symlink [/pwd/lang/readDir/bar] diff --git a/tests/functional/lang/eval-fail-readSymlink-not-a-symlink.nix b/tests/functional/lang/eval-fail-readSymlink-not-a-symlink.nix new file mode 100644 index 00000000000..a2a04dfb101 --- /dev/null +++ b/tests/functional/lang/eval-fail-readSymlink-not-a-symlink.nix @@ -0,0 +1 @@ +builtins.readSymlink ./readDir/bar diff --git a/tests/functional/lang/eval-fail-readSymlink-not-a-symlink.postprocess b/tests/functional/lang/eval-fail-readSymlink-not-a-symlink.postprocess new file mode 100644 index 00000000000..644651dadbd --- /dev/null +++ b/tests/functional/lang/eval-fail-readSymlink-not-a-symlink.postprocess @@ -0,0 +1,11 @@ +# shellcheck shell=bash +set -euo pipefail +testcaseBasename=$1 + +# Normalize platform-specific filesystem error messages +sed -i "$testcaseBasename.err" \ + -e 's/filesystem error: in read_symlink:/filesystem error: read_symlink:/' \ + -e 's/Invalid argument/not a symlink/' \ + -e 's/Not a symbolic link/not a symlink/' \ + -e 's/\["\([^"]*\)"\]/[\1]/' \ + ; diff --git a/tests/functional/lang/eval-okay-readSymlink.exp b/tests/functional/lang/eval-okay-readSymlink.exp new file mode 100644 index 00000000000..f2c5cdd1d10 --- /dev/null +++ b/tests/functional/lang/eval-okay-readSymlink.exp @@ -0,0 +1 @@ +{ ldir = /pwd/lang/readDir/foo; linked = /pwd/lang/readDir/foo/git-hates-directories; } diff --git a/tests/functional/lang/eval-okay-readSymlink.nix b/tests/functional/lang/eval-okay-readSymlink.nix new file mode 100644 index 00000000000..da19a51e0e3 --- /dev/null +++ b/tests/functional/lang/eval-okay-readSymlink.nix @@ -0,0 +1,4 @@ +{ + ldir = builtins.readSymlink ./readDir/ldir; + linked = builtins.readSymlink ./readDir/linked; +}