
Zsh and newer versions of bash have a builtin `which` function that will show you if a command is actually an alias or a function. For functions, the entire function is printed, and our `spack()` function is quite long. Instead of printing out all that, make the `spack()` function a wrapper around `_spack_shell_wrapper()`, and include some no-ops in the definition so that users can see where it was created and where Spack is installed. Here's what the new output looks like in zsh: ```console $ which spack spack () { : this is a shell function from: /Users/gamblin2/src/spack/share/spack/setup-env.sh : the real spack script is here: /Users/gamblin2/src/spack/bin/spack _spack "$@" return $? } ``` Note that `:` is a no-op in Bourne shell; it just discards anything after it on the line. We use it here to embed paths in the function definition (as comments are stripped).
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 [ "$_sp_shell" = bash ]; 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
|