query
On this page

symlinkJoin

pkgs.symlinkJoin

Docs pulled from | This Revision | 10 minutes ago


TODO: Deduplicate this documentation. More docs in doc/build-helpers/trivial-build-helpers.chapter.md See https://nixos.org/manual/nixpkgs/unstable/#trivial-builder-symlinkJoin

Create a forest of symlinks to the files in paths.

This creates a single derivation that replicates the directory structure of all the input paths.

BEWARE: it may not "work right" when the passed paths contain symlinks to directories.

Examples

symlinkJoin usage example

# adds symlinks of hello to current build.
symlinkJoin { name = "myhello"; paths = [ pkgs.hello ]; }

# adds symlinks of hello and stack to current build and prints "links added"
symlinkJoin { name = "myexample"; paths = [ pkgs.hello pkgs.stack ]; postBuild = "echo links added"; }

This creates a derivation with a directory structure like the following:

/nix/store/sglsr5g079a5235hy29da3mq3hv8sjmm-myexample
|-- bin
|   |-- hello -> /nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10/bin/hello
|   `-- stack -> /nix/store/6lzdpxshx78281vy056lbk553ijsdr44-stack-2.1.3.1/bin/stack
`-- share
    |-- bash-completion
    |   `-- completions
    |       `-- stack -> /nix/store/6lzdpxshx78281vy056lbk553ijsdr44-stack-2.1.3.1/share/bash-completion/completions/stack
    |-- fish
    |   `-- vendor_completions.d
    |       `-- stack.fish -> /nix/store/6lzdpxshx78281vy056lbk553ijsdr44-stack-2.1.3.1/share/fish/vendor_completions.d/stack.fish
...

To create a directory structure from a specific subdirectory of input `paths` instead of their full trees,
you can either append the subdirectory path to each input path, or use the `stripPrefix` argument to
remove the common prefix during linking.



# create symlinks of tmpfiles.d rules from multiple packages
symlinkJoin { name = "tmpfiles.d"; paths = [ pkgs.lvm2 pkgs.nix ]; stripPrefix = "/lib/tmpfiles.d"; }

This creates a derivation with a directory structure like the following:

/nix/store/m5s775yicb763hfa133jwml5hwmwzv14-tmpfiles.d
|-- lvm2.conf -> /nix/store/k6js0l5f0zpvrhay49579fj939j77p2w-lvm2-2.03.29/lib/tmpfiles.d/lvm2.conf
`-- nix-daemon.conf -> /nix/store/z4v2s3s3y79fmabhps5hakb3c5dwaj5a-nix-1.33.7/lib/tmpfiles.d/nix-daemon.conf

By default, packages that don't contain the specified subdirectory are silently skipped.
Set `failOnMissing = true` to make the build fail if any input package is missing the subdirectory
(this is the default behavior when not using stripPrefix).

symlinkJoin and linkFarm are similar functions, but they output
derivations with different structure.

symlinkJoin is used to create a derivation with a familiar directory
structure (top-level bin/, share/, etc), but with all actual files being symlinks to
the files in the input derivations.

symlinkJoin is used many places in nixpkgs to create a single derivation
that appears to contain binaries, libraries, documentation, etc from
multiple input derivations.

linkFarm is instead used to create a simple derivation with symlinks to
other derivations.  A derivation created with linkFarm is often used in CI
as a easy way to build multiple derivations at once.

Noogle detected

Implementation

The following is the current implementation of this function.

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 /.

      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
            f path
        ) paths;
      args =
        removeAttrs args_ [
          "name"
          "postBuild"
          "stripPrefix"
          "paths"
          "failOnMissing"
        ]
        // {
          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}
    '';