summary refs log tree commit diff
path: root/pkgs/build-support/setup-hooks
diff options
context:
space:
mode:
authorLily Ballard <lilyball@twitch.tv>2020-03-28 14:55:06 -0700
committerLily Ballard <lily@sb.org>2020-10-08 15:08:40 -0700
commit7e1e8543fce20baafd76b7b45d8f36a47d41090e (patch)
treea65fb3146693a9690015651773c5f738088736dd /pkgs/build-support/setup-hooks
parent506a936e5f22786158e2a50408c697c050bc61ed (diff)
downloadnixpkgs-7e1e8543fce20baafd76b7b45d8f36a47d41090e.tar
nixpkgs-7e1e8543fce20baafd76b7b45d8f36a47d41090e.tar.gz
nixpkgs-7e1e8543fce20baafd76b7b45d8f36a47d41090e.tar.bz2
nixpkgs-7e1e8543fce20baafd76b7b45d8f36a47d41090e.tar.lz
nixpkgs-7e1e8543fce20baafd76b7b45d8f36a47d41090e.tar.xz
nixpkgs-7e1e8543fce20baafd76b7b45d8f36a47d41090e.tar.zst
nixpkgs-7e1e8543fce20baafd76b7b45d8f36a47d41090e.zip
installShellFiles: Enhance installShellCompletion
Teach installShellCompletion how to install completions from a named
pipe. Also add a convenience flag `--cmd NAME` that synthesizes the name
for each completion instead of requiring repeated `--name` flags.

Usage looks something like

    installShellCompletion --cmd foobar \
      --bash <($out/bin/foobar --bash-completion) \
      --fish <($out/bin/foobar --fish-completion) \
      --zsh <($out/bin/foobar --zsh-completion)

Fixes #83284
Diffstat (limited to 'pkgs/build-support/setup-hooks')
-rw-r--r--pkgs/build-support/setup-hooks/install-shell-files.sh121
1 files changed, 93 insertions, 28 deletions
diff --git a/pkgs/build-support/setup-hooks/install-shell-files.sh b/pkgs/build-support/setup-hooks/install-shell-files.sh
index e0ea1f7f30a..434c07a59eb 100644
--- a/pkgs/build-support/setup-hooks/install-shell-files.sh
+++ b/pkgs/build-support/setup-hooks/install-shell-files.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+# shellcheck shell=bash
 # Setup hook for the `installShellFiles` package.
 #
 # Example usage in a derivation:
@@ -49,7 +49,7 @@ installManPage() {
     done
 }
 
