From 89ee800e37e7910a8933f642e2044abe9ed92a5b Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Tue, 10 Feb 2026 16:38:50 -0500 Subject: [PATCH 1/7] symlinkJoin: to extendMkDerivation, naively naive translation inlining runCommandWith --- .../trivial-builders/default.nix | 132 ++++++++++-------- 1 file changed, 72 insertions(+), 60 deletions(-) diff --git a/pkgs/build-support/trivial-builders/default.nix b/pkgs/build-support/trivial-builders/default.nix index ac834203cf91..ab372a692ec8 100644 --- a/pkgs/build-support/trivial-builders/default.nix +++ b/pkgs/build-support/trivial-builders/default.nix @@ -572,69 +572,81 @@ rec { other derivations. A derivation created with linkFarm is often used in CI as a easy way to build multiple derivations at once. */ - symlinkJoin = - args_@{ - name ? - assert lib.assertMsg ( - args_ ? pname && args_ ? version - ) "symlinkJoin requires either a `name` OR `pname` and `version`"; - "${args_.pname}-${args_.version}", - paths, - stripPrefix ? "", - preferLocalBuild ? true, - allowSubstitutes ? false, - postBuild ? "", - failOnMissing ? stripPrefix == "", - ... - }: - assert lib.assertMsg (stripPrefix != "" -> (hasPrefix "/" stripPrefix && stripPrefix != "/")) '' - stripPrefix must be either an empty string (disable stripping behavior), or relative path prefixed with /. + symlinkJoin = lib.extendMkDerivation { + constructDrv = stdenvNoCC.mkDerivation; - Ensure that the path starts with / and specifies path to the subdirectory. - ''; + extendDrvArgs = + finalAttrs: + args_@{ + name ? + assert lib.assertMsg ( + args_ ? pname && args_ ? version + ) "symlinkJoin requires either a `name` OR `pname` and `version`"; + "${args_.pname}-${args_.version}", + paths, + stripPrefix ? "", + preferLocalBuild ? true, + allowSubstitutes ? false, + postBuild ? "", + failOnMissing ? stripPrefix == "", + ... + }: + assert lib.assertMsg (stripPrefix != "" -> (hasPrefix "/" stripPrefix && stripPrefix != "/")) '' + stripPrefix must be either an empty string (disable stripping behavior), or relative path prefixed with /. - let - mapPaths = - f: paths: - map ( - path: - if path == null then - null - else if isList path then - mapPaths f path - else - f path - ) paths; - args = - removeAttrs args_ [ - "name" - "postBuild" - "stripPrefix" - "paths" - "failOnMissing" - ] - // { - # Allow getting the proper position of the output derivation. - # Since one of these are required, it should be fairly accurate. - pos = - if args_ ? pname then - builtins.unsafeGetAttrPos "pname" args_ + Ensure that the path starts with / and specifies path to the subdirectory. + ''; + let + mapPaths = + f: paths: + map ( + path: + if path == null then + null + else if isList path then + mapPaths f path else - builtins.unsafeGetAttrPos "name" args_; - inherit preferLocalBuild allowSubstitutes; - paths = mapPaths (path: "${path}${stripPrefix}") paths; - passAsFile = [ "paths" ]; - }; # pass the defaults - in - runCommand name args '' - mkdir -p $out - for i in $(cat $pathsPath); do - ${optionalString (!failOnMissing) "if test -d $i; then "}${lndir}/bin/lndir -silent $i $out${ - optionalString (!failOnMissing) "; fi" - } - done - ${postBuild} - ''; + f path + ) paths; + drvArgs = + removeAttrs args_ [ + "name" + "postBuild" + "stripPrefix" + "paths" + "failOnMissing" + ] + // { + inherit preferLocalBuild allowSubstitutes; + paths = mapPaths (path: "${path}${stripPrefix}") paths; + }; # pass the defaults + in + { + enableParallelBuilding = true; + inherit name; + passAsFile = [ + "buildCommand" + "paths" + ]; + buildCommand = '' + mkdir -p $out + for i in $(cat $pathsPath); do + ${optionalString (!failOnMissing) "if test -d $i; then "}${lndir}/bin/lndir -silent $i $out${ + optionalString (!failOnMissing) "; fi" + } + done + ${postBuild} + ''; + } + // lib.optionalAttrs (!drvArgs ? meta) { + pos = + let + args = builtins.attrNames drvArgs; + in + if builtins.length args > 0 then builtins.unsafeGetAttrPos (builtins.head args) drvArgs else null; + } + // drvArgs; + }; # TODO: move linkFarm docs to the Nixpkgs manual /* From 31af889a59eb63e7f7d839ab41b71becea60d5e5 Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:02:09 -0500 Subject: [PATCH 2/7] symlinkJoin: use `excludeDrvArgNames` for attr removal --- .../trivial-builders/default.nix | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/pkgs/build-support/trivial-builders/default.nix b/pkgs/build-support/trivial-builders/default.nix index ab372a692ec8..12f0482da90a 100644 --- a/pkgs/build-support/trivial-builders/default.nix +++ b/pkgs/build-support/trivial-builders/default.nix @@ -575,6 +575,14 @@ rec { symlinkJoin = lib.extendMkDerivation { constructDrv = stdenvNoCC.mkDerivation; + excludeDrvArgNames = [ + "name" + "postBuild" + "stripPrefix" + "paths" + "failOnMissing" + ]; + extendDrvArgs = finalAttrs: args_@{ @@ -608,26 +616,15 @@ rec { else f path ) paths; - drvArgs = - removeAttrs args_ [ - "name" - "postBuild" - "stripPrefix" - "paths" - "failOnMissing" - ] - // { - inherit preferLocalBuild allowSubstitutes; - paths = mapPaths (path: "${path}${stripPrefix}") paths; - }; # pass the defaults in { enableParallelBuilding = true; - inherit name; + inherit name allowSubstitutes preferLocalBuild; passAsFile = [ "buildCommand" "paths" ]; + paths = mapPaths (path: "${path}${stripPrefix}") paths; buildCommand = '' mkdir -p $out for i in $(cat $pathsPath); do @@ -638,14 +635,13 @@ rec { ${postBuild} ''; } - // lib.optionalAttrs (!drvArgs ? meta) { + // lib.optionalAttrs (!args_ ? meta) { pos = let - args = builtins.attrNames drvArgs; + args = builtins.attrNames args_; in - if builtins.length args > 0 then builtins.unsafeGetAttrPos (builtins.head args) drvArgs else null; - } - // drvArgs; + if builtins.length args > 0 then builtins.unsafeGetAttrPos (builtins.head args) args_ else null; + }; }; # TODO: move linkFarm docs to the Nixpkgs manual From 72b1b02a6b8092d61e1632188f52075b42512b1f Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:07:45 -0500 Subject: [PATCH 3/7] symlinkJoin: undo eta-expansion of `mapPaths` --- pkgs/build-support/trivial-builders/default.nix | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkgs/build-support/trivial-builders/default.nix b/pkgs/build-support/trivial-builders/default.nix index 12f0482da90a..a27deb49740f 100644 --- a/pkgs/build-support/trivial-builders/default.nix +++ b/pkgs/build-support/trivial-builders/default.nix @@ -576,7 +576,6 @@ rec { constructDrv = stdenvNoCC.mkDerivation; excludeDrvArgNames = [ - "name" "postBuild" "stripPrefix" "paths" @@ -606,7 +605,7 @@ rec { ''; let mapPaths = - f: paths: + f: map ( path: if path == null then @@ -615,7 +614,7 @@ rec { mapPaths f path else f path - ) paths; + ); in { enableParallelBuilding = true; From 6924e4ab3a03b1da20d348d4db926a63b916c133 Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:18:29 -0500 Subject: [PATCH 4/7] symlinkJoin: args_ -> args --- pkgs/build-support/trivial-builders/default.nix | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pkgs/build-support/trivial-builders/default.nix b/pkgs/build-support/trivial-builders/default.nix index a27deb49740f..a8f6c34cd20a 100644 --- a/pkgs/build-support/trivial-builders/default.nix +++ b/pkgs/build-support/trivial-builders/default.nix @@ -584,12 +584,12 @@ rec { extendDrvArgs = finalAttrs: - args_@{ + args@{ name ? assert lib.assertMsg ( - args_ ? pname && args_ ? version + args ? pname && args ? version ) "symlinkJoin requires either a `name` OR `pname` and `version`"; - "${args_.pname}-${args_.version}", + "${args.pname}-${args.version}", paths, stripPrefix ? "", preferLocalBuild ? true, @@ -634,12 +634,15 @@ rec { ${postBuild} ''; } - // lib.optionalAttrs (!args_ ? meta) { + // lib.optionalAttrs (!args ? meta) { pos = let - args = builtins.attrNames args_; + argNames = builtins.attrNames args; in - if builtins.length args > 0 then builtins.unsafeGetAttrPos (builtins.head args) args_ else null; + if builtins.length argNames > 0 then + builtins.unsafeGetAttrPos (builtins.head argNames) args + else + null; }; }; From 4ac62dfc4c5485531bacff1990e43f25deb2b229 Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:47:15 -0500 Subject: [PATCH 5/7] symlinkJoin: use final pname and version --- pkgs/build-support/trivial-builders/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/build-support/trivial-builders/default.nix b/pkgs/build-support/trivial-builders/default.nix index a8f6c34cd20a..83f387bbff57 100644 --- a/pkgs/build-support/trivial-builders/default.nix +++ b/pkgs/build-support/trivial-builders/default.nix @@ -587,9 +587,9 @@ rec { args@{ name ? assert lib.assertMsg ( - args ? pname && args ? version + finalAttrs ? pname && finalAttrs ? version ) "symlinkJoin requires either a `name` OR `pname` and `version`"; - "${args.pname}-${args.version}", + "${finalAttrs.pname}-${finalAttrs.version}", paths, stripPrefix ? "", preferLocalBuild ? true, From 48de4509f80e1fc145d57f31ae3f5341ec3d16b3 Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:21:27 -0500 Subject: [PATCH 6/7] kak-tree-sitter: migrate to finalAttrs --- pkgs/by-name/ka/kak-tree-sitter/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ka/kak-tree-sitter/package.nix b/pkgs/by-name/ka/kak-tree-sitter/package.nix index e80edb2f369e..1eb57f91f955 100644 --- a/pkgs/by-name/ka/kak-tree-sitter/package.nix +++ b/pkgs/by-name/ka/kak-tree-sitter/package.nix @@ -6,10 +6,10 @@ kak-tree-sitter-unwrapped, }: -symlinkJoin rec { +symlinkJoin (finalAttrs: { pname = lib.replaceStrings [ "-unwrapped" ] [ "" ] kak-tree-sitter-unwrapped.pname; inherit (kak-tree-sitter-unwrapped) version; - name = "${pname}-${version}"; + name = "${finalAttrs.pname}-${finalAttrs.version}"; paths = [ kak-tree-sitter-unwrapped ]; nativeBuildInputs = [ makeWrapper ]; @@ -24,4 +24,4 @@ symlinkJoin rec { ''; inherit (kak-tree-sitter-unwrapped) meta; -} +}) From e0ce766f9aefe62a13a2ce60be93413c6c73442c Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Mon, 16 Feb 2026 07:53:03 -0800 Subject: [PATCH 7/7] symlinkJoin: use targeted pos for pname/name Use the more precise pos computation that specifically targets pname or name, rather than the generic approach inherited from runCommandWith that uses the alphabetically-first attribute. Since one of pname or name is always required, this reliably points to the call site. --- pkgs/build-support/trivial-builders/default.nix | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pkgs/build-support/trivial-builders/default.nix b/pkgs/build-support/trivial-builders/default.nix index 83f387bbff57..3f3c1bc3a0bc 100644 --- a/pkgs/build-support/trivial-builders/default.nix +++ b/pkgs/build-support/trivial-builders/default.nix @@ -636,13 +636,10 @@ rec { } // lib.optionalAttrs (!args ? meta) { pos = - let - argNames = builtins.attrNames args; - in - if builtins.length argNames > 0 then - builtins.unsafeGetAttrPos (builtins.head argNames) args + if args ? pname then + builtins.unsafeGetAttrPos "pname" args else - null; + builtins.unsafeGetAttrPos "name" args; }; };