summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Blaschke <jpblaschke@lbl.gov>2020-06-30 12:26:27 -0700
committerGitHub <noreply@github.com>2020-06-30 14:26:27 -0500
commit1d55adfd2b75a8cbe7c1cd4f229cca6fa1dd81f9 (patch)
tree77cd09af4e0cb024e701a1f52acec86d61ef7bfb
parent212299a0213ec3bc46cda1835fd9d5dbc1e5922b (diff)
downloadspack-1d55adfd2b75a8cbe7c1cd4f229cca6fa1dd81f9.tar.gz
spack-1d55adfd2b75a8cbe7c1cd4f229cca6fa1dd81f9.tar.bz2
spack-1d55adfd2b75a8cbe7c1cd4f229cca6fa1dd81f9.tar.xz
spack-1d55adfd2b75a8cbe7c1cd4f229cca6fa1dd81f9.zip
Add fish shell support (#9279)
* share/spack/setup-env.fish file to setup environment in fish shell * setup-env.fish testing script * Update share/spack/setup-env.fish Co-Authored-By: Elsa Gonsiorowski, PhD <gonsie@me.com> * Update share/spack/qa/setup-env-test.fish Co-Authored-By: Adam J. Stewart <ajstewart426@gmail.com> * updates completions using `spack commands --update-completion` * added stderr-nocaret warning * added fish shell tests to CI system Co-authored-by: becker33 <becker33@llnl.gov> Co-authored-by: Adam J. Stewart <ajstewart426@gmail.com> Co-authored-by: Elsa Gonsiorowski, PhD <gonsie@me.com>
-rw-r--r--.travis.yml2
-rw-r--r--lib/spack/spack/cmd/env.py6
-rw-r--r--lib/spack/spack/cmd/load.py3
-rw-r--r--lib/spack/spack/cmd/unload.py3
-rw-r--r--lib/spack/spack/environment.py29
-rw-r--r--lib/spack/spack/util/environment.py2
-rwxr-xr-xshare/spack/qa/run-shell-tests6
-rwxr-xr-xshare/spack/qa/setup-env-test.fish395
-rwxr-xr-xshare/spack/setup-env.fish723
-rwxr-xr-xshare/spack/spack-completion.bash8
10 files changed, 1171 insertions, 6 deletions
diff --git a/.travis.yml b/.travis.yml
index b59d1666d8..5d8b793758 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,6 +40,7 @@ jobs:
- ninja-build
- realpath
- zsh
+ - fish
env: [ TEST_SUITE=unit, COVERAGE=true ]
- python: '3.8'
os: linux
@@ -73,6 +74,7 @@ addons:
- ninja-build
- patchelf
- zsh
+ - fish
update: true
# ~/.ccache needs to be cached directly as Travis is not taking care of it
diff --git a/lib/spack/spack/cmd/env.py b/lib/spack/spack/cmd/env.py
index 43c125e8f2..d3e825f1dd 100644
--- a/lib/spack/spack/cmd/env.py
+++ b/lib/spack/spack/cmd/env.py
@@ -52,6 +52,9 @@ def env_activate_setup_parser(subparser):
shells.add_argument(
'--csh', action='store_const', dest='shell', const='csh',
help="print csh commands to activate the environment")
+ shells.add_argument(
+ '--fish', action='store_const', dest='shell', const='fish',
+ help="print fish commands to activate the environment")
view_options = subparser.add_mutually_exclusive_group()
view_options.add_argument(
@@ -127,6 +130,9 @@ def env_deactivate_setup_parser(subparser):
shells.add_argument(
'--csh', action='store_const', dest='shell', const='csh',
help="print csh commands to deactivate the environment")
+ shells.add_argument(
+ '--fish', action='store_const', dest='shell', const='fish',
+ help="print fish commands to activate the environment")
def env_deactivate(args):
diff --git a/lib/spack/spack/cmd/load.py b/lib/spack/spack/cmd/load.py
index 3ef485941f..3938602882 100644
--- a/lib/spack/spack/cmd/load.py
+++ b/lib/spack/spack/cmd/load.py
@@ -32,6 +32,9 @@ def setup_parser(subparser):
shells.add_argument(
'--csh', action='store_const', dest='shell', const='csh',
help="print csh commands to load the package")
+ shells.add_argument(
+ '--fish', action='store_const', dest='shell', const='fish',
+ help="print fish commands to load the package")
subparser.add_argument(
'--first',
diff --git a/lib/spack/spack/cmd/unload.py b/lib/spack/spack/cmd/unload.py
index d19a33102f..cbee2fc769 100644
--- a/lib/spack/spack/cmd/unload.py
+++ b/lib/spack/spack/cmd/unload.py
@@ -31,6 +31,9 @@ def setup_parser(subparser):
shells.add_argument(
'--csh', action='store_const', dest='shell', const='csh',
help="print csh commands to activate the environment")
+ shells.add_argument(
+ '--fish', action='store_const', dest='shell', const='fish',
+ help="print fish commands to load the package")
subparser.add_argument('-a', '--all', action='store_true',
help='unload all loaded Spack packages.')
diff --git a/lib/spack/spack/environment.py b/lib/spack/spack/environment.py
index 32bd8c962d..f7b50c30c9 100644
--- a/lib/spack/spack/environment.py
+++ b/lib/spack/spack/environment.py
@@ -115,7 +115,7 @@ def activate(
use_env_repo (bool): use the packages exactly as they appear in the
environment's repository
add_view (bool): generate commands to add view to path variables
- shell (string): One of `sh`, `csh`.
+ shell (string): One of `sh`, `csh`, `fish`.
prompt (string): string to add to the users prompt, or None
Returns:
@@ -141,6 +141,19 @@ def activate(
cmds += 'if (! $?SPACK_OLD_PROMPT ) '
cmds += 'setenv SPACK_OLD_PROMPT "${prompt}";\n'
cmds += 'set prompt="%s ${prompt}";\n' % prompt
+ elif shell == 'fish':
+ if os.getenv('TERM') and 'color' in os.getenv('TERM') and prompt:
+ prompt = colorize('@G{%s} ' % prompt, color=True)
+
+ cmds += 'set -gx SPACK_ENV %s;\n' % env.path
+ cmds += 'function despacktivate;\n'
+ cmds += ' spack env deactivate;\n'
+ cmds += 'end;\n'
+ #
+ # NOTE: We're not changing the fish_prompt function (which is fish's
+ # solution to the PS1 variable) here. This is a bit fiddly, and easy to
+ # screw up => spend time reasearching a solution. Feedback welcome.
+ #
else:
if os.getenv('TERM') and 'color' in os.getenv('TERM') and prompt:
prompt = colorize('@G{%s} ' % prompt, color=True)
@@ -156,6 +169,12 @@ def activate(
cmds += 'fi;\n'
cmds += 'export PS1="%s ${PS1}";\n' % prompt
+ #
+ # NOTE in the fish-shell: Path variables are a special kind of variable
+ # used to support colon-delimited path lists including PATH, CDPATH,
+ # MANPATH, PYTHONPATH, etc. All variables that end in PATH (case-sensitive)
+ # become PATH variables.
+ #
if add_view and default_view_name in env.views:
with spack.store.db.read_transaction():
cmds += env.add_default_view_to_shell(shell)
@@ -167,7 +186,7 @@ def deactivate(shell='sh'):
"""Undo any configuration or repo settings modified by ``activate()``.
Arguments:
- shell (string): One of `sh`, `csh`. Shell style to use.
+ shell (string): One of `sh`, `csh`, `fish`. Shell style to use.
Returns:
(string): shell commands for `shell` to undo environment variables
@@ -191,6 +210,12 @@ def deactivate(shell='sh'):
cmds += 'set prompt="$SPACK_OLD_PROMPT" && '
cmds += 'unsetenv SPACK_OLD_PROMPT;\n'
cmds += 'unalias despacktivate;\n'
+ elif shell == 'fish':
+ cmds += 'set -e SPACK_ENV;\n'
+ cmds += 'functions -e despacktivate;\n'
+ #
+ # NOTE: Not changing fish_prompt (above) => no need to restore it here.
+ #
else:
cmds += 'if [ ! -z ${SPACK_ENV+x} ]; then\n'
cmds += 'unset SPACK_ENV; export SPACK_ENV;\n'
diff --git a/lib/spack/spack/util/environment.py b/lib/spack/spack/util/environment.py
index 3d69efa5ca..7e6d34e64d 100644
--- a/lib/spack/spack/util/environment.py
+++ b/lib/spack/spack/util/environment.py
@@ -32,12 +32,14 @@ system_dirs = [os.path.join(p, s) for s in suffixes for p in system_paths] + \
_shell_set_strings = {
'sh': 'export {0}={1};\n',
'csh': 'setenv {0} {1};\n',
+ 'fish': 'set -gx {0} {1};\n'
}
_shell_unset_strings = {
'sh': 'unset {0};\n',
'csh': 'unsetenv {0};\n',
+ 'fish': 'set -e {0};\n',
}
diff --git a/share/spack/qa/run-shell-tests b/share/spack/qa/run-shell-tests
index 31c1c1548b..126e639186 100755
--- a/share/spack/qa/run-shell-tests
+++ b/share/spack/qa/run-shell-tests
@@ -25,6 +25,9 @@ check_dependencies $coverage git hg svn
export PATH="$ORIGINAL_PATH"
unset spack
+# Convert QA_DIR to absolute path before changing directory
+export QA_DIR=$(realpath $QA_DIR)
+
# Start in the spack root directory
cd "$SPACK_ROOT"
@@ -41,3 +44,6 @@ fi
# Run the test scripts for their output (these will print nicely)
zsh "$QA_DIR/setup-env-test.sh"
dash "$QA_DIR/setup-env-test.sh"
+
+# Run fish tests
+fish "$QA_DIR/setup-env-test.fish"
diff --git a/share/spack/qa/setup-env-test.fish b/share/spack/qa/setup-env-test.fish
new file mode 100755
index 0000000000..964d876fe9
--- /dev/null
+++ b/share/spack/qa/setup-env-test.fish
@@ -0,0 +1,395 @@
+#!/usr/bin/env fish
+#
+# Copyright 2013-2019 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 script tests that Spack's setup-env.fish init script works.
+#
+
+
+function allocate_testing_global -d "allocate global variables used for testing"
+
+ # Colors for output
+ set -gx __spt_red '\033[1;31m'
+ set -gx __spt_cyan '\033[1;36m'
+ set -gx __spt_green '\033[1;32m'
+ set -gx __spt_reset '\033[0m'
+
+ # counts of test successes and failures.
+ set -gx __spt_success 0
+ set -gx __spt_errors 0
+end
+
+
+function delete_testing_global -d "deallocate global varialbes used for testing"
+
+ set -e __spt_red
+ set -e __spt_cyan
+ set -e __spt_green
+ set -e __spt_reset
+
+ set -e __spt_success
+ set -e __spt_errors
+end
+
+# ------------------------------------------------------------------------
+# Functions for color output.
+# ------------------------------------------------------------------------
+
+
+function echo_red
+ printf "$__spt_red$argv$__spt_reset\n"
+end
+
+function echo_green
+ printf "$__spt_green$argv$__spt_reset\n"
+end
+
+function echo_msg
+ printf "$__spt_cyan$argv$__spt_reset\n"
+end
+
+
+
+# ------------------------------------------------------------------------
+# Generic functions for testing fish code.
+# ------------------------------------------------------------------------
+
+
+# Print out a header for a group of tests.
+function title
+
+ echo
+ echo_msg "$argv"
+ echo_msg "---------------------------------"
+
+end
+
+# echo FAIL in red text; increment failures
+function fail
+ echo_red FAIL
+ set __spt_errors (math $__spt_errors+1)
+end
+
+# echo SUCCESS in green; increment successes
+function pass
+ echo_green SUCCESS
+ set __spt_success (math $__spt_success+1)
+end
+
+
+#
+# Run a command and suppress output unless it fails.
+# On failure, echo the exit code and output.
+#
+function spt_succeeds
+ printf "'$argv' succeeds ... "
+
+ set -l output (eval $argv 2>&1)
+
+ if test $status -ne 0
+ fail
+ echo_red "Command failed with error $status"
+ if test -n "$output"
+ echo_msg "Output:"
+ echo "$output"
+ else
+ echo_msg "No output."
+ end
+ else
+ pass
+ end
+end
+
+
+#
+# Run a command and suppress output unless it succeeds.
+# If the command succeeds, echo the output.
+#
+function spt_fails
+ printf "'$argv' fails ... "
+
+ set -l output (eval $argv 2>&1)
+
+ if test $status -eq 0
+ fail
+ echo_red "Command failed with error $status"
+ if test -n "$output"
+ echo_msg "Output:"
+ echo "$output"
+ else
+ echo_msg "No output."
+ end
+ else
+ pass
+ end
+end
+
+
+#
+# Ensure that a string is in the output of a command.
+# Suppresses output on success.
+# On failure, echo the exit code and output.
+#
+function spt_contains
+ set -l target_string $argv[1]
+ set -l remaining_args $argv[2..-1]
+
+ printf "'$remaining_args' output contains '$target_string' ... "
+
+ set -l output (eval $remaining_args 2>&1)
+
+ if not echo "$output" | string match -q -r ".*$target_string.*"
+ fail
+ echo_red "Command exited with error $status"
+ echo_red "'$target_string' was not in output."
+ if test -n "$output"
+ echo_msg "Output:"
+ echo "$output"
+ else
+ echo_msg "No output."
+ end
+ else
+ pass
+ end
+end
+
+
+#
+# Ensure that a variable is set.
+#
+function is_set
+ printf "'$argv[1]' is set ... "
+
+ if test -z "$$argv[1]"
+ fail
+ echo_msg "'$argv[1]' was not set!"
+ else
+ pass
+ end
+end
+
+
+#
+# Ensure that a variable is not set.
+# Fails and prints the value of the variable if it is set.
+#
+function is_not_set
+ printf "'$argv[1]' is not set ... "
+
+ if test -n "$$argv[1]"
+ fail
+ echo_msg "'$argv[1]' was set!"
+ echo " $$argv[1]"
+ else
+ pass
+ end
+end
+
+
+
+# -----------------------------------------------------------------------
+# Setup test environment and do some preliminary checks
+# -----------------------------------------------------------------------
+
+# Make sure no environment is active
+set -e SPACK_ENV
+true # ignore failing `set -e`
+
+# Source setup-env.sh before tests
+set -gx QA_DIR (dirname (status --current-filename))
+source $QA_DIR/../setup-env.fish
+
+
+
+# -----------------------------------------------------------------------
+# Instead of invoking the module and cd commands, we print the arguments that
+# Spack invokes the command with, so we can check that Spack passes the expected
+# arguments in the tests below.
+#
+# We make that happen by defining the fish functions below. NOTE: these overwrite
+# existing functions => define them last
+# -----------------------------------------------------------------------
+
+
+function module
+ echo "module $argv"
+end
+
+function cd
+ echo "cd $argv"
+end
+
+
+allocate_testing_global
+
+
+
+# -----------------------------------------------------------------------
+# Let the testing begin!
+# -----------------------------------------------------------------------
+
+
+title "Testing setup-env.fish with $_sp_shell"
+
+# spack command is now available
+spt_succeeds which spack
+
+
+# create a fake mock package install and store its location for later
+title "Setup"
+echo "Creating a mock package installation"
+spack -m install --fake a
+
+# create a test environment for testing environment commands
+echo "Creating a mock environment"
+spack env create spack_test_env
+
+# ensure that we uninstall b on exit
+function spt_cleanup
+
+ set trapped_error false
+ if test $status -ne 0
+ set trapped_error true
+ end
+
+ echo "Removing test environment before exiting."
+ spack env deactivate 2>&1 > /dev/null
+ spack env rm -y spack_test_env
+
+ title "Cleanup"
+ echo "Removing test packages before exiting."
+ spack -m uninstall -yf b a
+
+ echo
+ echo "$__spt_success tests succeeded."
+ echo "$__spt_errors tests failed."
+
+ if test "$trapped_error" = false
+ echo "Exited due to an error."
+ end
+
+ if test "$__spt_errors" -eq 0
+ if test "$trapped_error" = false
+ pass
+ exit 0
+ else
+ fail
+ exit 1
+ end
+ else
+ fail
+ exit 1
+ end
+
+ delete_testing_global
+end
+
+trap spt_cleanup EXIT
+
+
+
+# -----------------------------------------------------------------------
+# Test all spack commands with special env support
+# -----------------------------------------------------------------------
+title 'Testing `spack`'
+spt_contains 'usage: spack ' spack
+spt_contains "usage: spack " spack -h
+spt_contains "usage: spack " spack help
+spt_contains "usage: spack " spack -H
+spt_contains "usage: spack " spack help --all
+
+title 'Testing `spack cd`'
+spt_contains "usage: spack cd " spack cd -h
+spt_contains "usage: spack cd " spack cd --help
+spt_contains "cd $b_install" spack cd -i b
+
+title 'Testing `spack module`'
+spt_contains "usage: spack module " spack -m module -h
+spt_contains "usage: spack module " spack -m module --help
+spt_contains "usage: spack module " spack -m module
+
+title 'Testing `spack load`'
+set _b_loc (spack -m location -i b)
+set _b_ld $_b_loc"/lib"
+set _a_loc (spack -m location -i a)
+set _a_ld $_a_loc"/lib"
+
+spt_contains "set -gx LD_LIBRARY_PATH $_b_ld" spack -m load --only package --fish b
+spt_succeeds spack -m load b
+# test a variable MacOS clears and one it doesn't for recursive loads
+spt_contains "set -gx LD_LIBRARY_PATH $_a_ld:$_b_ld" spack -m load --fish a
+spt_contains "set -gx LIBRARY_PATH $_a_ld:$_b_ld" spack -m load --fish a
+spt_succeeds spack -m load --only dependencies a
+spt_succeeds spack -m load --only package a
+spt_fails spack -m load d
+spt_contains "usage: spack load " spack -m load -h
+spt_contains "usage: spack load " spack -m load -h d
+spt_contains "usage: spack load " spack -m load --help
+
+title 'Testing `spack unload`'
+spack -m load b a # setup
+# spt_contains "module unload $b_module" spack -m unload b
+spt_succeeds spack -m unload b
+spt_succeeds spack -m unload --all
+spack -m unload --all # cleanup
+spt_fails spack -m unload -l
+# spt_contains "module unload -l --arg $b_module" spack -m unload -l --arg b
+spt_fails spack -m unload d
+spt_contains "usage: spack unload " spack -m unload -h
+spt_contains "usage: spack unload " spack -m unload -h d
+spt_contains "usage: spack unload " spack -m unload --help
+
+title 'Testing `spack env`'
+spt_contains "usage: spack env " spack env -h
+spt_contains "usage: spack env " spack env --help
+
+title 'Testing `spack env list`'
+spt_contains " spack env list " spack env list -h
+spt_contains " spack env list " spack env list --help
+
+title 'Testing `spack env activate`'
+spt_contains "No such environment:" spack env activate no_such_environment
+spt_contains "usage: spack env activate " spack env activate
+spt_contains "usage: spack env activate " spack env activate -h
+spt_contains "usage: spack env activate " spack env activate --help
+
+title 'Testing `spack env deactivate`'
+spt_contains "Error: No environment is currently active" spack env deactivate
+spt_contains "usage: spack env deactivate " spack env deactivate no_such_environment
+spt_contains "usage: spack env deactivate " spack env deactivate -h
+spt_contains "usage: spack env deactivate " spack env deactivate --help
+
+title 'Testing activate and deactivate together'
+echo "Testing 'spack env activate spack_test_env'"
+spack env activate spack_test_env
+is_set SPACK_ENV
+
+echo "Testing 'spack env deactivate'"
+spack env deactivate
+is_not_set SPACK_ENV
+
+echo "Testing 'spack env activate spack_test_env'"
+spack env activate spack_test_env
+is_set SPACK_ENV
+
+echo "Testing 'despacktivate'"
+despacktivate
+is_not_set SPACK_ENV
+
+#
+# NOTE: `--prompt` on fish does nothing => currently not implemented.
+#
+
+# echo "Testing 'spack env activate --prompt spack_test_env'"
+# spack env activate --prompt spack_test_env
+# is_set SPACK_ENV
+# is_set SPACK_OLD_PS1
+#
+# echo "Testing 'despacktivate'"
+# despacktivate
+# is_not_set SPACK_ENV
+# is_not_set SPACK_OLD_PS1
diff --git a/share/spack/setup-env.fish b/share/spack/setup-env.fish
new file mode 100755
index 0000000000..a534f09ee9
--- /dev/null
+++ b/share/spack/setup-env.fish
@@ -0,0 +1,723 @@
+# Copyright 2013-2019 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 the friendly
+# interactive shell (fish). This includes module 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:
+#
+# source /path/to/spack/share/spack/setup-env.fish
+#
+#################################################################################
+# 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 load 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 modules and load 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 dotfiles.
+#################################################################################
+
+
+#
+# Test for STDERR-NOCARET feature: if this is off, fish will redirect stderr to
+# a file named in the string after `^`
+#
+
+
+if status test-feature stderr-nocaret
+else
+ echo "WARNING: you have not enabled the 'stderr-nocaret' feature."
+ echo "This means that you have to escape the caret (^) character when defining specs."
+ echo "Consider enabling stderr-nocaret: https://fishshell.com/docs/current/index.html#featureflags"
+end
+
+
+
+#
+# SPACK wrapper function, preprocessing arguments and flags.
+#
+
+
+function spack -d "wrapper for the `spack` command"
+
+
+#
+# DEFINE SUPPORT FUNCTIONS HERE
+#
+
+
+#
+# ALLOCATE_SP_SHARED, and DELETE_SP_SHARED allocate (and delete) temporary
+# global variables
+#
+
+
+function allocate_sp_shared -d "allocate shared (global variables)"
+ set -gx __sp_remaining_args
+ set -gx __sp_subcommand_args
+ set -gx __sp_module_args
+ set -gx __sp_stat
+ set -gx __sp_stdout
+ set -gx __sp_stderr
+end
+
+
+
+function delete_sp_shared -d "deallocate shared (global variables)"
+ set -e __sp_remaining_args
+ set -e __sp_subcommand_args
+ set -e __sp_module_args
+ set -e __sp_stat
+ set -e __sp_stdout
+ set -e __sp_stderr
+end
+
+
+
+
+#
+# STREAM_ARGS and SHIFT_ARGS: helper functions manipulating the `argv` array:
+# -> STREAM_ARGS: echos the `argv` array element-by-element
+# -> SHIFT_ARGS: echos the `argv` array element-by-element starting with the
+# second element. If `argv` has only one element, echo the
+# empty string `""`.
+# NOTE: while `stream_args` is not strictly necessary, it adds a nice symmetry
+# to `shift_args`
+#
+
+function stream_args -d "echos args as a stream"
+ # return the elements of `$argv` as an array
+ # -> since we want to be able to call it as part of `set x (shift_args
+ # $x)`, we return these one-at-a-time using echo... this means that the
+ # sub-command stream will correctly concatenate the output into an array
+ for elt in $argv
+ echo $elt
+ end
+end
+
+
+function shift_args -d "simulates bash shift"
+ #
+ # Returns argv[2..-1] (as an array)
+ # -> if argv has only 1 element, then returns the empty string. This
+ # simulates the behavior of bash `shift`
+ #
+
+ if test -z "$argv[2]"
+ # there are no more element, returning the empty string
+ echo ""
+ else
+ # return the next elements `$argv[2..-1]` as an array
+ # -> since we want to be able to call it as part of `set x (shift_args
+ # $x)`, we return these one-at-a-time using echo... this means that
+ # the sub-command stream will correctly concatenate the output into
+ # an array
+ for elt in $argv[2..-1]
+ echo $elt
+ end
+ end
+
+end
+
+
+
+
+#
+# CAPTURE_ALL: helper function used to capture stdout, stderr, and status
+# -> CAPTURE_ALL: there is a bug in fish, that prevents stderr re-capture
+# from nested command substitution:
+# https://github.com/fish-shell/fish-shell/issues/6459
+#
+
+function capture_all
+ begin;
+ begin;
+ eval $argv[1]
+ set $argv[2] $status # read sets the `status` flag => capture here
+ end 2>| read -z __err
+ end 1>| read -z __out
+
+ # output arrays
+ set $argv[3] (echo $__out | string split \n)
+ set $argv[4] (echo $__err | string split \n)
+
+ return 0
+end
+
+
+
+
+#
+# GET_SP_FLAGS, and GET_MOD_ARGS: support functions for extracting arguments and
+# flags. Note bash's `shift` operation is simulated by the `__sp_remaining_args`
+# array which is roughly equivalent to `$@` in bash.
+#
+
+function get_sp_flags -d "return leading flags"
+ #
+ # Accumulate initial flags for main spack command. NOTE: Sets the external
+ # array: `__sp_remaining_args` containing all unprocessed arguments.
+ #
+
+ # initialize argument counter
+ set -l i 1
+
+ # iterate over elements (`elt`) in `argv` array
+ for elt in $argv
+
+ # match element `elt` of `argv` array to check if it has a leading dash
+ if echo $elt | string match -r -q "^-"
+ # by echoing the current `elt`, the calling stream accumulates list
+ # of valid flags. NOTE that this can also be done by adding to an
+ # array, but fish functions can only return integers, so this is the
+ # most elegant solution.
+ echo $elt
+ else
+ # bash compatibility: stop when the match first fails. Upon failure,
+ # we pack the remainder of `argv` into a global `__sp_remaining_args`
+ # array (`i` tracks the index of the next element).
+ set __sp_remaining_args (stream_args $argv[$i..-1])
+ return
+ end
+
+ # increment argument counter: used in place of bash's `shift` command
+ set -l i (math $i+1)
+
+ end
+
+ # if all elements in `argv` are matched, make sure that `__sp_remaining_args`
+ # is deleted (this might be overkill...).
+ set -e __sp_remaining_args
+end
+
+
+
+#
+# CHECK_SP_FLAGS, CONTAINS_HELP_FLAGS, CHECK_ENV_ACTIVATE_FLAGS, and
+# CHECK_ENV_DEACTIVATE_FLAGS: support functions for checking arguments and flags.
+#
+
+function check_sp_flags -d "check spack flags for h/V flags"
+ #
+ # Check if inputs contain h or V flags.
+ #
+
+ # combine argument array into single string (space seperated), to be passed
+ # to regular expression matching (`string match -r`)
+ set -l _a "$argv"
+
+ # skip if called with blank input. Notes: [1] (cf. EOF)
+ if test -n "$_a"
+ if echo $_a | string match -r -q ".*h.*"
+ return 0
+ end
+ if echo $_a | string match -r -q ".*V.*"
+ return 0
+ end
+ end
+
+ return 1
+end
+
+
+
+function check_env_activate_flags -d "check spack env subcommand flags for -h, --sh, --csh, or --fish"
+ #
+ # Check if inputs contain -h, --sh, --csh, or --fish
+ #
+
+ # combine argument array into single string (space seperated), to be passed
+ # to regular expression matching (`string match -r`)
+ set -l _a "$argv"
+
+ # skip if called with blank input. Notes: [1] (cf. EOF)
+ if test -n "$_a"
+ # looks for a single `-h` (possibly surrounded by spaces)
+ if echo $_a | string match -r -q " *-h *"
+ return 0
+ end
+
+ # looks for a single `--sh` (possibly surrounded by spaces)
+ if echo $_a | string match -r -q " *--sh *"
+ return 0
+ end
+
+ # looks for a single `--csh` (possibly surrounded by spaces)
+ if echo $_a | string match -r -q " *--csh *"
+ return 0
+ end
+
+ # looks for a single `--fish` (possibly surrounded by spaces)
+ if echo $_a | string match -r -q " *--fish *"
+ return 0
+ end
+
+ end
+
+ return 1
+end
+
+
+function check_env_deactivate_flags -d "check spack env subcommand flags for --sh, --csh, or --fish"
+ #
+ # Check if inputs contain -h, --sh, --csh, or --fish
+ #
+
+ # combine argument array into single string (space seperated), to be passed
+ # to regular expression matching (`string match -r`)
+ set -l _a "$argv"
+
+ # skip if called with blank input. Notes: [1] (cf. EOF)
+ if test -n "$_a"
+
+ # TODO: should this crash (we're clearly using fish, not bash, here)?
+ # looks for a single `--sh` (possibly surrounded by spaces)
+ if echo $_a | string match -r -q " *--sh *"
+ return 0
+ end
+
+ # TODO: should this crash (we're clearly using fish, not csh, here)?
+ # looks for a single `--csh` (possibly surrounded by spaces)
+ if echo $_a | string match -r -q " *--csh *"
+ return 0
+ end
+
+ # looks for a single `--fish` (possibly surrounded by spaces)
+ if echo $_a | string match -r -q " *--fish *"
+ return 0
+ end
+
+ end
+
+ return 1
+end
+
+
+
+
+#
+# SPACK RUNNER function, this does all the work!
+#
+
+
+function spack_runner -d "Runner function for the `spack` wrapper"
+
+
+ #
+ # Accumulate initial flags for main spack command
+ #
+
+ set __sp_remaining_args # remaining (unparsed) arguments
+ set -l sp_flags (get_sp_flags $argv) # sets __sp_remaining_args
+
+
+ #
+ # h and V flags don't require further output parsing.
+ #
+
+ if check_sp_flags $sp_flags
+ command spack $sp_flags $__sp_remaining_args
+ return 0
+ end
+
+
+ #
+ # Isolate subcommand and subcommand specs. Notes: [1] (cf. EOF)
+ #
+
+ set -l sp_subcommand ""
+
+ if test -n "$__sp_remaining_args[1]"
+ set sp_subcommand $__sp_remaining_args[1]
+ set __sp_remaining_args (shift_args $__sp_remaining_args) # simulates bash shift
+ end
+
+ set -l sp_spec $__sp_remaining_args
+
+
+ #
+ # Filter out cd, env, and load and unload. For any other commands, just run
+ # the spack command as is.
+ #
+
+ switch $sp_subcommand
+
+ # CASE: spack subcommand is `cd`: if the sub command arg is `-h`, nothing
+ # further needs to be done. Otherwise, test the location referring the
+ # subcommand and cd there (if it exists).
+
+ case "cd"
+
+ set -l sp_arg ""
+
+ # Extract the first subcommand argument. Notes: [1] (cf. EOF)
+ if test -n "$__sp_remaining_args[1]"
+ set sp_arg $__sp_remaining_args[1]
+ set __sp_remaining_args (shift_args $__sp_remaining_args) # simulates bash shift
+ end
+
+ # Notes: [2] (cf. EOF)
+ if test "x$sp_arg" = "x-h"; or test "x$sp_arg" = "x--help"
+ # nothing more needs to be done for `-h` or `--help`
+ command spack cd -h
+ else
+ # extract location using the subcommand (fish `(...)`)
+ set -l LOC (command spack location $sp_arg $__sp_remaining_args)
+
+ # test location and cd if exists:
+ if test -d "$LOC"
+ cd $LOC
+ else
+ return 1
+ end
+
+ end
+
+ return 0
+
+
+ # CASE: spack subcommand is `env`. Here we get the spack runtime to
+ # supply the appropriate shell commands for setting the environment
+ # varibles. These commands are then run by fish (using the `capture_all`
+ # function, instead of a command substitution).
+
+ case "env"
+
+ set -l sp_arg ""
+
+ # Extract the first subcommand argument. Notes: [1] (cf. EOF)
+ if test -n "$__sp_remaining_args[1]"
+ set sp_arg $__sp_remaining_args[1]
+ set __sp_remaining_args (shift_args $__sp_remaining_args) # simulates bash shift
+ end
+
+ # Notes: [2] (cf. EOF)
+ if test "x$sp_arg" = "x-h"; or test "x$sp_arg" = "x--help"
+ # nothing more needs to be done for `-h` or `--help`
+ command spack env -h
+ else
+ switch $sp_arg
+ case "activate"
+ set -l _a (stream_args $__sp_remaining_args)
+
+ if check_env_activate_flags $_a
+ # no args or args contain -h/--help, --sh, or --csh: just execute
+ command spack env activate $_a
+ else
+ # actual call to activate: source the output
+ set -l sp_env_cmd "command spack $sp_flags env activate --fish $__sp_remaining_args"
+ capture_all $sp_env_cmd __sp_stat __sp_stdout __sp_stderr
+ eval $__sp_stdout
+ if test -n "$__sp_stderr"
+ echo -s \n$__sp_stderr 1>&2 # current fish bug: handle stderr manually
+ end
+ end
+
+ case "deactivate"
+ set -l _a (stream_args $__sp_remaining_args)
+
+ if check_env_deactivate_flags $_a
+ # just execute the command if --sh, --csh, or --fish are provided
+ command spack env deactivate $_a
+
+ # Test of further (unparsed arguments). Any other
+ # arguments are an error or help, so just run help
+ # -> TODO: This should throw and error but leave as is
+ # for compatibility with setup-env.sh
+ # -> Notes: [1] (cf. EOF).
+ else if test -n "$__sp_remaining_args"
+ command spack env deactivate -h
+ else
+ # no args: source the output of the command
+ set -l sp_env_cmd "command spack $sp_flags env deactivate --fish"
+ capture_all $sp_env_cmd __sp_stat __sp_stdout __sp_stderr
+ eval $__sp_stdout
+ if test $__sp_stat -ne 0
+ if test -n "$__sp_stderr"
+ echo -s \n$__sp_stderr 1>&2 # current fish bug: handle stderr manually
+ end
+ return 1
+ end
+ end
+
+ case "*"
+ # if $__sp_remaining_args is empty, then don't include it
+ # as argument (otherwise it will be confused as a blank
+ # string input!)
+ if test -n "$__sp_remaining_args"
+ command spack env $sp_arg $__sp_remaining_args
+ else
+ command spack env $sp_arg
+ end
+ end
+ end
+
+
+ # CASE: spack subcommand is either `load`, or `unload`. These statements
+ # deal with the technical details of actually using modules. Especially
+ # to deal with the substituting latest version numbers to the module
+ # command.
+
+ case "load" or "unload"
+
+ set -l _a (stream_args $__sp_remaining_args)
+
+ if check_env_activate_flags $_a
+ # no args or args contain -h/--help, --sh, or --csh: just execute
+ command spack $sp_flags $sp_subcommand $__sp_remaining_args
+ else
+ # actual call to activate: source the output
+ set -l sp_env_cmd "command spack $sp_flags $sp_subcommand --fish $__sp_remaining_args"
+ capture_all $sp_env_cmd __sp_stat __sp_stdout __sp_stderr
+ eval $__sp_stdout
+ if test $__sp_stat -ne 0
+ if test -n "$__sp_stderr"
+ echo -s \n$__sp_stderr 1>&2 # current fish bug: handle stderr manually
+ end
+ return 1
+ end
+ end
+
+
+ # CASE: Catch-all
+
+ case "*"
+ command spack $argv
+
+ end
+
+ return 0
+end
+
+
+
+
+#
+# RUN SPACK_RUNNER HERE
+#
+
+
+#
+# Allocate temporary global variables used for return extra arguments from
+# functions. NOTE: remember to call delete_sp_shared whenever returning from
+# this function.
+#
+
+allocate_sp_shared
+
+
+#
+# Run spack command using the spack_runner.
+#
+
+spack_runner $argv
+# Capture state of spack_runner (returned below)
+set -l stat $status
+
+
+#
+# Delete temprary global variabels allocated in `allocated_sp_shared`.
+#
+
+delete_sp_shared
+
+
+
+return $stat
+
+end
+
+
+
+#################################################################################
+# Prepends directories to path, if they exist.
+# pathadd /path/to/dir # add to PATH
+# or pathadd OTHERPATH /path/to/dir # add to OTHERPATH
+#################################################################################
+function spack_pathadd -d "Add path to specified variable (defaults to PATH)"
+ #
+ # Adds (existing only) paths to specified (defaults to PATH)
+ # variable. Does not warn attempting to add non-existing path. This is not a
+ # bug because the MODULEPATH setup tries add all possible compatible systems
+ # and therefore sp_multi_pathadd relies on this function failing silently.
+ #
+
+ # If no variable name is supplied, just append to PATH otherwise append to
+ # that variable.
+ # -> Notes: [1] (cf. EOF).
+ if test -n "$argv[2]"
+ set pa_varname $argv[1]
+ set pa_new_path $argv[2]
+ else
+ true # this is a bit of a strange hack! Notes: [3] (cf EOF).
+ set pa_varname PATH
+ set pa_new_path $argv[1]
+ end
+
+ set pa_oldvalue $$pa_varname
+
+ # skip path is not existing directory
+ # -> Notes: [1] (cf. EOF).
+ if test -d "$pa_new_path"
+
+ # combine argument array into single string (space seperated), to be
+ # passed to regular expression matching (`string match -r`)
+ set -l _a "$pa_oldvalue"
+
+ # skip path if it is already contained in the variable
+ # note spaces in regular expression: we're matching to a space delimited
+ # list of paths
+ if not echo $_a | string match -q -r " *$pa_new_path *"
+ if test -n "$pa_oldvalue"
+ set $pa_varname $pa_new_path $pa_oldvalue
+ else
+ true # this is a bit of a strange hack! Notes: [3] (cf. EOF)
+ set $pa_varname $pa_new_path
+ end
+ end
+ end
+end
+
+
+function sp_multi_pathadd -d "Helper for adding module-style paths by incorporating compatible systems into pathadd" --inherit-variable _sp_compatible_sys_types
+ #
+ # Calls spack_pathadd in path inputs, adding all compatible system types
+ # (sourced from $_sp_compatible_sys_types) to input paths.
+ #
+
+ for pth in $argv[2]
+ for systype in $_sp_compatible_sys_types
+ spack_pathadd $argv[1] "$pth/$systype"
+ end
+ end
+end
+
+
+
+#
+# Figure out where this file is. Below code only needs to work in fish
+#
+set -l sp_source_file (status -f) # name of current file
+
+
+
+#
+# Find root directory and add bin to path.
+#
+set -l sp_share_dir (realpath (dirname $sp_source_file))
+set -l sp_prefix (realpath (dirname (dirname $sp_share_dir)))
+spack_pathadd PATH "$sp_prefix/bin"
+set -xg SPACK_ROOT $sp_prefix
+
+
+
+#
+# No need to determine which shell is being used (obviously it's fish)
+#
+set -xg SPACK_SHELL "fish"
+set -xg _sp_shell "fish"
+
+
+
+
+#
+# Check whether we need environment-variables (module) <= `use` is not available
+#
+set -l need_module "no"
+if not functions -q use; and not functions -q module
+ set need_module "yes"
+end
+
+
+
+#
+# Make environment-modules available to shell
+#
+function sp_apply_shell_vars -d "applies expressions of the type `a='b'` as `set a b`"
+
+ # convert `a='b' to array variable `a b`
+ set -l expr_token (string trim -c "'" (string split "=" $argv))
+
+ # run set command to takes, converting lists of type `a:b:c` to array
+ # variables `a b c` by splitting around the `:` character
+ set -xg $expr_token[1] (string split ":" $expr_token[2])
+end
+
+
+if test "$need_module" = "yes"
+ set -l sp_shell_vars (command spack --print-shell-vars sh,modules)
+
+ for sp_var_expr in $sp_shell_vars
+ sp_apply_shell_vars $sp_var_expr
+ end
+
+ # _sp_module_prefix is set by spack --print-sh-vars
+ if test "$_sp_module_prefix" != "not_installed"
+ set -xg MODULE_PREFIX $_sp_module_prefix
+ spack_pathadd PATH "$MODULE_PREFIX/bin"
+ end
+
+else
+
+ set -l sp_shell_vars (command spack --print-shell-vars sh)
+
+ for sp_var_expr in $sp_shell_vars
+ sp_apply_shell_vars $sp_var_expr
+ end
+
+end
+
+if test "$need_module" = "yes"
+ function module -d "wrapper for the `module` command to point at Spack's modules instance" --inherit-variable MODULE_PREFIX
+ eval $MODULE_PREFIX/bin/modulecmd $SPACK_SHELL $argv
+ end
+end
+
+
+
+#
+# set module system roots
+#
+
+# Search of MODULESPATHS by trying all possible compatible system types as
+# module roots.
+if test -z "$MODULEPATH"
+ set -gx MODULEPATH
+end
+sp_multi_pathadd MODULEPATH $_sp_tcl_roots
+
+
+
+#
+# NOTES
+#
+# [1]: `test -n` requires exactly 1 argument. If `argv` is undefined, or if it
+# is an array, `test -n $argv` is unpredictable. Instead, encapsulate
+# `argv` in a string, and test the string.
+#
+# [2]: `test "$a" = "$b$` is dangerous if `a` and `b` contain flags at index 1,
+# as `test $a` can be interpreted as `test $a[1] $a[2..-1]`. Solution is to
+# prepend a non-flag character, eg: `test "x$a" = "x$b"`.
+#
+# [3]: When the test in the if statement fails, the `status` flag is set to 1.
+# `true` here manuallt resets the value of `status` to 0. Since `set`
+# passes `status` along, we thus avoid the function returning 1 by mistake.
diff --git a/share/spack/spack-completion.bash b/share/spack/spack-completion.bash
index 830958fe42..11bd906ad0 100755
--- a/share/spack/spack-completion.bash
+++ b/share/spack/spack-completion.bash
@@ -732,14 +732,14 @@ _spack_env() {
_spack_env_activate() {
if $list_options
then
- SPACK_COMPREPLY="-h --help --sh --csh -v --with-view -V --without-view -d --dir -p --prompt"
+ SPACK_COMPREPLY="-h --help --sh --csh --fish -v --with-view -V --without-view -d --dir -p --prompt"
else
_environments
fi
}
_spack_env_deactivate() {
- SPACK_COMPREPLY="-h --help --sh --csh"
+ SPACK_COMPREPLY="-h --help --sh --csh --fish"
}
_spack_env_create() {
@@ -997,7 +997,7 @@ _spack_list() {
_spack_load() {
if $list_options
then
- SPACK_COMPREPLY="-h --help -r --dependencies --sh --csh --first --only"
+ SPACK_COMPREPLY="-h --help -r --dependencies --sh --csh --fish --first --only"
else
_installed_packages
fi
@@ -1445,7 +1445,7 @@ _spack_uninstall() {
_spack_unload() {
if $list_options
then
- SPACK_COMPREPLY="-h --help --sh --csh -a --all"
+ SPACK_COMPREPLY="-h --help --sh --csh --fish -a --all"
else
_installed_packages
fi