
Since zsh can load bash completion files natively, seems reasonable to just turn this on. The only changes are to switch from `type -t` which zsh doesn't support to using `type` with a regex and adding a new arm to the sourcing of the completions to allow it to work for zsh as well as bash. Could use more bash/dash/etc testing probably, but everything I've thought to try has worked so far. Notes: * unit-test zsh support, fix issues Specifically fixed word splitting in completion-test, use a different method to apply sh emulation to zsh loaded bash completion, and fixed an incompatibility in regex operator quoting requirements. * compinit now ignores insecure directories Completion isn't meant to be enabled in non-interactive environments, so by default compinit will ask the user if they want to ignore insecure directories or load them anyway. To pass the spack unit tests in GH actions, this prompt must be disabled, so ignore explicitly until a better solution can be found. * debug functions test also requires bash emulation COMP_WORDS is a bash-ism that zsh doesn't natively support, turn on emulation for just that section of tests to allow the comparison to work. Does not change the behavior of the functions themselves since they are already pinned to sh emulation elsewhere. * propagate change to .in file * fix comment and update script based on .in
381 lines
14 KiB
Bash
Executable File
381 lines
14 KiB
Bash
Executable File
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
|
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
|
#
|
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# This file is part of Spack and sets up the spack environment for bash,
|
|
# zsh, and dash (sh). This includes environment modules and lmod support,
|
|
# and it also puts spack in your path. The script also checks that at least
|
|
# module support exists, and provides suggestions if it doesn't. Source
|
|
# it like this:
|
|
#
|
|
# . /path/to/spack/share/spack/setup-env.sh
|
|
#
|
|
########################################################################
|
|
# This is a wrapper around the spack command that forwards calls to
|
|
# 'spack load' and 'spack unload' to shell functions. This in turn
|
|
# allows them to be used to invoke environment modules functions.
|
|
#
|
|
# 'spack load' is smarter than just 'load' because it converts its
|
|
# arguments into a unique Spack spec that is then passed to module
|
|
# commands. This allows the user to use packages without knowing all
|
|
# their installation details.
|
|
#
|
|
# e.g., rather than requiring a full spec for libelf, the user can type:
|
|
#
|
|
# spack load libelf
|
|
#
|
|
# This will first find the available libelf module file and use a
|
|
# matching one. If there are two versions of libelf, the user would
|
|
# need to be more specific, e.g.:
|
|
#
|
|
# spack load libelf@0.8.13
|
|
#
|
|
# This is very similar to how regular spack commands work and it
|
|
# avoids the need to come up with a user-friendly naming scheme for
|
|
# spack module files.
|
|
########################################################################
|
|
|
|
# prevent infinite recursion when spack shells out (e.g., on cray for modules)
|
|
if [ -n "${_sp_initializing:-}" ]; then
|
|
exit 0
|
|
fi
|
|
export _sp_initializing=true
|
|
|
|
|
|
_spack_shell_wrapper() {
|
|
# Store LD_LIBRARY_PATH variables from spack shell function
|
|
# This is necessary because MacOS System Integrity Protection clears
|
|
# variables that affect dyld on process start.
|
|
for var in LD_LIBRARY_PATH DYLD_LIBRARY_PATH DYLD_FALLBACK_LIBRARY_PATH; do
|
|
eval "if [ -n \"\${${var}-}\" ]; then export SPACK_$var=\${${var}}; fi"
|
|
done
|
|
|
|
# Zsh does not do word splitting by default, this enables it for this
|
|
# function only
|
|
if [ -n "${ZSH_VERSION:-}" ]; then
|
|
emulate -L sh
|
|
fi
|
|
|
|
# accumulate flags meant for the main spack command
|
|
# the loop condition is unreadable, but it means:
|
|
# while $1 is set (while there are arguments)
|
|
# and $1 starts with '-' (and the arguments are flags)
|
|
_sp_flags=""
|
|
while [ ! -z ${1+x} ] && [ "${1#-}" != "${1}" ]; do
|
|
_sp_flags="$_sp_flags $1"
|
|
shift
|
|
done
|
|
|
|
# h and V flags don't require further output parsing.
|
|
if [ -n "$_sp_flags" ] && \
|
|
[ "${_sp_flags#*h}" != "${_sp_flags}" ] || \
|
|
[ "${_sp_flags#*V}" != "${_sp_flags}" ];
|
|
then
|
|
command spack $_sp_flags "$@"
|
|
return
|
|
fi
|
|
|
|
# set the subcommand if there is one (if $1 is set)
|
|
_sp_subcommand=""
|
|
if [ ! -z ${1+x} ]; then
|
|
_sp_subcommand="$1"
|
|
shift
|
|
fi
|
|
|
|
# Filter out use and unuse. For any other commands, just run the
|
|
# command.
|
|
case $_sp_subcommand in
|
|
"cd")
|
|
_sp_arg=""
|
|
if [ -n "$1" ]; then
|
|
_sp_arg="$1"
|
|
shift
|
|
fi
|
|
if [ "$_sp_arg" = "-h" ] || [ "$_sp_arg" = "--help" ]; then
|
|
command spack cd -h
|
|
else
|
|
LOC="$(spack location $_sp_arg "$@")"
|
|
if [ -d "$LOC" ] ; then
|
|
cd "$LOC"
|
|
else
|
|
return 1
|
|
fi
|
|
fi
|
|
return
|
|
;;
|
|
"env")
|
|
_sp_arg=""
|
|
if [ -n "$1" ]; then
|
|
_sp_arg="$1"
|
|
shift
|
|
fi
|
|
|
|
if [ "$_sp_arg" = "-h" ] || [ "$_sp_arg" = "--help" ]; then
|
|
command spack env -h
|
|
else
|
|
case $_sp_arg in
|
|
activate)
|
|
# Get --sh, --csh, or -h/--help arguments.
|
|
# Space needed here becauses regexes start with a space
|
|
# and `-h` may be the only argument.
|
|
_a=" $@"
|
|
# Space needed here to differentiate between `-h`
|
|
# argument and environments with "-h" in the name.
|
|
# Also see: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#Shell-Parameter-Expansion
|
|
if [ -z ${1+x} ] || \
|
|
[ "${_a#* --sh}" != "$_a" ] || \
|
|
[ "${_a#* --csh}" != "$_a" ] || \
|
|
[ "${_a#* -h}" != "$_a" ] || \
|
|
[ "${_a#* --help}" != "$_a" ];
|
|
then
|
|
# No args or args contain --sh, --csh, or -h/--help: just execute.
|
|
command spack env activate "$@"
|
|
else
|
|
# Actual call to activate: source the output.
|
|
eval $(command spack $_sp_flags env activate --sh "$@")
|
|
fi
|
|
;;
|
|
deactivate)
|
|
# Get --sh, --csh, or -h/--help arguments.
|
|
# Space needed here becauses regexes start with a space
|
|
# and `-h` may be the only argument.
|
|
_a=" $@"
|
|
# Space needed here to differentiate between `--sh`
|
|
# argument and environments with "--sh" in the name.
|
|
# Also see: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#Shell-Parameter-Expansion
|
|
if [ "${_a#* --sh}" != "$_a" ] || \
|
|
[ "${_a#* --csh}" != "$_a" ];
|
|
then
|
|
# Args contain --sh or --csh: just execute.
|
|
command spack env deactivate "$@"
|
|
elif [ -n "$*" ]; then
|
|
# Any other arguments are an error or -h/--help: just run help.
|
|
command spack env deactivate -h
|
|
else
|
|
# No args: source the output of the command.
|
|
eval $(command spack $_sp_flags env deactivate --sh)
|
|
fi
|
|
;;
|
|
*)
|
|
command spack env $_sp_arg "$@"
|
|
;;
|
|
esac
|
|
fi
|
|
return
|
|
;;
|
|
"load"|"unload")
|
|
# Get --sh, --csh, -h, or --help arguments.
|
|
# Space needed here becauses regexes start with a space
|
|
# and `-h` may be the only argument.
|
|
_a=" $@"
|
|
# Space needed here to differentiate between `-h`
|
|
# argument and specs with "-h" in the name.
|
|
# Also see: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#Shell-Parameter-Expansion
|
|
if [ "${_a#* --sh}" != "$_a" ] || \
|
|
[ "${_a#* --csh}" != "$_a" ] || \
|
|
[ "${_a#* -h}" != "$_a" ] || \
|
|
[ "${_a#* --help}" != "$_a" ];
|
|
then
|
|
# Args contain --sh, --csh, or -h/--help: just execute.
|
|
command spack $_sp_flags $_sp_subcommand "$@"
|
|
else
|
|
eval $(command spack $_sp_flags $_sp_subcommand --sh "$@" || \
|
|
echo "return 1") # return 1 if spack command fails
|
|
fi
|
|
;;
|
|
*)
|
|
command spack $_sp_flags $_sp_subcommand "$@"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
|
|
########################################################################
|
|
# Prepends directories to path, if they exist.
|
|
# pathadd /path/to/dir # add to PATH
|
|
# or pathadd OTHERPATH /path/to/dir # add to OTHERPATH
|
|
########################################################################
|
|
_spack_pathadd() {
|
|
# If no variable name is supplied, just append to PATH
|
|
# otherwise append to that variable.
|
|
_pa_varname=PATH
|
|
_pa_new_path="$1"
|
|
if [ -n "$2" ]; then
|
|
_pa_varname="$1"
|
|
_pa_new_path="$2"
|
|
fi
|
|
|
|
# Do the actual prepending here.
|
|
eval "_pa_oldvalue=\${${_pa_varname}:-}"
|
|
|
|
_pa_canonical=":$_pa_oldvalue:"
|
|
if [ -d "$_pa_new_path" ] && \
|
|
[ "${_pa_canonical#*:${_pa_new_path}:}" = "${_pa_canonical}" ];
|
|
then
|
|
if [ -n "$_pa_oldvalue" ]; then
|
|
eval "export $_pa_varname=\"$_pa_new_path:$_pa_oldvalue\""
|
|
else
|
|
export $_pa_varname="$_pa_new_path"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
|
|
# Determine which shell is being used
|
|
_spack_determine_shell() {
|
|
if [ -f "/proc/$$/exe" ]; then
|
|
# If procfs is present this seems a more reliable
|
|
# way to detect the current shell
|
|
_sp_exe=$(readlink /proc/$$/exe)
|
|
# Shell may contain number, like zsh5 instead of zsh
|
|
basename ${_sp_exe} | tr -d '0123456789'
|
|
elif [ -n "${BASH:-}" ]; then
|
|
echo bash
|
|
elif [ -n "${ZSH_NAME:-}" ]; then
|
|
echo zsh
|
|
else
|
|
PS_FORMAT= ps -p $$ | tail -n 1 | awk '{print $4}' | sed 's/^-//' | xargs basename
|
|
fi
|
|
}
|
|
_sp_shell=$(_spack_determine_shell)
|
|
|
|
|
|
alias spacktivate="spack env activate"
|
|
|
|
#
|
|
# Figure out where this file is.
|
|
#
|
|
if [ "$_sp_shell" = bash ]; then
|
|
_sp_source_file="${BASH_SOURCE[0]:-}"
|
|
elif [ "$_sp_shell" = zsh ]; then
|
|
_sp_source_file="${(%):-%N}"
|
|
else
|
|
# Try to read the /proc filesystem (works on linux without lsof)
|
|
# In dash, the sourced file is the last one opened (and it's kept open)
|
|
_sp_source_file_fd="$(\ls /proc/$$/fd 2>/dev/null | sort -n | tail -1)"
|
|
if ! _sp_source_file="$(readlink /proc/$$/fd/$_sp_source_file_fd)"; then
|
|
# Last resort: try lsof. This works in dash on macos -- same reason.
|
|
# macos has lsof installed by default; some linux containers don't.
|
|
_sp_lsof_output="$(lsof -p $$ -Fn0 | tail -1)"
|
|
_sp_source_file="${_sp_lsof_output#*n}"
|
|
fi
|
|
|
|
# If we can't find this script's path after all that, bail out with
|
|
# plain old $0, which WILL NOT work if this is sourced indirectly.
|
|
if [ ! -f "$_sp_source_file" ]; then
|
|
_sp_source_file="$0"
|
|
fi
|
|
fi
|
|
|
|
#
|
|
# Find root directory and add bin to path.
|
|
#
|
|
# We send cd output to /dev/null to avoid because a lot of users set up
|
|
# their shell so that cd prints things out to the tty.
|
|
_sp_share_dir="$(cd "$(dirname $_sp_source_file)" > /dev/null && pwd)"
|
|
_sp_prefix="$(cd "$(dirname $(dirname $_sp_share_dir))" > /dev/null && pwd)"
|
|
if [ -x "$_sp_prefix/bin/spack" ]; then
|
|
export SPACK_ROOT="${_sp_prefix}"
|
|
else
|
|
# If the shell couldn't find the sourced script, fall back to
|
|
# whatever the user set SPACK_ROOT to.
|
|
if [ -n "$SPACK_ROOT" ]; then
|
|
_sp_prefix="$SPACK_ROOT"
|
|
_sp_share_dir="$_sp_prefix/share/spack"
|
|
fi
|
|
|
|
# If SPACK_ROOT didn't work, fail. We should need this rarely, as
|
|
# the tricks above for finding the sourced file are pretty robust.
|
|
if [ ! -x "$_sp_prefix/bin/spack" ]; then
|
|
echo "==> Error: SPACK_ROOT must point to spack's prefix when using $_sp_shell"
|
|
echo "Run this with the correct prefix before sourcing setup-env.sh:"
|
|
echo " export SPACK_ROOT=</path/to/spack>"
|
|
return 1
|
|
fi
|
|
fi
|
|
_spack_pathadd PATH "${_sp_prefix%/}/bin"
|
|
|
|
#
|
|
# Check whether a function of the given name is defined
|
|
#
|
|
_spack_fn_exists() {
|
|
LANG= type $1 2>&1 | grep -q 'function'
|
|
}
|
|
|
|
need_module="no"
|
|
if ! _spack_fn_exists use && ! _spack_fn_exists module; then
|
|
need_module="yes"
|
|
fi;
|
|
|
|
# Define the spack shell function with some informative no-ops, so when users
|
|
# run `which spack`, they see the path to spack and where the function is from.
|
|
eval "spack() {
|
|
: this is a shell function from: $_sp_share_dir/setup-env.sh
|
|
: the real spack script is here: $_sp_prefix/bin/spack
|
|
_spack_shell_wrapper \"\$@\"
|
|
return \$?
|
|
}"
|
|
|
|
# Export spack function so it is available in subshells (only works with bash)
|
|
if [ "$_sp_shell" = bash ]; then
|
|
export -f spack
|
|
export -f _spack_shell_wrapper
|
|
fi
|
|
|
|
#
|
|
# make available environment-modules
|
|
#
|
|
if [ "${need_module}" = "yes" ]; then
|
|
eval `spack --print-shell-vars sh,modules`
|
|
|
|
# _sp_module_prefix is set by spack --print-sh-vars
|
|
if [ "${_sp_module_prefix}" != "not_installed" ]; then
|
|
# activate it!
|
|
# environment-modules@4: has a bin directory inside its prefix
|
|
_sp_module_bin="${_sp_module_prefix}/bin"
|
|
if [ ! -d "${_sp_module_bin}" ]; then
|
|
# environment-modules@3 has a nested bin directory
|
|
_sp_module_bin="${_sp_module_prefix}/Modules/bin"
|
|
fi
|
|
|
|
# _sp_module_bin and _sp_shell are evaluated here; the quoted
|
|
# eval statement and $* are deferred.
|
|
_sp_cmd="module() { eval \`${_sp_module_bin}/modulecmd ${_sp_shell} \$*\`; }"
|
|
eval "$_sp_cmd"
|
|
_spack_pathadd PATH "${_sp_module_bin}"
|
|
fi;
|
|
else
|
|
eval `spack --print-shell-vars sh`
|
|
fi;
|
|
|
|
|
|
#
|
|
# set module system roots
|
|
#
|
|
_sp_multi_pathadd() {
|
|
local IFS=':'
|
|
if [ "$_sp_shell" = zsh ]; then
|
|
emulate -L sh
|
|
fi
|
|
for pth in $2; do
|
|
for systype in ${_sp_compatible_sys_types}; do
|
|
_spack_pathadd "$1" "${pth}/${systype}"
|
|
done
|
|
done
|
|
}
|
|
_sp_multi_pathadd MODULEPATH "$_sp_tcl_roots"
|
|
|
|
# Add programmable tab completion for Bash
|
|
#
|
|
if test "$_sp_shell" = bash || test -n "${ZSH_VERSION:-}"; then
|
|
source $_sp_share_dir/spack-completion.bash
|
|
fi
|
|
|
|
# done: unset sentinel variable as we're no longer initializing
|
|
unset _sp_initializing
|
|
export _sp_initializing
|