From 221cf6acb932442f5964243eb3413a1d921920c8 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Sat, 16 Aug 2014 14:53:57 -0700 Subject: [PATCH] Consolidate most module code into spack.modules and spack.cmd.module - One file with all the module classes (spack/modules.py) - Has an EnvModule superclass that does most of the work and consolidates common code - Subclasses have specializations for different module systems (TclModule, Dotkit) - One command (spack module) for all the types of modules to use - the one command is used by the scripts, only need to maintain in one place - has some subcommands for different module types, but they're handled mostly generically. - Consolidate zsh support into a single setup-env.sh script. --- bin/spack | 7 + lib/spack/spack/__init__.py | 2 - lib/spack/spack/cmd/load.py | 24 +-- lib/spack/spack/cmd/{dotkit.py => module.py} | 69 +++--- lib/spack/spack/cmd/tclmodule.py | 99 --------- lib/spack/spack/cmd/unload.py | 8 +- lib/spack/spack/cmd/unuse.py | 8 +- lib/spack/spack/cmd/use.py | 22 +- lib/spack/spack/hooks/dotkit.py | 58 +---- lib/spack/spack/hooks/tclmodule.py | 61 +----- lib/spack/spack/modules.py | 216 +++++++++++++++++++ share/spack/{setup-env.bash => setup-env.sh} | 115 +++++----- share/spack/setup-env.zsh | 122 ----------- 13 files changed, 349 insertions(+), 462 deletions(-) rename lib/spack/spack/cmd/{dotkit.py => module.py} (60%) delete mode 100644 lib/spack/spack/cmd/tclmodule.py create mode 100644 lib/spack/spack/modules.py rename share/spack/{setup-env.bash => setup-env.sh} (56%) delete mode 100755 share/spack/setup-env.zsh diff --git a/bin/spack b/bin/spack index c63178b191..f2b5d1d583 100755 --- a/bin/spack +++ b/bin/spack @@ -75,6 +75,13 @@ for cmd in spack.cmd.commands: module = spack.cmd.get_module(cmd) subparser = subparsers.add_parser(cmd, help=module.description) module.setup_parser(subparser) + +# Just print help and exit if run with no arguments at all +if len(sys.argv) == 1: + parser.print_help() + sys.exit(1) + +# actually parse the args. args = parser.parse_args() # Set up environment based on args. diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py index 0b69ccde38..be38971c30 100644 --- a/lib/spack/spack/__init__.py +++ b/lib/spack/spack/__init__.py @@ -58,8 +58,6 @@ stage_path = join_path(var_path, "stage") install_path = join_path(prefix, "opt") share_path = join_path(prefix, "share", "spack") -dotkit_path = join_path(share_path, "dotkit") -tclmodule_path = join_path(share_path, "modules") # # Set up the packages database. diff --git a/lib/spack/spack/cmd/load.py b/lib/spack/spack/cmd/load.py index 1389740df1..5bc6b15784 100644 --- a/lib/spack/spack/cmd/load.py +++ b/lib/spack/spack/cmd/load.py @@ -23,28 +23,16 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## import argparse -import llnl.util.tty as tty -import spack +import spack.modules -description ="Add package to environment using module." +description ="Add package to environment using modules." def setup_parser(subparser): + """Parser is only constructed so that this prints a nice help + message with -h. """ subparser.add_argument( - 'spec', nargs=argparse.REMAINDER, help='Spec of package to add.') - - -def print_help(): - tty.msg("Spack module support is not initialized.", - "", - "To use module with Spack, you must first run the command", - "below, which you can copy and paste:", - "", - "For bash:", - " . %s/setup-env.bash" % spack.share_path, - "", - "ksh/csh/tcsh shells are currently unsupported", - "") + 'spec', nargs=argparse.REMAINDER, help='Spec of package to load with modules.') def load(parser, args): - print_help() + spack.modules.print_help() diff --git a/lib/spack/spack/cmd/dotkit.py b/lib/spack/spack/cmd/module.py similarity index 60% rename from lib/spack/spack/cmd/dotkit.py rename to lib/spack/spack/cmd/module.py index 7a691ae5c0..ead3b9abac 100644 --- a/lib/spack/spack/cmd/dotkit.py +++ b/lib/spack/spack/cmd/module.py @@ -32,34 +32,43 @@ from llnl.util.filesystem import mkdirp import spack.cmd -import spack.hooks.dotkit +import spack.modules +from spack.util.string import * + from spack.spec import Spec +description ="Manipulate modules and dotkits." + +module_types = { + 'dotkit' : spack.modules.Dotkit, + 'tcl' : spack.modules.TclModule +} -description ="Find dotkits for packages if they exist." def setup_parser(subparser): - subparser.add_argument( - '--refresh', action='store_true', help='Regenerate all dotkits') + sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='module_command') - subparser.add_argument( - 'spec', nargs=argparse.REMAINDER, help='spec to find a dotkit for.') + refresh_parser = sp.add_parser('refresh', help='Regenerate all module files.') + + find_parser = sp.add_parser('find', help='Find module files for packages.') + find_parser.add_argument( + 'module_type', help="Type of module to find file for. [" + '|'.join(module_types) + "]") + find_parser.add_argument('spec', nargs='+', help='spec to find a module file for.') -def dotkit_find(parser, args): - if not args.spec: - parser.parse_args(['dotkit', '-h']) - - spec = spack.cmd.parse_specs(args.spec) - if len(spec) > 1: +def module_find(mtype, spec_array): + specs = spack.cmd.parse_specs(spec_array) + if len(specs) > 1: tty.die("You can only pass one spec.") - spec = spec[0] + spec = specs[0] if not spack.db.exists(spec.name): tty.die("No such package: %s" % spec.name) - specs = [s for s in spack.db.installed_package_specs() if s.satisfies(spec)] + if mtype not in module_types: + tty.die("Invalid module type: '%s'. Options are " + comma_and(module_types)) + specs = [s for s in spack.db.installed_package_specs() if s.satisfies(spec)] if len(specs) == 0: tty.die("No installed packages match spec %s" % spec) @@ -69,31 +78,27 @@ def dotkit_find(parser, args): sys.stderr.write(s.tree(color=True)) sys.exit(1) - match = specs[0] - if not os.path.isfile(spack.hooks.dotkit.dotkit_file(match.package)): + mt = module_types[mtype] + mod = mt(spec.package) + if not os.path.isfile(mod.file_name): tty.die("No dotkit is installed for package %s." % spec) - print match.format('$_$@$+$%@$=$#') + print mod.file_name -def dotkit_refresh(parser, args): - query_specs = spack.cmd.parse_specs(args.spec) +def module_refresh(): + shutil.rmtree(spack.dotkit_path, ignore_errors=False) + mkdirp(spack.dotkit_path) specs = spack.db.installed_package_specs() - if query_specs: - specs = [s for s in specs - if any(s.satisfies(q) for q in query_specs)] - else: - shutil.rmtree(spack.dotkit_path, ignore_errors=False) - mkdirp(spack.dotkit_path) - for spec in specs: - spack.hooks.dotkit.post_install(spec.package) + for mt in module_types: + mt(spec.package).write() +def module(parser, args): + if args.module_command == 'refresh': + module_refresh() -def dotkit(parser, args): - if args.refresh: - dotkit_refresh(parser, args) - else: - dotkit_find(parser, args) + elif args.module_command == 'find': + module_find(args.module_type, args.spec) diff --git a/lib/spack/spack/cmd/tclmodule.py b/lib/spack/spack/cmd/tclmodule.py deleted file mode 100644 index 270ee65b7b..0000000000 --- a/lib/spack/spack/cmd/tclmodule.py +++ /dev/null @@ -1,99 +0,0 @@ -############################################################################## -# Copyright (c) 2013, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Written by David Beckingsale, david@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://scalability-llnl.github.io/spack -# Please also see the LICENSE file for our notice and the LGPL. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License (as published by -# the Free Software Foundation) version 2.1 dated February 1999. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and -# conditions of the GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -############################################################################## -import sys -import os -import shutil -import argparse - -import llnl.util.tty as tty -from llnl.util.lang import partition_list -from llnl.util.filesystem import mkdirp - -import spack.cmd -import spack.hooks.tclmodule -from spack.spec import Spec - - -description ="Find modules for packages if they exist." - -def setup_parser(subparser): - subparser.add_argument( - '--refresh', action='store_true', help='Regenerate all modules') - - subparser.add_argument( - 'spec', nargs=argparse.REMAINDER, help='spec to find a module for.') - - -def module_find(parser, args): - if not args.spec: - parser.parse_args(['tclmodule', '-h']) - - spec = spack.cmd.parse_specs(args.spec) - if len(spec) > 1: - tty.die("You can only pass one spec.") - spec = spec[0] - - if not spack.db.exists(spec.name): - tty.die("No such package: %s" % spec.name) - - specs = [s for s in spack.db.installed_package_specs() if s.satisfies(spec)] - - if len(specs) == 0: - tty.die("No installed packages match spec %s" % spec) - - if len(specs) > 1: - tty.error("Multiple matches for spec %s. Choose one:" % spec) - for s in specs: - sys.stderr.write(s.tree(color=True)) - sys.exit(1) - - match = specs[0] - if not os.path.isfile(spack.hooks.tclmodule.module_file(match.package)): - tty.die("No module is installed for package %s." % spec) - - print match.format('$_$@$+$%@$=$#') - - -def module_refresh(parser, args): - query_specs = spack.cmd.parse_specs(args.spec) - - specs = spack.db.installed_package_specs() - if query_specs: - specs = [s for s in specs - if any(s.satisfies(q) for q in query_specs)] - else: - shutil.rmtree(spack.tclmodule_path, ignore_errors=False) - mkdirp(spack.tclmodule_path) - - for spec in specs: - spack.hooks.tclmodule.post_install(spec.package) - - - -def tclmodule(parser, args): - if args.refresh: - module_refresh(parser, args) - else: - module_find(parser, args) diff --git a/lib/spack/spack/cmd/unload.py b/lib/spack/spack/cmd/unload.py index df009c840b..24e49b3f24 100644 --- a/lib/spack/spack/cmd/unload.py +++ b/lib/spack/spack/cmd/unload.py @@ -23,14 +23,16 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## import argparse -import spack.cmd.tclmodule +import spack.modules description ="Remove package from environment using module." def setup_parser(subparser): + """Parser is only constructed so that this prints a nice help + message with -h. """ subparser.add_argument( - 'spec', nargs=argparse.REMAINDER, help='Spec of package to remove.') + 'spec', nargs=argparse.REMAINDER, help='Spec of package to unload with modules.') def unload(parser, args): - spack.cmd.load.print_help() + spack.modules.print_help() diff --git a/lib/spack/spack/cmd/unuse.py b/lib/spack/spack/cmd/unuse.py index a31e16d11a..7f0b384ea0 100644 --- a/lib/spack/spack/cmd/unuse.py +++ b/lib/spack/spack/cmd/unuse.py @@ -23,14 +23,16 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## import argparse -import spack.cmd.use +import spack.modules description ="Remove package from environment using dotkit." def setup_parser(subparser): + """Parser is only constructed so that this prints a nice help + message with -h. """ subparser.add_argument( - 'spec', nargs=argparse.REMAINDER, help='Spec of package to remove.') + 'spec', nargs=argparse.REMAINDER, help='Spec of package to unuse with dotkit.') def unuse(parser, args): - spack.cmd.use.print_help() + spack.modules.print_help() diff --git a/lib/spack/spack/cmd/use.py b/lib/spack/spack/cmd/use.py index 10a0644df8..4990fea2f8 100644 --- a/lib/spack/spack/cmd/use.py +++ b/lib/spack/spack/cmd/use.py @@ -23,28 +23,16 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## import argparse -import llnl.util.tty as tty -import spack +import spack.modules description ="Add package to environment using dotkit." def setup_parser(subparser): + """Parser is only constructed so that this prints a nice help + message with -h. """ subparser.add_argument( - 'spec', nargs=argparse.REMAINDER, help='Spec of package to add.') - - -def print_help(): - tty.msg("Spack dotkit support is not initialized.", - "", - "To use dotkit with Spack, you must first run the command", - "below, which you can copy and paste:", - "", - "For bash:", - " . %s/setup-env.bash" % spack.share_path, - "", - "ksh/csh/tcsh shells are currently unsupported", - "") + 'spec', nargs=argparse.REMAINDER, help='Spec of package to use with dotkit.') def use(parser, args): - print_help() + spack.modules.print_help() diff --git a/lib/spack/spack/hooks/dotkit.py b/lib/spack/spack/hooks/dotkit.py index 10b7732353..0f46f6a2fc 100644 --- a/lib/spack/spack/hooks/dotkit.py +++ b/lib/spack/spack/hooks/dotkit.py @@ -22,62 +22,14 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## -import os -import re -import textwrap -import shutil -from contextlib import closing - -from llnl.util.filesystem import join_path, mkdirp - -import spack - - -def dotkit_file(pkg): - dk_file_name = pkg.spec.format('$_$@$%@$+$=$#') + ".dk" - return join_path(spack.dotkit_path, dk_file_name) +import spack.modules def post_install(pkg): - if not os.path.exists(spack.dotkit_path): - mkdirp(spack.dotkit_path) - - alterations = [] - for var, path in [ - ('PATH', pkg.prefix.bin), - ('MANPATH', pkg.prefix.man), - ('MANPATH', pkg.prefix.share_man), - ('LD_LIBRARY_PATH', pkg.prefix.lib), - ('LD_LIBRARY_PATH', pkg.prefix.lib64)]: - - if os.path.isdir(path): - alterations.append("dk_alter %s %s\n" % (var, path)) - - if not alterations: - return - - alterations.append("dk_alter CMAKE_PREFIX_PATH %s\n" % pkg.prefix) - - dk_file = dotkit_file(pkg) - with closing(open(dk_file, 'w')) as dk: - # Put everything in the spack category. - dk.write('#c spack\n') - - dk.write('#d %s\n' % pkg.spec.format("$_ $@")) - - # Recycle the description - if pkg.__doc__: - doc = re.sub(r'\s+', ' ', pkg.__doc__) - for line in textwrap.wrap(doc, 72): - dk.write("#h %s\n" % line) - - # Write alterations - for alter in alterations: - dk.write(alter) + dk = spack.modules.Dotkit(pkg) + dk.write() def post_uninstall(pkg): - dk_file = dotkit_file(pkg) - if os.path.exists(dk_file): - shutil.rmtree(dk_file, ignore_errors=True) - + dk = spack.modules.Dotkit(pkg) + dk.remove() diff --git a/lib/spack/spack/hooks/tclmodule.py b/lib/spack/spack/hooks/tclmodule.py index d9b4a43831..d93da3177e 100644 --- a/lib/spack/spack/hooks/tclmodule.py +++ b/lib/spack/spack/hooks/tclmodule.py @@ -22,65 +22,14 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## -import os -import re -import textwrap -import shutil -from contextlib import closing - -from llnl.util.filesystem import join_path, mkdirp - -import spack - - -def module_file(pkg): - m_file_name = pkg.spec.format('$_$@$%@$+$=$#') - return join_path(spack.tclmodule_path, m_file_name) +import spack.modules def post_install(pkg): - if not os.path.exists(spack.module_path): - mkdirp(spack.module_path) - - alterations = [] - for var, path in [ - ('PATH', pkg.prefix.bin), - ('MANPATH', pkg.prefix.man), - ('MANPATH', pkg.prefix.share_man), - ('LD_LIBRARY_PATH', pkg.prefix.lib), - ('LD_LIBRARY_PATH', pkg.prefix.lib64)]: - - if os.path.isdir(path): - alterations.append("prepend-path %s \"%s\"\n" % (var, path)) - - if not alterations: - return - - alterations.append("prepend-path CMAKE_PREFIX_PATH \"%s\"\n" % pkg.prefix) - - m_file = module_file(pkg) - with closing(open(m_file, 'w')) as m: - # Put everything in the spack category. - m.write('#%Module1.0\n') - - m.write('module-whatis \"%s\"\n\n' % pkg.spec.format("$_ $@")) - - # Recycle the description - if pkg.__doc__: - m.write('proc ModulesHelp { } {\n') - doc = re.sub(r'\s+', ' ', pkg.__doc__) - doc = re.sub(r'"', '\"', pkg.__doc__) - m.write("puts stderr \"%s\"\n" % doc) - m.write('}\n\n') - - - # Write alterations - for alter in alterations: - m.write(alter) + dk = spack.modules.TclModule(pkg) + dk.write() def post_uninstall(pkg): - m_file = module_file(pkg) - if os.path.exists(m_file): - shutil.rmtree(m_file, ignore_errors=True) - + dk = spack.modules.TclModule(pkg) + dk.remove() diff --git a/lib/spack/spack/modules.py b/lib/spack/spack/modules.py new file mode 100644 index 0000000000..8ac7e470cb --- /dev/null +++ b/lib/spack/spack/modules.py @@ -0,0 +1,216 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (as published by +# the Free Software Foundation) version 2.1 dated February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +"""This module contains code for creating environment modules, which +can include dotkits, tcl modules, lmod, and others. + +The various types of modules are installed by post-install hooks and +removed after an uninstall by post-uninstall hooks. This class +consolidates the logic for creating an abstract description of the +information that module systems need. Currently that includes a +number directories to be appended to paths in the user's environment: + + * /bin directories to be appended to PATH + * /lib* directories for LD_LIBRARY_PATH + * /man* and /share/man* directories for LD_LIBRARY_PATH + * the package prefix for CMAKE_PREFIX_PATH + +This module also includes logic for coming up with unique names for +the module files so that they can be found by the various +shell-support files in $SPACK/share/spack/setup-env.*. + +Each hook in hooks/ implements the logic for writing its specific type +of module file. +""" +__all__ = ['EnvModule', 'Dotkit', 'TclModule'] + +import os +import re +import textwrap +import shutil +from contextlib import closing + +import llnl.util.tty as tty +from llnl.util.filesystem import join_path, mkdirp + +import spack + +dotkit_path = join_path(spack.share_path, "dotkit") +tcl_mod_path = join_path(spack.share_path, "modules") + +def print_help(): + """For use by commands to tell user how to activate shell support.""" + + tty.msg("Spack module/dotkit support is not initialized.", + "", + "To use dotkit or modules with Spack, you must first run", + "one of the commands below. You can copy/paste them.", + "", + "For bash and zsh:", + " . %s/setup-env.sh" % spack.share_path, + "", + "For csh and tcsh:", + " source %s/setup-env.csh" % spack.share_path, + "") + + +class EnvModule(object): + def __init__(self, pkg=None): + # category in the modules system + # TODO: come up with smarter category names. + self.category = "spack" + + # Descriptions for the module system's UI + self.short_description = "" + self.long_description = "" + + # dict pathname -> list of directories to be prepended to in + # the module file. + self._paths = None + self.pkg = pkg + + + @property + def paths(self): + if self._paths is None: + self._paths = {} + + def add_path(self, path_name, directory): + path = self._paths.setdefault(path_name, []) + path.append(directory) + + # Add paths if they exist. + for var, directory in [ + ('PATH', self.pkg.prefix.bin), + ('MANPATH', self.pkg.prefix.man), + ('MANPATH', self.pkg.prefix.share_man), + ('LD_LIBRARY_PATH', self.pkg.prefix.lib), + ('LD_LIBRARY_PATH', self.pkg.prefix.lib64)]: + + if os.path.isdir(directory): + add_path(var, directory) + + # short description is just the package + version + # TODO: maybe packages can optionally provide it. + self.short_description = self.pkg.spec.format("$_ $@") + + # long description is the docstring with reduced whitespace. + if self.pkg.__doc__: + self.long_description = re.sub(r'\s+', ' ', self.pkg.__doc__) + + return self._paths + + + def write(self): + """Write out a module file for this object.""" + module_dir = os.path.dirname(self.file_name) + if not os.path.exists(): + mkdirp(module_dir) + + # If there are no paths, no need for a dotkit. + if not self.paths: + return + + with closing(open(self.file_name)) as f: + self._write(f) + + + def _write(self, stream): + """To be implemented by subclasses.""" + raise NotImplementedError() + + + @property + def file_name(self): + """Subclasses should implement this to return the name of the file + where this module lives.""" + return self.pkg.spec.format('$_$@$%@$+$=$#') + + + def remove(self): + mod_file = self.file_name + if os.path.exists(mod_file): + shutil.rmtree(mod_file, ignore_errors=True) + + +class Dotkit(EnvModule): + @property + def file_name(self): + spec = self.pkg.spec + return join_path(dotkit_path, spec.architecture, + spec.format('$_$@$%@$+$#.dk')) + + + def _write(self, dk_file): + # Category + if self.category: + dk_file.write('#c %s\n' % self.category) + + # Short description + if self.short_description: + dk_file.write('#d %s\n' % self.short_description) + + # Long description + if self.long_description: + for line in textwrap.wrap(self.long_description, 72): + dk_file.write("#h %s\n" % line) + + # Path alterations + for var, dirs in self.paths.items(): + for directory in dirs: + dk_file.write("dk_alter %s %s\n" % (var, directory)) + + # Let CMake find this package. + dk_file.write("dk_alter CMAKE_PREFIX_PATH %s\n" % pkg.prefix) + + +class TclModule(EnvModule): + @property + def file_name(self): + spec = self.pkg.spec + return join_path(tcl_mod_path, spec.architecture, + spec.format('$_$@$%@$+$#')) + + + def _write(self, m_file): + # TODO: cateogry? + m_file.write('#%Module1.0\n') + + # Short description + if self.short_description: + m_file.write('module-whatis \"%s\"\n\n' % self.short_description) + + # Long description + if self.long_description: + m_file.write('proc ModulesHelp { } {\n') + doc = re.sub(r'"', '\"', self.long_description) + m_file.write("puts stderr \"%s\"\n" % doc) + m_file.write('}\n\n') + + # Path alterations + for var, dirs in self.paths.items(): + for directory in dirs: + m_file.write("prepend-path %s \"%s\"\n" % (var, directory)) + + m_file.write("prepend-path CMAKE_PREFIX_PATH \"%s\"\n" % pkg.prefix) diff --git a/share/spack/setup-env.bash b/share/spack/setup-env.sh similarity index 56% rename from share/spack/setup-env.bash rename to share/spack/setup-env.sh index c23a5acab4..7cadc6f202 100755 --- a/share/spack/setup-env.bash +++ b/share/spack/setup-env.sh @@ -23,16 +23,13 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## -# # # This file is part of Spack and sets up the spack environment for -# bash shells. This includes dotkit support as well as putting spack -# in your path. Source it like this: +# bash and zsh. This includes dotkit support, module support, and +# it also puts spack in your path. Source it like this: # -# . /path/to/spack/share/spack/setup-env.bash +# . /path/to/spack/share/spack/setup-env.sh # -# - ######################################################################## # This is a wrapper around the spack command that forwards calls to @@ -59,56 +56,46 @@ # spack dotfiles. ######################################################################## function spack { - _spack_subcommand=$1; shift - _spack_spec="$@" + _sp_subcommand=$1; shift + _sp_spec="$@" # Filter out use and unuse. For any other commands, just run the # command. - case $_spack_subcommand in - "use"|"unuse") + case $_sp_subcommand in + "use"|"unuse"|"load"|"unload") # Shift any other args for use off before parsing spec. - _spack_use_args="" + _sp_module_args="" if [[ "$1" =~ ^- ]]; then - _spack_use_args="$1"; shift - _spack_spec="$@" + _sp_module_args="$1"; shift + _sp_spec="$@" fi - # Here the user has run use or unuse with a spec. Find a matching - # spec with a dotkit using spack dotkit, then use or unuse the - # result. If spack dotkit comes back with an error, do nothing. - if _spack_full_spec=$(command spack dotkit $_spack_spec); then - $_spack_subcommand $_spack_use_args $_spack_full_spec - fi - return - ;; - "load"|"unload") - # Shift any other args for module off before parsing spec. - _spack_module_args="" - if [[ "$1" =~ ^- ]]; then - _spack_module_args="$1"; shift - _spack_spec="$@" - fi + # Translate the parameter into pieces of a command. + # _sp_modtype is an arg to spack module find, and + # _sp_sh_cmd is the equivalent shell command. + case $_sp_subcommand in + "use"|"unuse") + _sp_modtype=dotkit + _sp_sh_cmd=$_sp_subcommand + ;; + "load"|"unload") + _sp_modtype=tcl + _sp_sh_cmd="module $_sp_subcommand" + ;; + esac # Here the user has run use or unuse with a spec. Find a matching - # spec with a dotkit using spack dotkit, then use or unuse the - # result. If spack dotkit comes back with an error, do nothing. - if _spack_full_spec=$(command spack tclmodule $_spack_spec); then - $_spack_subcommand $_spack_module_args $_spack_full_spec + # spec using 'spack module find', then use the appropriate module + # tool's commands to add/remove the result from the environment. + # If spack module command comes back with an error, do nothing. + if _sp_full_spec=$(command spack module find $_sp_modtype $_sp_spec); then + $_sp_sh_cmd $_sp_module_args $_sp_full_spec fi return ;; *) - command spack $_spack_subcommand "$@" - return - ;; + command spack $_sp_subcommand $_sp_spec esac - - # If no args or -h, just run that command as well. - if [ -z "$1" -o "$1" = "-h" ]; then - command spack $_spack_subcommand -h - return - fi - } ######################################################################## @@ -119,31 +106,45 @@ function spack { function _spack_pathadd { # If no variable name is supplied, just append to PATH # otherwise append to that variable. - varname=PATH - path="$1" + _pa_varname=PATH + _pa_new_path="$1" if [ -n "$2" ]; then - varname="$1" - path="$2" + _pa_varname="$1" + _pa_new_path="$2" fi # Do the actual prepending here. - eval "oldvalue=\"\$$varname\"" - if [ -d "$path" ] && [[ ":$oldvalue:" != *":$path:"* ]]; then - if [ -n "$oldvalue" ]; then - eval "export $varname=\"$path:$oldvalue\"" + eval "_pa_oldvalue=\$${_pa_varname}" + + if [ -d "$_pa_new_path" ] && [[ ":$_pa_oldvalue:" != *":$_pa_new_path:"* ]]; then + if [ -n "$_pa_oldvalue" ]; then + eval "export $_pa_varname=\"$_pa_new_path:$_pa_oldvalue\"" else - export $varname="$path" + export $_pa_varname="$_pa_new_path" fi fi } +# +# Figure out where this file is. Below code needs to be portable to +# bash and zsh. +# +_sp_source_file="${BASH_SOURCE[0]}" # Bash's location of last sourced file. +if [ -z "$_sp_source_file" ]; then + _sp_source_file="$0:A" # zsh way to do it + if [[ "$_sp_source_file" == *":A" ]]; then + # Not zsh either... bail out with plain old $0, + # which WILL NOT work if this is sourced indirectly. + _sp_source_file="$0" + fi +fi # -# Set up dotkit and path in the user environment +# Set up modules and dotkit search paths in the user environment # -_spack_share_dir="$(dirname ${BASH_SOURCE[0]})" -_spack_prefix="$(dirname $(dirname $_spack_share_dir))" +_sp_share_dir="$(dirname $_sp_source_file)" +_sp_prefix="$(dirname $(dirname $_sp_share_dir))" -_spack_pathadd DK_NODE "$_spack_share_dir/dotkit" -_spack_pathadd MODULEPATH "$_spack_share_dir/modules" -_spack_pathadd PATH "$_spack_prefix/bin" +_spack_pathadd DK_NODE "$_sp_share_dir/dotkit" +_spack_pathadd MODULEPATH "$_sp_share_dir/modules" +_spack_pathadd PATH "$_sp_prefix/bin" diff --git a/share/spack/setup-env.zsh b/share/spack/setup-env.zsh deleted file mode 100755 index 9aba92818d..0000000000 --- a/share/spack/setup-env.zsh +++ /dev/null @@ -1,122 +0,0 @@ -############################################################################## -# Copyright (c) 2013, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Written by David Beckingsale, david@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://scalability-llnl.github.io/spack -# Please also see the LICENSE file for our notice and the LGPL. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License (as published by -# the Free Software Foundation) version 2.1 dated February 1999. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and -# conditions of the GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -############################################################################## - -# -# -# This file is part of Spack and sets up the spack environment for zsh shells. -# This includes dotkit and module support as well as putting spack -# in your path. Source it like this: -# -# source /path/to/spack/share/spack/setup-env.zsh -# -# - - -######################################################################## -# This is a wrapper around the spack command that forwards calls to -# 'spack use' and 'spack unuse' to shell functions. This in turn -# allows them to be used to invoke dotkit functions. -# -# 'spack use' is smarter than just 'use' because it converts its -# arguments into a unique spack spec that is then passed to dotkit -# commands. This allows the user to use packages without knowing all -# their installation details. -# -# e.g., rather than requring a full spec for libelf, the user can type: -# -# spack use libelf -# -# This will first find the available libelf dotkits and use a -# matching one. If there are two versions of libelf, the user would -# need to be more specific, e.g.: -# -# spack use 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. -######################################################################## -function spack { - _spack_subcommand=${1}; shift - _spack_spec="$@" - - # Filter out use and unuse. For any other commands, just run the - # command. - case ${_spack_subcommand} in - "use"|"unuse") - # Shift any other args for use off before parsing spec. - _spack_use_args="" - if [[ "$1" =~ ^- ]]; then - _spack_use_args="$1"; shift - _spack_spec="$@" - fi - - # Here the user has run use or unuse with a spec. Find a matching - # spec with a dotkit using spack dotkit, then use or unuse the - # result. If spack dotkit comes back with an error, do nothing. - if _spack_full_spec=$(command spack dotkit $_spack_spec); then - $_spack_subcommand $_spack_use_args $_spack_full_spec - fi - return - ;; - "load"|"unload") - # Shift any other args for module off before parsing spec. - _spack_module_args="" - if [[ "$1" =~ ^- ]]; then - _spack_module_args="$1"; shift - _spack_spec="$@" - fi - - # Here the user has run use or unuse with a spec. Find a matching - # spec with a dotkit using spack dotkit, then use or unuse the - # result. If spack dotkit comes back with an error, do nothing. - if _spack_full_spec=$(command spack tclmodule ${_spack_spec}); then - module ${_spack_subcommand} ${_spack_module_args} ${_spack_full_spec} - fi - return - ;; - *) - command spack $_spack_subcommand "$@" - return - ;; - esac - - # If no args or -h, just run that command as well. - if [ -z "$1" -o "$1" = "-h" ]; then - command spack $_spack_subcommand -h - return - fi - -} - -# -# Set up dotkit and path in the user environment -# -_spack_share_dir="$(dirname $0:A)" -_spack_prefix="$(dirname $(dirname ${_spack_share_dir}))" - -export DK_NODE="$_spack_share_dir/dotkit:$DK_NODE" -export MODULEPATH="$_spack_share_dir/modules:$MODULEPATH" -export PATH="$_spack_prefix/bin:$PATH"