|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Usage: dapp --nix-run PKG COMMAND... |
| 3 | +# Example: |
| 4 | +# $ dapp --nix-run go-ethereum geth --version |
| 5 | +# |
| 6 | +# Runs a command with the binaries from a named Nix package in PATH. |
| 7 | + |
| 8 | +# It's basically an implementation of nix-shell(1) but simpler and |
| 9 | +# without actually starting a subshell, and using ~/.dapp/nix as an |
| 10 | +# "indirect GC root." |
| 11 | + |
| 12 | +set -e |
| 13 | + |
| 14 | +have() { command -v "$1" >/dev/null; } |
| 15 | +{ have nix-channel && have nix-env && have nix-shell; } || { |
| 16 | + echo >&2 "${0##*/}: error: The Nix package manager is required." |
| 17 | + echo >&2 "${0##*/}: error: See https://dapp.tools for installation instructions." |
| 18 | + exit 1 |
| 19 | +} |
| 20 | + |
| 21 | +channel=$HOME/.nix-defexpr/channels/dapphub |
| 22 | + |
| 23 | +if [[ ! -d "$channel" ]]; then |
| 24 | + echo >&2 "${0##*/}: DappHub Nix channel not present; adding..." |
| 25 | + ( set -x; nix-channel --add https://nix.dapphub.com/pkgs/dapphub ) |
| 26 | + ( set -x; nix-channel --update ) |
| 27 | +fi |
| 28 | + |
| 29 | +joinpaths() { printf "%s" "$*" | sed 's/ /:/g' ; } |
| 30 | + |
| 31 | +# Let's have a custom directory for semi-temporary files related to |
| 32 | +# Nix packages. It will contain symlinks to store paths. |
| 33 | +pkgs=$HOME/.dapp/nix/pkgs |
| 34 | +mkdir -p "$pkgs" |
| 35 | + |
| 36 | +attr="$1"; shift |
| 37 | + |
| 38 | +# First step in Nix jargon: instantiate the package as a |
| 39 | +# derivation file. |
| 40 | +# |
| 41 | +# A package is a fully evaluated Nix expression which describes a |
| 42 | +# build product -- a "derivation" which is then saved as a file in |
| 43 | +# the store. |
| 44 | +drvpath=$(nix-instantiate --indirect --add-root "$pkgs"/"$attr".drv \ |
| 45 | + "$channel" -A "$attr") |
| 46 | +drvpath=$(sed 's/!.*$//' <<<"$drvpath") |
| 47 | + |
| 48 | +# The derivation will have a number of output paths, either of which |
| 49 | +# might contain the ./bin that we're interested in, so we'll accumulate |
| 50 | +# an array of PATH entries to use. |
| 51 | +paths=() |
| 52 | + |
| 53 | +# This while loop gets its stdin from a Nix command; see below. |
| 54 | +while read output; do |
| 55 | + |
| 56 | + name=$(basename "$output") |
| 57 | + path="$HOME/.dapp/nix/pkgs/$name" |
| 58 | + paths+=("$path"/bin) |
| 59 | + |
| 60 | + if [[ ! -d "$path" ]]; then |
| 61 | + # The derivation's output paths must be "realised", which in our |
| 62 | + # case should mean downloading them from the binary cache. |
| 63 | + echo >&2 "${0##*/}: Need Nix package: $name" |
| 64 | + nix-store \ |
| 65 | + -Q --indirect --add-root ~/.dapp/nix/pkgs/"$name" \ |
| 66 | + --realise "$output" >/dev/null \ |
| 67 | + 2> >(sed 's/^/nix: /' >&2) |
| 68 | + fi |
| 69 | + |
| 70 | +done < <(nix-store --query "$drvpath") |
| 71 | + |
| 72 | +PATH=$(joinpaths "${paths[@]}"):$PATH "$@" |
0 commit comments