Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1# shellcheck shell=bash 2# Setup hook for the `installShellFiles` package. 3# 4# Example usage in a derivation: 5# 6# { …, installShellFiles, … }: 7# stdenv.mkDerivation { 8# … 9# nativeBuildInputs = [ installShellFiles ]; 10# postInstall = '' 11# installManPage share/doc/foobar.1 12# installShellCompletion share/completions/foobar.{bash,fish,zsh} 13# ''; 14# … 15# } 16# 17# See comments on each function for more details. 18 19# installManPage <path> [...<path>] 20# 21# Each argument is checked for its man section suffix and installed into the appropriate 22# share/man/man<n>/ directory. The function returns an error if any paths don't have the man 23# section suffix (with optional .gz compression). 24installManPage() { 25 local path 26 for path in "$@"; do 27 if test -z "$path"; then 28 nixErrorLog "${FUNCNAME[0]}: path cannot be empty" 29 return 1 30 fi 31 nixInfoLog "${FUNCNAME[0]}: installing $path" 32 local basename 33 basename=$(stripHash "$path") # use stripHash in case it's a nix store path 34 local trimmed=${basename%.gz} # don't get fooled by compressed manpages 35 local suffix=${trimmed##*.} 36 if test -z "$suffix" -o "$suffix" = "$trimmed"; then 37 nixErrorLog "${FUNCNAME[0]}: path missing manpage section suffix: $path" 38 return 1 39 fi 40 local outRoot 41 if test "$suffix" = 3; then 42 outRoot=${!outputDevman:?} 43 else 44 outRoot=${!outputMan:?} 45 fi 46 local outPath="${outRoot}/share/man/man$suffix/$basename" 47 install -D --mode=644 --no-target-directory "$path" "$outPath" 48 done 49} 50 51# installShellCompletion [--cmd <name>] ([--bash|--fish|--zsh] [--name <name>] <path>)... 52# 53# Each path is installed into the appropriate directory for shell completions for the given shell. 54# If one of `--bash`, `--fish`, or `--zsh` is given the path is assumed to belong to that shell. 55# Otherwise the file extension will be examined to pick a shell. If the shell is unknown a warning 56# will be logged and the command will return a non-zero status code after processing any remaining 57# paths. Any of the shell flags will affect all subsequent paths (unless another shell flag is 58# given). 59# 60# If the shell completion needs to be renamed before installing the optional `--name <name>` flag 61# may be given. Any name provided with this flag only applies to the next path. 62# 63# If all shell completions need to be renamed before installing the optional `--cmd <name>` flag 64# may be given. This will synthesize a name for each file, unless overridden with an explicit 65# `--name` flag. For example, `--cmd foobar` will synthesize the name `_foobar` for zsh and 66# `foobar.bash` for bash. 67# 68# For zsh completions, if the `--name` flag is not given, the path will be automatically renamed 69# such that `foobar.zsh` becomes `_foobar`. 70# 71# A path may be a named fd, such as produced by the bash construct `<(cmd)`. When using a named fd, 72# the shell type flag must be provided, and either the `--name` or `--cmd` flag must be provided. 73# This might look something like: 74# 75# installShellCompletion --zsh --name _foobar <($out/bin/foobar --zsh-completion) 76# 77# This command accepts multiple shell flags in conjunction with multiple paths if you wish to 78# install them all in one command: 79# 80# installShellCompletion share/completions/foobar.{bash,fish} --zsh share/completions/_foobar 81# 82# However it may be easier to read if each shell is split into its own invocation, especially when 83# renaming is involved: 84# 85# installShellCompletion --bash --name foobar.bash share/completions.bash 86# installShellCompletion --fish --name foobar.fish share/completions.fish 87# installShellCompletion --zsh --name _foobar share/completions.zsh 88# 89# Or to use shell newline escaping to split a single invocation across multiple lines: 90# 91# installShellCompletion --cmd foobar \ 92# --bash <($out/bin/foobar --bash-completion) \ 93# --fish <($out/bin/foobar --fish-completion) \ 94# --zsh <($out/bin/foobar --zsh-completion) 95# 96# If any argument is `--` the remaining arguments will be treated as paths. 97installShellCompletion() { 98 local shell='' name='' cmdname='' retval=0 parseArgs=1 arg 99 while { arg=$1; shift; }; do 100 # Parse arguments 101 if (( parseArgs )); then 102 case "$arg" in 103 --bash|--fish|--zsh) 104 shell=${arg#--} 105 continue;; 106 --name) 107 name=$1 108 shift || { 109 nixErrorLog "${FUNCNAME[0]}: --name flag expected an argument" 110 return 1 111 } 112 continue;; 113 --name=*) 114 # treat `--name=foo` the same as `--name foo` 115 name=${arg#--name=} 116 continue;; 117 --cmd) 118 cmdname=$1 119 shift || { 120 nixErrorLog "${FUNCNAME[0]}: --cmd flag expected an argument" 121 return 1 122 } 123 continue;; 124 --cmd=*) 125 # treat `--cmd=foo` the same as `--cmd foo` 126 cmdname=${arg#--cmd=} 127 continue;; 128 --?*) 129 nixWarnLog "${FUNCNAME[0]}: unknown flag ${arg%%=*}" 130 retval=2 131 continue;; 132 --) 133 # treat remaining args as paths 134 parseArgs=0 135 continue;; 136 esac 137 fi 138 nixInfoLog "${FUNCNAME[0]}: installing $arg${name:+ as $name}" 139 # if we get here, this is a path or named pipe 140 # Identify shell and output name 141 local curShell=$shell 142 local outName='' 143 if [[ -z "$arg" ]]; then 144 nixErrorLog "${FUNCNAME[0]}: empty path is not allowed" 145 return 1 146 elif [[ -p "$arg" ]]; then 147 # this is a named fd or fifo 148 if [[ -z "$curShell" ]]; then 149 nixErrorLog "${FUNCNAME[0]}: named pipe requires one of --bash, --fish, or --zsh" 150 return 1 151 elif [[ -z "$name" && -z "$cmdname" ]]; then 152 nixErrorLog "${FUNCNAME[0]}: named pipe requires one of --cmd or --name" 153 return 1 154 fi 155 else 156 # this is a path 157 local argbase 158 argbase=$(stripHash "$arg") 159 if [[ -z "$curShell" ]]; then 160 # auto-detect the shell 161 case "$argbase" in 162 ?*.bash) curShell=bash;; 163 ?*.fish) curShell=fish;; 164 ?*.zsh) curShell=zsh;; 165 *) 166 if [[ "$argbase" = _* && "$argbase" != *.* ]]; then 167 # probably zsh 168 nixWarnLog "${FUNCNAME[0]}: assuming path \`$arg' is zsh; please specify with --zsh" 169 curShell=zsh 170 else 171 nixWarnLog "${FUNCNAME[0]}: unknown shell for path: $arg" >&2 172 retval=2 173 continue 174 fi;; 175 esac 176 fi 177 outName=$argbase 178 fi 179 # Identify output path 180 if [[ -n "$name" ]]; then 181 outName=$name 182 elif [[ -n "$cmdname" ]]; then 183 case "$curShell" in 184 bash|fish) outName=$cmdname.$curShell;; 185 zsh) outName=_$cmdname;; 186 *) 187 # Our list of shells is out of sync with the flags we accept or extensions we detect. 188 nixErrorLog "${FUNCNAME[0]}: internal: shell $curShell not recognized" 189 return 1;; 190 esac 191 fi 192 local sharePath 193 case "$curShell" in 194 bash) sharePath=bash-completion/completions;; 195 fish) sharePath=fish/vendor_completions.d;; 196 zsh) 197 sharePath=zsh/site-functions 198 # only apply automatic renaming if we didn't have a manual rename 199 if [[ -z "$name" && -z "$cmdname" ]]; then 200 # convert a name like `foo.zsh` into `_foo` 201 outName=${outName%.zsh} 202 outName=_${outName#_} 203 fi;; 204 *) 205 # Our list of shells is out of sync with the flags we accept or extensions we detect. 206 nixErrorLog "${FUNCNAME[0]}: internal: shell $curShell not recognized" 207 return 1;; 208 esac 209 # Install file 210 local outDir="${!outputBin:?}/share/$sharePath" 211 local outPath="$outDir/$outName" 212 if [[ -p "$arg" ]]; then 213 # install handles named pipes on NixOS but not on macOS 214 mkdir -p "$outDir" \ 215 && cat "$arg" > "$outPath" 216 else 217 install -D --mode=644 --no-target-directory "$arg" "$outPath" 218 fi 219 220 if [ ! -s "$outPath" ]; then 221 nixErrorLog "${FUNCNAME[0]}: installed shell completion file \`$outPath' does not exist or has zero size" 222 return 1 223 fi 224 # Clear the per-path flags 225 name= 226 done 227 if [[ -n "$name" ]]; then 228 nixErrorLog "${FUNCNAME[0]}: --name flag given with no path" >&2 229 return 1 230 fi 231 return $retval 232} 233 234# installBin <path> [...<path>] 235# 236# Install each argument to $outputBin 237installBin() { 238 local path 239 for path in "$@"; do 240 if test -z "$path"; then 241 nixErrorLog "${FUNCNAME[0]}: path cannot be empty" 242 return 1 243 fi 244 nixInfoLog "${FUNCNAME[0]}: installing $path" 245 246 local basename 247 # use stripHash in case it's a nix store path 248 basename=$(stripHash "$path") 249 250 local outRoot 251 outRoot=${!outputBin:?} 252 253 local outPath="${outRoot}/bin/$basename" 254 install -D --mode=755 --no-target-directory "$path" "${outRoot}/bin/$basename" 255 done 256}