From 0ce396bdab902be626a9055e991551ce6120551e Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 2 May 2022 12:32:01 +0200 Subject: feat(nix/lazy-deps): Add function to generate lazy binary dispatcher 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 --- nix/lazy-deps/default.nix | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 nix/lazy-deps/default.nix (limited to 'nix') diff --git a/nix/lazy-deps/default.nix b/nix/lazy-deps/default.nix new file mode 100644 index 0000000000..3cce48d8a5 --- /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))} + ''; +} -- cgit 1.4.1