about summary refs log tree commit diff
path: root/nix
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-05-02T10·32+0200
committertazjin <tazjin@tvl.su>2022-05-02T23·34+0000
commit0ce396bdab902be626a9055e991551ce6120551e (patch)
treebf834c0bf9bff9886942e2e39d4690df5e704478 /nix
parent419dd2a7aa4523bf89f1ff2d4e876f6b85a3bacb (diff)
feat(nix/lazy-deps): Add function to generate lazy binary dispatcher r/3996
There is a reoccuring problem in readTree-type repositories that use
nix-shell, where evaluation of the full set of dependencies that
should be made available to users takes a noticeable amount of time,
slowing down operations when `direnv` is involved.

In depot, we have so far fixed this by maintaining a manual `//bin`
directory which contains a set of symlinks to a central dispatch
script that can dispatch to various tools in depot lazily.

This script can instead be generated ad-hoc by Nix (pretty fast if we
can make assumptions like `git` and `nix-build` existing on user's
machines already) and added to $PATH.

The function introduced in this commit implements the logic for that.
The structure of the script is based on the existing
`//bin/__dispatch`.

This does not yet switch depot's envrc to use this new method of
installing dependencies lazily.

Change-Id: I92efcd9bb6aa51aa2709ad910a464e9dac97ee89
Reviewed-on: https://cl.tvl.fyi/c/depot/+/5512
Tested-by: BuildkiteCI
Reviewed-by: ezemtsov <eugene.zemtsov@gmail.com>
Diffstat (limited to 'nix')
-rw-r--r--nix/lazy-deps/default.nix75
1 files changed, 75 insertions, 0 deletions
diff --git a/nix/lazy-deps/default.nix b/nix/lazy-deps/default.nix
new file mode 100644
index 000000000000..3cce48d8a53e
--- /dev/null
+++ b/nix/lazy-deps/default.nix
@@ -0,0 +1,75 @@
+# Helper function to synthesize a directory of "lazy-built" binaries
+# that can be added to $PATH inside of a repository.
+#
+# Using this, a Nix shell environment in some repository can contain
+# several slow-to-build commands without blowing up evaluation and
+# build time whenever the shell is loaded.
+#
+# Note that the generated script is deliberately impure to speed up
+# evaluation, and expects both `git` and `nix-build` to exist in the
+# user's $PATH. If required, this can be done in the shell
+# configuration invoking this function.
+{ pkgs, ... }:
+
+let
+  inherit (builtins) attrNames attrValues mapAttrs;
+  inherit (pkgs.lib) concatStringsSep;
+
+  # Create the case statement for a command invocations, optionally
+  # overriding the `TARGET_TOOL` variable.
+  invoke = name: { attr, cmd ? null }: ''
+    ${name})
+      attr="${attr}"
+      ${if cmd != null then "TARGET_TOOL=\"${cmd}\"\n;;" else ";;"}
+  '';
+
+  # Create command to symlink to the dispatch script for each tool.
+  link = name: "ln -s $target $out/bin/${name}";
+
+  invocations = tools: concatStringsSep "\n" (attrValues (mapAttrs invoke tools));
+in
+
+# Attribute set of tools that should be lazily-added to the $PATH.
+
+  # The name of each attribute is used as the command name (on $PATH).
+  # It must contain the keys 'attr' (containing the Nix attribute path
+  # to the tool's derivation from the top-level), and may optionally
+  # contain the key 'cmd' to override the name of the binary inside the
+  # derivation.
+tools:
+
+pkgs.writeTextFile {
+  name = "lazy-dispatch";
+  executable = true;
+  destination = "/bin/__dispatch";
+
+  text = ''
+    #!${pkgs.runtimeShell}
+    set -ue
+
+    if ! type git>/dev/null || ! type nix-build>/dev/null; then
+      echo "The 'git' and 'nix-build' commands must be available." >&2
+      exit 127
+    fi
+
+    readonly REPO_ROOT=$(git rev-parse --show-toplevel)
+    TARGET_TOOL=$(basename "$0")
+
+    case "''${TARGET_TOOL}" in
+    ${invocations tools}
+    *)
+      echo "''${TARGET_TOOL} is currently not installed in this repository." >&2
+      exit 127
+      ;;
+    esac
+
+    result=$(nix-build --no-out-link --attr "''${attr}" "''${REPO_ROOT}")
+    PATH="''${result}/bin:$PATH"
+    exec "''${TARGET_TOOL}" "''${@}"
+  '';
+
+  checkPhase = ''
+    ${pkgs.stdenv.shellDryRun} "$target"
+    ${concatStringsSep "\n" (map link (attrNames tools))}
+  '';
+}