summary refs log tree commit diff
path: root/pkgs/build-support/setup-hooks
diff options
context:
space:
mode:
authorLily Ballard <lily@sb.org>2019-07-20 19:53:19 -0700
committerRobert Helgesson <robert@rycee.net>2019-09-04 23:19:17 +0200
commit43dade238f39fc3edb6c6be6d318e4f7f990f971 (patch)
treee3ffa6d22b5a7f0a847eb14fd1e0799b22beb665 /pkgs/build-support/setup-hooks
parent37e333af9a18814537b872745d9b1c580c1b1a9d (diff)
downloadnixpkgs-43dade238f39fc3edb6c6be6d318e4f7f990f971.tar
nixpkgs-43dade238f39fc3edb6c6be6d318e4f7f990f971.tar.gz
nixpkgs-43dade238f39fc3edb6c6be6d318e4f7f990f971.tar.bz2
nixpkgs-43dade238f39fc3edb6c6be6d318e4f7f990f971.tar.lz
nixpkgs-43dade238f39fc3edb6c6be6d318e4f7f990f971.tar.xz
nixpkgs-43dade238f39fc3edb6c6be6d318e4f7f990f971.tar.zst
nixpkgs-43dade238f39fc3edb6c6be6d318e4f7f990f971.zip
installShellFiles: init (#65211)
This is a new package that provides a shell hook to make it easy to
declare manpages and shell completions in a manner that doesn't require
remembering where to actually install them. Basic usage looks like

  { stdenv, installShellFiles, ... }:
  stdenv.mkDerivation {
    # ...
    nativeBuildInputs = [ installShellFiles ];
    postInstall = ''
      installManPage doc/foobar.1
      installShellCompletion --bash share/completions/foobar.bash
      installShellCompletion --fish share/completions/foobar.fish
      installShellCompletion --zsh share/completions/_foobar
    '';
    # ...
  }

See source comments for more details on the functions.
Diffstat (limited to 'pkgs/build-support/setup-hooks')
-rw-r--r--pkgs/build-support/setup-hooks/install-shell-files.sh165
1 files changed, 165 insertions, 0 deletions
diff --git a/pkgs/build-support/setup-hooks/install-shell-files.sh b/pkgs/build-support/setup-hooks/install-shell-files.sh
new file mode 100644
index 00000000000..e0ea1f7f30a
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/install-shell-files.sh
@@ -0,0 +1,165 @@
+#!/bin/bash
+# Setup hook for the `installShellFiles` package.
+#
+# Example usage in a derivation:
+#
+#   { …, installShellFiles, … }:
+#   stdenv.mkDerivation {
+#     …
+#     nativeBuildInputs = [ installShellFiles ];
+#     postInstall = ''
+#       installManPage share/doc/foobar.1
+#       installShellCompletion share/completions/foobar.{bash,fish,zsh}
+#     '';
+#     …
+#   }
+#
+# See comments on each function for more details.
+
+# installManPage <path> [...<path>]
+#
+# Each argument is checked for its man section suffix and installed into the appropriate
+# share/man<n>/ directory. The function returns an error if any paths don't have the man section
+# suffix (with optional .gz compression).
+installManPage() {
+    local path
+    for path in "$@"; do
+        if (( "${NIX_DEBUG:-0}" >= 1 )); then
+            echo "installManPage: installing $path"
+        fi
+        if test -z "$path"; then
+            echo "installManPage: error: path cannot be empty" >&2
+            return 1
+        fi
+        local basename
+        basename=$(stripHash "$path") # use stripHash in case it's a nix store path
+        local trimmed=${basename%.gz} # don't get fooled by compressed manpages
+        local suffix=${trimmed##*.}
+        if test -z "$suffix" -o "$suffix" = "$trimmed"; then
+            echo "installManPage: error: path missing manpage section suffix: $path" >&2
+            return 1
+        fi
+        local outRoot
+        if test "$suffix" = 3; then
+            outRoot=${!outputDevman:?}
+        else
+            outRoot=${!outputMan:?}
+        fi
+        install -Dm644 -T "$path" "${outRoot}/share/man/man$suffix/$basename" || return
+    done
+}
+
+# installShellCompletion [--bash|--fish|--zsh] ([--name <name>] <path>)...
+#
+# Each path is installed into the appropriate directory for shell completions for the given shell.
+# If one of `--bash`, `--fish`, or `--zsh` is given the path is assumed to belong to that shell.
+# Otherwise the file extension will be examined to pick a shell. If the shell is unknown a warning
+# will be logged and the command will return a non-zero status code after processing any remaining
+# paths. Any of the shell flags will affect all subsequent paths (unless another shell flag is
+# given).
+#
+# If the shell completion needs to be renamed before installing the optional `--name <name>` flag
+# may be given. Any name provided with this flag only applies to the next path.
+#
+# For zsh completions, if the `--name` flag is not given, the path will be automatically renamed
+# such that `foobar.zsh` becomes `_foobar`.
+#
+# This command accepts multiple shell flags in conjunction with multiple paths if you wish to
+# install them all in one command:
+#
+#   installShellCompletion share/completions/foobar.{bash,fish} --zsh share/completions/_foobar
+#
+# However it may be easier to read if each shell is split into its own invocation, especially when
+# renaming is involved:
+#
+#   installShellCompletion --bash --name foobar.bash share/completions.bash
+#   installShellCompletion --fish --name foobar.fish share/completions.fish
+#   installShellCompletion --zsh --name _foobar share/completions.zsh
+#
+# If any argument is `--` the remaining arguments will be treated as paths.
+installShellCompletion() {
+    local shell='' name='' retval=0 parseArgs=1 arg
+    while { arg=$1; shift; }; do
+        # Parse arguments
+        if (( parseArgs )); then
+            case "$arg" in
+            --bash|--fish|--zsh)
+                shell=${arg#--}
+                continue;;
+            --name)
+                name=$1
+                shift || {
+                    echo 'installShellCompletion: error: --name flag expected an argument' >&2
+                    return 1
+                }
+                continue;;
+            --name=*)
+                # treat `--name=foo` the same as `--name foo`
+                name=${arg#--name=}
+                continue;;
+            --?*)
+                echo "installShellCompletion: warning: unknown flag ${arg%%=*}" >&2
+                retval=2
+                continue;;
+            --)
+                # treat remaining args as paths
+                parseArgs=0
+                continue;;
+            esac
+        fi
+        if (( "${NIX_DEBUG:-0}" >= 1 )); then
+            echo "installShellCompletion: installing $arg${name:+ as $name}"
+        fi
+        # if we get here, this is a path
+        # Identify shell
+        local basename
+        basename=$(stripHash "$arg")
+        local curShell=$shell
+        if [[ -z "$curShell" ]]; then
+            # auto-detect the shell
+            case "$basename" in
+            ?*.bash) curShell=bash;;
+            ?*.fish) curShell=fish;;
+            ?*.zsh) curShell=zsh;;
+            *)
+                if [[ "$basename" = _* && "$basename" != *.* ]]; then
+                    # probably zsh
+                    echo "installShellCompletion: warning: assuming path \`$arg' is zsh; please specify with --zsh" >&2
+                    curShell=zsh
+                else
+                    echo "installShellCompletion: warning: unknown shell for path: $arg" >&2
+                    retval=2
+                    continue
+                fi;;
+            esac
+        fi
+        # Identify output path
+        local outName sharePath
+        outName=${name:-$basename}
+        case "$curShell" in
+        bash) sharePath=bash-completion/completions;;
+        fish) sharePath=fish/vendor_completions.d;;
+        zsh)
+            sharePath=zsh/site-functions
+            # only apply automatic renaming if we didn't have a manual rename
+            if test -z "$name"; then
+                # convert a name like `foo.zsh` into `_foo`
+                outName=${outName%.zsh}
+                outName=_${outName#_}
+            fi;;
+        *)
+            # Our list of shells is out of sync with the flags we accept or extensions we detect.
+            echo 'installShellCompletion: internal error' >&2
+            return 1;;
+        esac
+        # Install file
+        install -Dm644 -T "$arg" "${!outputBin:?}/share/$sharePath/$outName" || return
+        # Clear the name, it only applies to one path
+        name=
+    done
+    if [[ -n "$name" ]]; then
+        echo 'installShellCompletion: error: --name flag given with no path' >&2
+        return 1
+    fi
+    return $retval
+}