-# installShellCompletion [--bash|--fish|--zsh] ([--name <name>] <path>)...
+# installShellCompletion [--cmd <name>] ([--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.
@@ -61,9 +61,20 @@ installManPage() {
 # 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.
 #
+# If all shell completions need to be renamed before installing the optional `--cmd <name>` flag
+# may be given. This will synthesize a name for each file, unless overridden with an explicit
+# `--name` flag. For example, `--cmd foobar` will synthesize the name `_foobar` for zsh and
+# `foobar.bash` for bash.
+#
 # For zsh completions, if the `--name` flag is not given, the path will be automatically renamed
 # such that `foobar.zsh` becomes `_foobar`.
 #
+# A path may be a named fd, such as produced by the bash construct `<(cmd)`. When using a named fd,
+# the shell type flag must be provided, and either the `--name` or `--cmd` flag must be provided.
+# This might look something like:
+#
+#   installShellCompletion --zsh --name _foobar <($out/bin/foobar --zsh-completion)
+#
 # This command accepts multiple shell flags in conjunction with multiple paths if you wish to
 # install them all in one command:
 #
@@ -76,9 +87,16 @@ installManPage() {
 #   installShellCompletion --fish --name foobar.fish share/completions.fish
 #   installShellCompletion --zsh --name _foobar share/completions.zsh
 #
+# Or to use shell newline escaping to split a single invocation across multiple lines:
+#
+#   installShellCompletion --cmd foobar \
+#     --bash <($out/bin/foobar --bash-completion) \
+#     --fish <($out/bin/foobar --fish-completion) \
+#     --zsh <($out/bin/foobar --zsh-completion)
+#
 # If any argument is `--` the remaining arguments will be treated as paths.
 installShellCompletion() {
-    local shell='' name='' retval=0 parseArgs=1 arg
+    local shell='' name='' cmdname='' retval=0 parseArgs=1 arg
     while { arg=$1; shift; }; do
         # Parse arguments
         if (( parseArgs )); then
@@ -97,6 +115,17 @@ installShellCompletion() {
                 # treat `--name=foo` the same as `--name foo`
                 name=${arg#--name=}
                 continue;;
+            --cmd)
+                cmdname=$1
+                shift || {
+                    echo 'installShellCompletion: error: --cmd flag expected an argument' >&2
+                    return 1
+                }
+                continue;;
+            --cmd=*)
+                # treat `--cmd=foo` the same as `--cmd foo`
+                cmdname=${arg#--cmd=}
+                continue;;
             --?*)
                 echo "installShellCompletion: warning: unknown flag ${arg%%=*}" >&2
                 retval=2
@@ -110,39 +139,67 @@ installShellCompletion() {
         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")
+        # if we get here, this is a path or named pipe
+        # Identify shell and output name
         local curShell=$shell
-        if [[ -z "$curShell" ]]; then
-            # auto-detect the shell
-            case "$basename" in
-            ?*.bash) curShell=bash;;
-            ?*.fish) curShell=fish;;
-            ?*.zsh) curShell=zsh;;
+        local outName=''
+        if [[ -z "$arg" ]]; then
+            echo "installShellCompletion: error: empty path is not allowed" >&2
+            return 1
+        elif [[ -p "$arg" ]]; then
+            # this is a named fd or fifo
+            if [[ -z "$curShell" ]]; then
+                echo "installShellCompletion: error: named pipe requires one of --bash, --fish, or --zsh" >&2
+                return 1
+            elif [[ -z "$name" && -z "$cmdname" ]]; then
+                echo "installShellCompletion: error: named pipe requires one of --cmd or --name" >&2
+                return 1
+            fi
+        else
+            # this is a path
+            local argbase
+            argbase=$(stripHash "$arg")
+            if [[ -z "$curShell" ]]; then
+                # auto-detect the shell
+                case "$argbase" in
+                ?*.bash) curShell=bash;;
+                ?*.fish) curShell=fish;;
+                ?*.zsh) curShell=zsh;;
+                *)
+                    if [[ "$argbase" = _* && "$argbase" != *.* ]]; 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
+            outName=$argbase
+        fi
+        # Identify output path
+        if [[ -n "$name" ]]; then
+            outName=$name
+        elif [[ -n "$cmdname" ]]; then
+            case "$curShell" in
+            bash|fish) outName=$cmdname.$curShell;;
+            zsh) outName=_$cmdname;;
             *)
-                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;;
+                # 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
         fi
-        # Identify output path
-        local outName sharePath
-        outName=${name:-$basename}
+        local sharePath
         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
+            if [[ -z "$name" && -z "$cmdname" ]]; then
                 # convert a name like `foo.zsh` into `_foo`
                 outName=${outName%.zsh}
                 outName=_${outName#_}
@@ -153,8 +210,16 @@ installShellCompletion() {
             return 1;;
         esac
         # Install file
-        install -Dm644 -T "$arg" "${!outputBin:?}/share/$sharePath/$outName" || return
-        # Clear the name, it only applies to one path
+        local outDir="${!outputBin:?}/share/$sharePath"
+        local outPath="$outDir/$outName"
+        if [[ -p "$arg" ]]; then
+            # install handles named pipes on NixOS but not on macOS
+            mkdir -p "$outDir" \
+            && cat "$arg" > "$outPath"
+        else
+            install -Dm644 -T "$arg" "$outPath"
+        fi || return
+        # Clear the per-path flags
         name=
     done
     if [[ -n "$name" ]]; then