From 35cef169740d8eb87a1cc76fcde85d207935d91a Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Fri, 20 Jul 2018 10:30:36 +0200 Subject: Grouped all the module commands under `spack module` As requested in the review all the commands meant to manage module files have been grouped under the `spack module` command. Unit tests have been refactored to match the new command structure. --- lib/spack/docs/developer_guide.rst | 2 +- lib/spack/docs/getting_started.rst | 4 +- lib/spack/docs/module_file_support.rst | 30 +-- lib/spack/docs/tutorial_modules.rst | 103 +++++---- lib/spack/docs/workflows.rst | 20 +- lib/spack/spack/cmd/common/modules.py | 357 -------------------------------- lib/spack/spack/cmd/dotkit.py | 42 ---- lib/spack/spack/cmd/lmod.py | 75 ------- lib/spack/spack/cmd/module.py | 45 ++++ lib/spack/spack/cmd/modules/__init__.py | 346 +++++++++++++++++++++++++++++++ lib/spack/spack/cmd/modules/dotkit.py | 38 ++++ lib/spack/spack/cmd/modules/lmod.py | 69 ++++++ lib/spack/spack/cmd/modules/tcl.py | 38 ++++ lib/spack/spack/cmd/tcl.py | 42 ---- lib/spack/spack/test/cmd/dotkit.py | 101 --------- lib/spack/spack/test/cmd/lmod.py | 103 --------- lib/spack/spack/test/cmd/module.py | 198 ++++++++++++++++++ lib/spack/spack/test/cmd/tcl.py | 135 ------------ share/spack/setup-env.sh | 8 +- 19 files changed, 816 insertions(+), 940 deletions(-) delete mode 100644 lib/spack/spack/cmd/common/modules.py delete mode 100644 lib/spack/spack/cmd/dotkit.py delete mode 100644 lib/spack/spack/cmd/lmod.py create mode 100644 lib/spack/spack/cmd/module.py create mode 100644 lib/spack/spack/cmd/modules/__init__.py create mode 100644 lib/spack/spack/cmd/modules/dotkit.py create mode 100644 lib/spack/spack/cmd/modules/lmod.py create mode 100644 lib/spack/spack/cmd/modules/tcl.py delete mode 100644 lib/spack/spack/cmd/tcl.py delete mode 100644 lib/spack/spack/test/cmd/dotkit.py delete mode 100644 lib/spack/spack/test/cmd/lmod.py create mode 100644 lib/spack/spack/test/cmd/module.py delete mode 100644 lib/spack/spack/test/cmd/tcl.py diff --git a/lib/spack/docs/developer_guide.rst b/lib/spack/docs/developer_guide.rst index c44806bd61..046de145d0 100644 --- a/lib/spack/docs/developer_guide.rst +++ b/lib/spack/docs/developer_guide.rst @@ -314,7 +314,7 @@ See the `Argparse documentation >~/.bashrc + $ spack module tcl loads curl >>~/.bashrc or if environment modules don't work: diff --git a/lib/spack/docs/module_file_support.rst b/lib/spack/docs/module_file_support.rst index 67e041e061..41e1245d9a 100644 --- a/lib/spack/docs/module_file_support.rst +++ b/lib/spack/docs/module_file_support.rst @@ -180,9 +180,9 @@ To identify just the one built with the Intel compiler. .. _cmd-spack-module-loads: -^^^^^^^^^^^^^^^^^^^^^^ -``spack tcl loads`` -^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^ +``spack module tcl loads`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ In some cases, it is desirable to load not just a module, but also all the modules it depends on. This is not required for most modules @@ -195,13 +195,13 @@ Scripts to load modules recursively may be made with the command: .. code-block:: console - $ spack tcl loads --dependencies + $ spack module tcl loads --dependencies An equivalent alternative using `process substitution `_ is: .. code-block :: console - $ source <( spack tcl loads --dependencies ) + $ source <( spack module tcl loads --dependencies ) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -211,12 +211,12 @@ Module Commands for Shell Scripts Although Spack is flexible, the ``module`` command is much faster. This could become an issue when emitting a series of ``spack load`` commands inside a shell script. By adding the ``--shell`` flag, -``spack tcl find`` may also be used to generate code that can be +``spack module tcl find`` may also be used to generate code that can be cut-and-pasted into a shell script. For example: .. code-block:: console - $ spack tcl loads --dependencies py-numpy git + $ spack module tcl loads --dependencies py-numpy git # bzip2@1.0.6%gcc@4.9.3=linux-x86_64 module load bzip2-1.0.6-gcc-4.9.3-ktnrhkrmbbtlvnagfatrarzjojmkvzsx # ncurses@6.0%gcc@4.9.3=linux-x86_64 @@ -256,9 +256,9 @@ Module Prefixes ^^^^^^^^^^^^^^^ On some systems, modules are automatically prefixed with a certain -string; ``spack tcl loads`` needs to know about that prefix when it +string; ``spack module tcl loads`` needs to know about that prefix when it issues ``module load`` commands. Add the ``--prefix`` option to your -``spack tcl loads`` commands if this is necessary. +``spack module tcl loads`` commands if this is necessary. For example, consider the following on one system: @@ -267,11 +267,11 @@ For example, consider the following on one system: $ module avail linux-SuSE11-x86_64/antlr-2.7.7-gcc-5.3.0-bdpl46y - $ spack tcl loads antlr # WRONG! + $ spack module tcl loads antlr # WRONG! # antlr@2.7.7%gcc@5.3.0~csharp+cxx~java~python arch=linux-SuSE11-x86_64 module load antlr-2.7.7-gcc-5.3.0-bdpl46y - $ spack tcl loads --prefix linux-SuSE11-x86_64/ antlr + $ spack module tcl loads --prefix linux-SuSE11-x86_64/ antlr # antlr@2.7.7%gcc@5.3.0~csharp+cxx~java~python arch=linux-SuSE11-x86_64 module load linux-SuSE11-x86_64/antlr-2.7.7-gcc-5.3.0-bdpl46y @@ -626,9 +626,9 @@ Maintaining Module Files Each type of module file has a command with the same name associated with it. The actions these commands permit are usually associated with the maintenance of a production environment. Here's, for instance, -a sample of the features of the ``spack tcl`` command: +a sample of the features of the ``spack module tcl`` command: -.. command-output:: spack tcl --help +.. command-output:: spack module tcl --help .. _cmd-spack-module-refresh: @@ -639,7 +639,7 @@ Refresh the set of modules The subcommand that regenerates module files to update their content or their layout is ``refresh``: -.. command-output:: spack tcl refresh --help +.. command-output:: spack module tcl refresh --help A set of packages can be selected using anonymous specs for the optional ``constraint`` positional argument. Optionally the entire tree can be deleted @@ -654,7 +654,7 @@ Delete module files If instead what you need is just to delete a few module files, then the right subcommand is ``rm``: -.. command-output:: spack tcl rm --help +.. command-output:: spack module tcl rm --help .. note:: We care about your module files! diff --git a/lib/spack/docs/tutorial_modules.rst b/lib/spack/docs/tutorial_modules.rst index ef817a0223..6175479f35 100644 --- a/lib/spack/docs/tutorial_modules.rst +++ b/lib/spack/docs/tutorial_modules.rst @@ -166,7 +166,7 @@ The fastest way to set-up your environment is to :ref:`use a Docker image You are about to regenerate tcl module files for: -- linux-ubuntu16.04-x86_64 / gcc@5.4.0 ------------------------- @@ -444,11 +444,11 @@ Next you should regenerate all the module files: mkaoyhz git@2.9.4 wl6mywv lmod@7.7.29 56lbd3h mpc@1.1.0 nxhwrg7 readline@7.0 -- linux-ubuntu16.04-x86_64 / gcc@7.2.0 ------------------------- - mwamumj bzip2@1.0.6 bcikpen netlib-lapack@3.8.0 pdatzbi openblas@0.3.0 fdiryak py-scipy@1.1.0 ezuwp4p zlib@1.2.11 - h24ofjs cmake@3.11.4 d3lertf netlib-scalapack@2.0.2 2h6xmxh openmpi@1.10.2 jqhycal py-setuptools@39.2.0 + mwamumj bzip2@1.0.6 7apabqu netlib-lapack@3.8.0 pdatzbi openblas@0.3.0 fdiryak py-scipy@1.1.0 ezuwp4p zlib@1.2.11 + 6ovorxs cmake@3.12.0 6i5qsqx netlib-scalapack@2.0.2 6oewzwj openmpi@1.10.2 jqhycal py-setuptools@39.2.0 zk5lhob gdbm@1.14.1 uhzmwog netlib-scalapack@2.0.2 cvldq3v openssl@1.0.2o c7pnzul python@2.7.15 - vt5xcat mpich@3.2.1 lqfhvfh netlib-scalapack@2.0.2 yoxwmgb pkgconf@1.4.2 ccruj2i readline@7.0 - xcgzqdv ncurses@6.1 jae3ilo netlib-scalapack@2.0.2 t3loxvu py-numpy@1.14.3 5ltus3a sqlite@3.23.1 + vt5xcat mpich@3.2.1 3bz5rxx netlib-scalapack@2.0.2 yoxwmgb pkgconf@1.4.2 ccruj2i readline@7.0 + xcgzqdv ncurses@6.1 z52ltyy netlib-scalapack@2.0.2 t3loxvu py-numpy@1.14.3 5ltus3a sqlite@3.23.1 ==> Do you want to proceed? [y/n] y ==> Regenerating tcl module files @@ -503,7 +503,7 @@ and regenerate the module files: .. code-block:: console - root@module-file-tutorial:/# spack tcl refresh --delete-tree + root@module-file-tutorial:/# spack module tcl refresh --delete-tree ==> You are about to regenerate tcl module files for: -- linux-ubuntu16.04-x86_64 / gcc@5.4.0 ------------------------- @@ -514,11 +514,11 @@ and regenerate the module files: mkaoyhz git@2.9.4 wl6mywv lmod@7.7.29 56lbd3h mpc@1.1.0 nxhwrg7 readline@7.0 -- linux-ubuntu16.04-x86_64 / gcc@7.2.0 ------------------------- - mwamumj bzip2@1.0.6 bcikpen netlib-lapack@3.8.0 pdatzbi openblas@0.3.0 fdiryak py-scipy@1.1.0 ezuwp4p zlib@1.2.11 - h24ofjs cmake@3.11.4 d3lertf netlib-scalapack@2.0.2 2h6xmxh openmpi@1.10.2 jqhycal py-setuptools@39.2.0 + mwamumj bzip2@1.0.6 7apabqu netlib-lapack@3.8.0 pdatzbi openblas@0.3.0 fdiryak py-scipy@1.1.0 ezuwp4p zlib@1.2.11 + 6ovorxs cmake@3.12.0 6i5qsqx netlib-scalapack@2.0.2 6oewzwj openmpi@1.10.2 jqhycal py-setuptools@39.2.0 zk5lhob gdbm@1.14.1 uhzmwog netlib-scalapack@2.0.2 cvldq3v openssl@1.0.2o c7pnzul python@2.7.15 - vt5xcat mpich@3.2.1 lqfhvfh netlib-scalapack@2.0.2 yoxwmgb pkgconf@1.4.2 ccruj2i readline@7.0 - xcgzqdv ncurses@6.1 jae3ilo netlib-scalapack@2.0.2 t3loxvu py-numpy@1.14.3 5ltus3a sqlite@3.23.1 + vt5xcat mpich@3.2.1 3bz5rxx netlib-scalapack@2.0.2 yoxwmgb pkgconf@1.4.2 ccruj2i readline@7.0 + xcgzqdv ncurses@6.1 z52ltyy netlib-scalapack@2.0.2 t3loxvu py-numpy@1.14.3 5ltus3a sqlite@3.23.1 ==> Do you want to proceed? [y/n] y ==> Regenerating tcl module files @@ -526,13 +526,13 @@ and regenerate the module files: root@module-file-tutorial:/# module avail -------------------------------------------- /usr/local/share/spack/modules/linux-ubuntu16.04-x86_64 --------------------------------------------- - bzip2-1.0.6-gcc-7.2.0-mwamumj netlib-scalapack-2.0.2-gcc-7.2.0-jae3ilo py-numpy-1.14.3-gcc-7.2.0-t3loxvu - cmake-3.11.4-gcc-7.2.0-h24ofjs netlib-scalapack-2.0.2-gcc-7.2.0-lqfhvfh py-scipy-1.1.0-gcc-7.2.0-fdiryak - gdbm-1.14.1-gcc-7.2.0-zk5lhob netlib-scalapack-2.0.2-gcc-7.2.0-uhzmwog py-setuptools-39.2.0-gcc-7.2.0-jqhycal + bzip2-1.0.6-gcc-7.2.0-mwamumj netlib-scalapack-2.0.2-gcc-7.2.0-6i5qsqx py-numpy-1.14.3-gcc-7.2.0-t3loxvu + cmake-3.12.0-gcc-7.2.0-6ovorxs netlib-scalapack-2.0.2-gcc-7.2.0-uhzmwog py-scipy-1.1.0-gcc-7.2.0-fdiryak + gdbm-1.14.1-gcc-7.2.0-zk5lhob netlib-scalapack-2.0.2-gcc-7.2.0-z52ltyy py-setuptools-39.2.0-gcc-7.2.0-jqhycal mpich-3.2.1-gcc-7.2.0-vt5xcat openblas-0.3.0-gcc-7.2.0-pdatzbi python-2.7.15-gcc-7.2.0-c7pnzul - ncurses-6.1-gcc-7.2.0-xcgzqdv openmpi-1.10.2-gcc-7.2.0-2h6xmxh readline-7.0-gcc-7.2.0-ccruj2i - netlib-lapack-3.8.0-gcc-7.2.0-bcikpen openssl-1.0.2o-gcc-7.2.0-cvldq3v sqlite-3.23.1-gcc-7.2.0-5ltus3a - netlib-scalapack-2.0.2-gcc-7.2.0-d3lertf pkgconf-1.4.2-gcc-7.2.0-yoxwmgb zlib-1.2.11-gcc-7.2.0-ezuwp4p + ncurses-6.1-gcc-7.2.0-xcgzqdv openmpi-1.10.2-gcc-7.2.0-6oewzwj readline-7.0-gcc-7.2.0-ccruj2i + netlib-lapack-3.8.0-gcc-7.2.0-7apabqu openssl-1.0.2o-gcc-7.2.0-cvldq3v sqlite-3.23.1-gcc-7.2.0-5ltus3a + netlib-scalapack-2.0.2-gcc-7.2.0-3bz5rxx pkgconf-1.4.2-gcc-7.2.0-yoxwmgb zlib-1.2.11-gcc-7.2.0-ezuwp4p Use "module spider" to find all possible modules. Use "module keyword key1 key2 ..." to search for all possible modules matching any of the "keys". @@ -562,7 +562,7 @@ exceptions to the blacklist rules you can use ``whitelist``: .. code-block:: console - root@module-file-tutorial:/# spack tcl refresh -y + root@module-file-tutorial:/# spack module tcl refresh -y ==> Regenerating tcl module files @@ -626,7 +626,7 @@ If you try to regenerate the module files now you will get an error: .. code-block:: console - root@module-file-tutorial:/# spack tcl refresh --delete-tree -y + root@module-file-tutorial:/# spack module tcl refresh --delete-tree -y ==> Error: Name clashes detected in module files: file: /usr/local/share/spack/modules/linux-ubuntu16.04-x86_64/netlib-scalapack-2.0.2-gcc-7.2.0 @@ -673,14 +673,14 @@ Regenerating module files now we obtain: .. code-block:: console - root@module-file-tutorial:/# spack tcl refresh --delete-tree -y + root@module-file-tutorial:/# spack module tcl refresh --delete-tree -y ==> Regenerating tcl module files root@module-file-tutorial:/# module av -------------------------------------------- /usr/local/share/spack/modules/linux-ubuntu16.04-x86_64 --------------------------------------------- bzip2-1.0.6-gcc-7.2.0 netlib-lapack-3.8.0-gcc-7.2.0 openmpi-1.10.2-gcc-7.2.0 python-2.7.15-gcc-7.2.0 - cmake-3.11.4-gcc-7.2.0 netlib-scalapack-2.0.2-gcc-7.2.0-netlib-mpich openssl-1.0.2o-gcc-7.2.0 readline-7.0-gcc-7.2.0 + cmake-3.12.0-gcc-7.2.0 netlib-scalapack-2.0.2-gcc-7.2.0-netlib-mpich openssl-1.0.2o-gcc-7.2.0 readline-7.0-gcc-7.2.0 gcc-7.2.0-gcc-5.4.0 netlib-scalapack-2.0.2-gcc-7.2.0-netlib-openmpi pkgconf-1.4.2-gcc-7.2.0 sqlite-3.23.1-gcc-7.2.0 gdbm-1.14.1-gcc-7.2.0 netlib-scalapack-2.0.2-gcc-7.2.0-openblas-mpich py-numpy-1.14.3-gcc-7.2.0-openblas zlib-1.2.11-gcc-7.2.0 mpich-3.2.1-gcc-7.2.0 netlib-scalapack-2.0.2-gcc-7.2.0-openblas-openmpi py-scipy-1.1.0-gcc-7.2.0-openblas @@ -720,13 +720,13 @@ The final result should look like: .. code-block:: console - root@module-file-tutorial:/# spack tcl refresh --delete-tree -y + root@module-file-tutorial:/# spack module tcl refresh --delete-tree -y ==> Regenerating tcl module files root@module-file-tutorial:/# module av -------------------------------------------- /usr/local/share/spack/modules/linux-ubuntu16.04-x86_64 --------------------------------------------- bzip2/1.0.6-gcc-7.2.0 netlib-scalapack/2.0.2-gcc-7.2.0-netlib-openmpi py-scipy/1.1.0-gcc-7.2.0-openblas - cmake/3.11.4-gcc-7.2.0 netlib-scalapack/2.0.2-gcc-7.2.0-openblas-mpich py-setuptools/39.2.0-gcc-7.2.0 + cmake/3.12.0-gcc-7.2.0 netlib-scalapack/2.0.2-gcc-7.2.0-openblas-mpich py-setuptools/39.2.0-gcc-7.2.0 gcc/7.2.0-gcc-5.4.0 netlib-scalapack/2.0.2-gcc-7.2.0-openblas-openmpi (D) python/2.7.15-gcc-7.2.0 gdbm/1.14.1-gcc-7.2.0 openblas/0.3.0-gcc-7.2.0 readline/7.0-gcc-7.2.0 mpich/3.2.1-gcc-7.2.0 openmpi/1.10.2-gcc-7.2.0 sqlite/3.23.1-gcc-7.2.0 @@ -794,7 +794,7 @@ Regenerating the module files results in something like: .. code-block:: console :emphasize-lines: 15 - root@module-file-tutorial:/# spack tcl refresh -y + root@module-file-tutorial:/# spack module tcl refresh -y ==> Regenerating tcl module files root@module-file-tutorial:/# module show gcc @@ -870,10 +870,10 @@ This time we will be more selective and regenerate only the ``gcc`` and .. code-block:: console - root@module-file-tutorial:/# spack tcl refresh -y gcc + root@module-file-tutorial:/# spack module tcl refresh -y gcc ==> Regenerating tcl module files - root@module-file-tutorial:/# spack tcl refresh -y openmpi + root@module-file-tutorial:/# spack module tcl refresh -y openmpi ==> Regenerating tcl module files root@module-file-tutorial:/# module show gcc @@ -981,7 +981,7 @@ and regenerating the module files for every package that depends on ``python``: .. code-block:: console - root@module-file-tutorial:/# spack tcl refresh -y ^python + root@module-file-tutorial:/# spack module tcl refresh -y ^python ==> Regenerating tcl module files Now the ``py-scipy`` module will be: @@ -1165,7 +1165,7 @@ If we now regenerate the module files: .. code-block:: console - root@module-file-tutorial:/# spack lmod refresh --delete-tree -y + root@module-file-tutorial:/# spack module lmod refresh --delete-tree -y ==> Regenerating lmod module files and update ``MODULEPATH`` to point to the ``Core``: @@ -1198,7 +1198,7 @@ the ``Compiler`` part of the hierarchy: ----------------------------------------- /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/gcc/7.2.0 ----------------------------------------- bzip2/1.0.6 mpich/3.2.1 openblas/0.3.0 pkgconf/1.4.2 py-setuptools/39.2.0 sqlite/3.23.1 - cmake/3.11.4 ncurses/6.1 openmpi/1.10.2 py-numpy/1.14.3-openblas python/2.7.15 zlib/1.2.11 + cmake/3.12.0 ncurses/6.1 openmpi/1.10.2 py-numpy/1.14.3-openblas python/2.7.15 zlib/1.2.11 gdbm/1.14.1 netlib-lapack/3.8.0 openssl/1.0.2o py-scipy/1.1.0-openblas readline/7.0 ------------------------------------------- /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/Core -------------------------------------------- @@ -1223,7 +1223,7 @@ either ``mpich`` or ``openmpi``. Let's start by loading ``mpich``: ----------------------------------------- /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/gcc/7.2.0 ----------------------------------------- bzip2/1.0.6 mpich/3.2.1 (L) openblas/0.3.0 pkgconf/1.4.2 py-setuptools/39.2.0 sqlite/3.23.1 - cmake/3.11.4 ncurses/6.1 openmpi/1.10.2 py-numpy/1.14.3-openblas python/2.7.15 zlib/1.2.11 + cmake/3.12.0 ncurses/6.1 openmpi/1.10.2 py-numpy/1.14.3-openblas python/2.7.15 zlib/1.2.11 gdbm/1.14.1 netlib-lapack/3.8.0 openssl/1.0.2o py-scipy/1.1.0-openblas readline/7.0 ------------------------------------------- /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/Core -------------------------------------------- @@ -1357,7 +1357,7 @@ After module files have been regenerated as usual: root@module-file-tutorial:/# module purge - root@module-file-tutorial:/# spack lmod refresh --delete-tree -y + root@module-file-tutorial:/# spack module lmod refresh --delete-tree -y ==> Regenerating lmod module files we can see that now we have additional components in the hierarchy: @@ -1369,11 +1369,11 @@ we can see that now we have additional components in the hierarchy: root@module-file-tutorial:/# module avail ----------------------------- /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/openblas/0.3.0-pdatzbi/gcc/7.2.0 ------------------------------ - py-numpy/1.14.3-openblas py-scipy/1.1.0-openblas + py-numpy/1.14.3 py-scipy/1.1.0 ----------------------------------------- /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/gcc/7.2.0 ----------------------------------------- bzip2/1.0.6 mpich/3.2.1 openblas/0.3.0 (L) pkgconf/1.4.2 readline/7.0 - cmake/3.11.4 ncurses/6.1 openmpi/1.10.2 py-setuptools/39.2.0 sqlite/3.23.1 + cmake/3.12.0 ncurses/6.1 openmpi/1.10.2 py-setuptools/39.2.0 sqlite/3.23.1 gdbm/1.14.1 netlib-lapack/3.8.0 openssl/1.0.2o python/2.7.15 zlib/1.2.11 ------------------------------------------- /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/Core -------------------------------------------- @@ -1389,11 +1389,11 @@ we can see that now we have additional components in the hierarchy: root@module-file-tutorial:/# module load openmpi root@module-file-tutorial:/# module avail - ------------------ /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/openmpi/1.10.2-2h6xmxh/openblas/0.3.0-pdatzbi/gcc/7.2.0 ------------------ - netlib-scalapack/2.0.2-openblas + ------------------ /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/openmpi/1.10.2-6oewzwj/openblas/0.3.0-pdatzbi/gcc/7.2.0 ------------------ + netlib-scalapack/2.0.2 ----------------------------- /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/openblas/0.3.0-pdatzbi/gcc/7.2.0 ------------------------------ - py-numpy/1.14.3-openblas py-scipy/1.1.0-openblas + py-numpy/1.14.3 py-scipy/1.1.0 ----------------------------------------- /usr/local/share/spack/lmod/linux-ubuntu16.04-x86_64/gcc/7.2.0 ----------------------------------------- bzip2/1.0.6 mpich/3.2.1 openblas/0.3.0 (L) pkgconf/1.4.2 readline/7.0 @@ -1420,18 +1420,15 @@ Both ``MPI`` and ``LAPACK`` providers will now benefit from the same safety feat Due to MODULEPATH changes, the following have been reloaded: - 1) netlib-scalapack/2.0.2-openblas - - root@module-file-tutorial:/# module load netlib-lapack + 1) netlib-scalapack/2.0.2 - Lmod is automatically replacing "openblas/0.3.0" with "netlib-lapack/3.8.0". + root@module-file-tutorial:/# module load mpich + Lmod is automatically replacing "openmpi/1.10.2" with "mpich/3.2.1". - Inactive Modules: - 1) py-numpy - The following have been reloaded with a version change: - 1) netlib-scalapack/2.0.2-openblas => netlib-scalapack/2.0.2-netlib + Due to MODULEPATH changes, the following have been reloaded: + 1) netlib-scalapack/2.0.2 Because we only compiled ``py-numpy`` with ``openblas`` the module is made inactive when we switch the ``LAPACK`` provider. The user @@ -1589,7 +1586,7 @@ If we regenerate the module files one last time: .. code-block:: console - root@module-file-tutorial:/# spack lmod refresh -y netlib-scalapack + root@module-file-tutorial:/# spack module lmod refresh -y netlib-scalapack ==> Regenerating lmod module files we'll find the following at the end of each ``netlib-scalapack`` module file: diff --git a/lib/spack/docs/workflows.rst b/lib/spack/docs/workflows.rst index d3a60d9490..b3bdaf3291 100644 --- a/lib/spack/docs/workflows.rst +++ b/lib/spack/docs/workflows.rst @@ -276,11 +276,11 @@ have some drawbacks: 2. The ``spack spec`` and ``spack install`` commands use a sophisticated concretization algorithm that chooses the "best" among several options, taking into account ``packages.yaml`` file. - The ``spack load`` and ``spack tcl loads`` commands, on the + The ``spack load`` and ``spack module tcl loads`` commands, on the other hand, are not very smart: if the user-supplied spec matches - more than one installed package, then ``spack tcl loads`` will + more than one installed package, then ``spack module tcl loads`` will fail. This may change in the future. For now, the workaround is to - be more specific on any ``spack tcl loads`` lines that fail. + be more specific on any ``spack module tcl loads`` lines that fail. """""""""""""""""""""" @@ -290,7 +290,7 @@ Generated Load Scripts Another problem with using `spack load` is, it is slow; a typical user environment could take several seconds to load, and would not be appropriate to put into ``.bashrc`` directly. It is preferable to use -a series of ``spack tcl loads`` commands to pre-compute which +a series of ``spack module tcl loads`` commands to pre-compute which modules to load. These can be put in a script that is run whenever installed Spack packages change. For example: @@ -301,7 +301,7 @@ installed Spack packages change. For example: # Generate module load commands in ~/env/spackenv cat <$HOME/env/spackenv - FIND='spack tcl loads --prefix linux-SuSE11-x86_64/' + FIND='spack module tcl loads --prefix linux-SuSE11-x86_64/' \$FIND modele-utils \$FIND emacs @@ -346,14 +346,14 @@ Users may now put ``source ~/env/spackenv`` into ``.bashrc``. Some module systems put a prefix on the names of modules created by Spack. For example, that prefix is ``linux-SuSE11-x86_64/`` in the above case. If a prefix is not needed, you may omit the - ``--prefix`` flag from ``spack tcl loads``. + ``--prefix`` flag from ``spack module tcl loads``. """"""""""""""""""""""" Transitive Dependencies """"""""""""""""""""""" -In the script above, each ``spack tcl loads`` command generates a +In the script above, each ``spack module tcl loads`` command generates a *single* ``module load`` line. Transitive dependencies do not usually need to be loaded, only modules the user needs in ``$PATH``. This is because Spack builds binaries with RPATH. Spack's RPATH policy has @@ -394,13 +394,13 @@ Unfortunately, Spack's RPATH support does not work in all case. For example: In cases where RPATH support doesn't make things "just work," it can be necessary to load a module's dependencies as well as the module itself. This is done by adding the ``--dependencies`` flag to the -``spack tcl loads`` command. For example, the following line, +``spack module tcl loads`` command. For example, the following line, added to the script above, would be used to load SciPy, along with Numpy, core Python, BLAS/LAPACK and anything else needed: .. code-block:: sh - spack tcl loads --dependencies py-scipy + spack module tcl loads --dependencies py-scipy ^^^^^^^^^^^^^^ Dummy Packages @@ -630,7 +630,7 @@ environments: and extension packages. * Views and activated extensions maintain state that is semantically - equivalent to the information in a ``spack tcl loads`` script. + equivalent to the information in a ``spack module tcl loads`` script. Administrators might find things easier to maintain without the added "heavyweight" state of a view. diff --git a/lib/spack/spack/cmd/common/modules.py b/lib/spack/spack/cmd/common/modules.py deleted file mode 100644 index 11ee9cdb5e..0000000000 --- a/lib/spack/spack/cmd/common/modules.py +++ /dev/null @@ -1,357 +0,0 @@ -############################################################################## -# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as -# published by the Free Software Foundation) version 2.1, 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 Lesser 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 -############################################################################## -"""Contains all the functions that are common to the implementation of -each module file command. -""" - -import collections -import os.path -import shutil - -from llnl.util import filesystem, tty - -import spack.cmd -import spack.modules -import spack.repo - -import spack.cmd.common.arguments as arguments - -description = "manipulate module files" -section = "environment" -level = "short" - - -#: Dictionary that will be populated with the list of sub-commands -#: Each sub-command must be callable and accept 3 arguments: -#: -#: - mtype : the type of the module file -#: - specs : the list of specs to be processed -#: - args : namespace containing the parsed command line arguments -callbacks = {} - - -def setup_parser(subparser): - sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='subparser_name') - - refresh_parser = sp.add_parser('refresh', help='regenerate module files') - refresh_parser.add_argument( - '--delete-tree', - help='delete the module file tree before refresh', - action='store_true' - ) - arguments.add_common_arguments( - refresh_parser, ['constraint', 'yes_to_all'] - ) - - find_parser = sp.add_parser('find', help='find module files for packages') - find_parser.add_argument( - '--full-path', - help='display full path to module file', - action='store_true' - ) - arguments.add_common_arguments( - find_parser, ['constraint', 'recurse_dependencies'] - ) - - rm_parser = sp.add_parser('rm', help='remove module files') - arguments.add_common_arguments( - rm_parser, ['constraint', 'yes_to_all'] - ) - - loads_parser = sp.add_parser( - 'loads', - help='prompt the list of modules associated with a constraint' - ) - loads_parser.add_argument( - '--input-only', action='store_false', dest='shell', - help='generate input for module command (instead of a shell script)' - ) - loads_parser.add_argument( - '-p', '--prefix', dest='prefix', default='', - help='prepend to module names when issuing module load commands' - ) - loads_parser.add_argument( - '-x', '--exclude', dest='exclude', action='append', default=[], - help="exclude package from output; may be specified multiple times" - ) - arguments.add_common_arguments( - loads_parser, ['constraint', 'recurse_dependencies'] - ) - return sp - - -class MultipleSpecsMatch(Exception): - """Raised when multiple specs match a constraint, in a context where - this is not allowed. - """ - - -class NoSpecMatches(Exception): - """Raised when no spec matches a constraint, in a context where - this is not allowed. - """ - - -def one_spec_or_raise(specs): - """Ensures exactly one spec has been selected, or raises the appropriate - exception. - """ - # Ensure a single spec matches the constraint - if len(specs) == 0: - raise NoSpecMatches() - if len(specs) > 1: - raise MultipleSpecsMatch() - - # Get the spec and module type - return specs[0] - - -def loads(module_type, specs, args): - """Prompt the list of modules associated with a list of specs""" - - # Get a comprehensive list of specs - if args.recurse_dependencies: - specs_from_user_constraint = specs[:] - specs = [] - # FIXME : during module file creation nodes seem to be visited - # FIXME : multiple times even if cover='nodes' is given. This - # FIXME : work around permits to get a unique list of spec anyhow. - # FIXME : (same problem as in spack/modules.py) - seen = set() - seen_add = seen.add - for spec in specs_from_user_constraint: - specs.extend( - [item for item in spec.traverse(order='post', cover='nodes') - if not (item in seen or seen_add(item))] - ) - - module_cls = spack.modules.module_types[module_type] - modules = [ - (spec, module_cls(spec).layout.use_name) - for spec in specs if os.path.exists(module_cls(spec).layout.filename) - ] - - module_commands = { - 'tcl': 'module load ', - 'lmod': 'module load ', - 'dotkit': 'use ' - } - - d = { - 'command': '' if not args.shell else module_commands[module_type], - 'prefix': args.prefix - } - - exclude_set = set(args.exclude) - prompt_template = '{comment}{exclude}{command}{prefix}{name}' - for spec, mod in modules: - d['exclude'] = '## ' if spec.name in exclude_set else '' - d['comment'] = '' if not args.shell else '# {0}\n'.format( - spec.format()) - d['name'] = mod - print(prompt_template.format(**d)) - - -def find(module_type, specs, args): - """Returns the module file "use" name if there's a single match. Raises - error messages otherwise. - """ - - spec = one_spec_or_raise(specs) - - # Check if the module file is present - def module_exists(spec): - writer = spack.modules.module_types[module_type](spec) - return os.path.isfile(writer.layout.filename) - - if not module_exists(spec): - msg = 'Even though {1} is installed, ' - msg += 'no {0} module has been generated for it.' - tty.die(msg.format(module_type, spec)) - - # Check if we want to recurse and load all dependencies. In that case - # modify the list of specs adding all the dependencies in post order - if args.recurse_dependencies: - specs = [ - item for item in spec.traverse(order='post', cover='nodes') - if module_exists(item) - ] - - # ... and if it is print its use name or full-path if requested - def module_str(specs): - modules = [] - for x in specs: - writer = spack.modules.module_types[module_type](x) - if args.full_path: - modules.append(writer.layout.filename) - else: - modules.append(writer.layout.use_name) - - return ' '.join(modules) - - print(module_str(specs)) - - -def rm(module_type, specs, args): - """Deletes the module files associated with every spec in specs, for every - module type in module types. - """ - - module_cls = spack.modules.module_types[module_type] - module_exist = lambda x: os.path.exists(module_cls(x).layout.filename) - - specs_with_modules = [spec for spec in specs if module_exist(spec)] - - modules = [module_cls(spec) for spec in specs_with_modules] - - if not modules: - tty.die('No module file matches your query') - - # Ask for confirmation - if not args.yes_to_all: - msg = 'You are about to remove {0} module files for:\n' - tty.msg(msg.format(module_type)) - spack.cmd.display_specs(specs_with_modules, long=True) - print('') - answer = tty.get_yes_or_no('Do you want to proceed?') - if not answer: - tty.die('Will not remove any module files') - - # Remove the module files - for s in modules: - s.remove() - - -def refresh(module_type, specs, args): - """Regenerates the module files for every spec in specs and every module - type in module types. - """ - - # Prompt a message to the user about what is going to change - if not specs: - tty.msg('No package matches your query') - return - - if not args.yes_to_all: - msg = 'You are about to regenerate {types} module files for:\n' - tty.msg(msg.format(types=module_type)) - spack.cmd.display_specs(specs, long=True) - print('') - answer = tty.get_yes_or_no('Do you want to proceed?') - if not answer: - tty.die('Module file regeneration aborted.') - - # Cycle over the module types and regenerate module files - - cls = spack.modules.module_types[module_type] - - # Skip unknown packages. - writers = [ - cls(spec) for spec in specs - if spack.repo.path.exists(spec.name)] - - # Filter blacklisted packages early - writers = [x for x in writers if not x.conf.blacklisted] - - # Detect name clashes in module files - file2writer = collections.defaultdict(list) - for item in writers: - file2writer[item.layout.filename].append(item) - - if len(file2writer) != len(writers): - message = 'Name clashes detected in module files:\n' - for filename, writer_list in file2writer.items(): - if len(writer_list) > 1: - message += '\nfile: {0}\n'.format(filename) - for x in writer_list: - message += 'spec: {0}\n'.format(x.spec.format()) - tty.error(message) - tty.error('Operation aborted') - raise SystemExit(1) - - if len(writers) == 0: - msg = 'Nothing to be done for {0} module files.' - tty.msg(msg.format(module_type)) - return - - # If we arrived here we have at least one writer - module_type_root = writers[0].layout.dirname() - # Proceed regenerating module files - tty.msg('Regenerating {name} module files'.format(name=module_type)) - if os.path.isdir(module_type_root) and args.delete_tree: - shutil.rmtree(module_type_root, ignore_errors=False) - filesystem.mkdirp(module_type_root) - for x in writers: - try: - x.write(overwrite=True) - except Exception as e: - msg = 'Could not write module file [{0}]' - tty.warn(msg.format(x.layout.filename)) - tty.warn('\t--> {0} <--'.format(str(e))) - - -#: Dictionary populated with the list of sub-commands. -#: Each sub-command must be callable and accept 3 arguments: -#: -#: - module_type: the type of module it refers to -#: - specs : the list of specs to be processed -#: - args : namespace containing the parsed command line arguments -callbacks = { - 'refresh': refresh, - 'rm': rm, - 'find': find, - 'loads': loads -} - - -def modules_cmd(parser, args, module_type, callbacks=callbacks): - - # Qualifiers to be used when querying the db for specs - constraint_qualifiers = { - 'refresh': { - 'installed': True, - 'known': True - }, - } - query_args = constraint_qualifiers.get(args.subparser_name, {}) - - # Get the specs that match the query from the DB - specs = args.specs(**query_args) - - try: - - callbacks[args.subparser_name](module_type, specs, args) - - except MultipleSpecsMatch: - msg = "the constraint '{query}' matches multiple packages:\n" - for s in specs: - msg += '\t' + s.cformat() + '\n' - tty.error(msg.format(query=args.constraint)) - tty.die('In this context exactly **one** match is needed: please specify your constraints better.') # NOQA: ignore=E501 - - except NoSpecMatches: - msg = "the constraint '{query}' matches no package." - tty.error(msg.format(query=args.constraint)) - tty.die('In this context exactly **one** match is needed: please specify your constraints better.') # NOQA: ignore=E501 diff --git a/lib/spack/spack/cmd/dotkit.py b/lib/spack/spack/cmd/dotkit.py deleted file mode 100644 index 619389eb04..0000000000 --- a/lib/spack/spack/cmd/dotkit.py +++ /dev/null @@ -1,42 +0,0 @@ -############################################################################## -# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as -# published by the Free Software Foundation) version 2.1, 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 Lesser 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 spack.cmd.common.modules - -description = "manipulate dotkit module files" -section = "environment" -level = "short" - -#: Type of the modules managed by this command -_module_type = 'dotkit' - - -def setup_parser(subparser): - spack.cmd.common.modules.setup_parser(subparser) - - -def dotkit(parser, args): - spack.cmd.common.modules.modules_cmd( - parser, args, module_type=_module_type - ) diff --git a/lib/spack/spack/cmd/lmod.py b/lib/spack/spack/cmd/lmod.py deleted file mode 100644 index ea3cbd7a61..0000000000 --- a/lib/spack/spack/cmd/lmod.py +++ /dev/null @@ -1,75 +0,0 @@ -############################################################################## -# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as -# published by the Free Software Foundation) version 2.1, 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 Lesser 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 os - -import llnl.util.filesystem -import spack.cmd.common.arguments -import spack.cmd.common.modules - -description = "manipulate hierarchical module files" -section = "environment" -level = "short" - -#: Type of the modules managed by this command -_module_type = 'lmod' - - -def setup_parser(subparser): - sp = spack.cmd.common.modules.setup_parser(subparser) - - # Set default module file for a package - setdefault = sp.add_parser( - 'setdefault', help='set the default module file for a package' - ) - spack.cmd.common.arguments.add_common_arguments( - setdefault, ['constraint'] - ) - - -def setdefault(module_type, specs, args): - """Set the default module file, when multiple are present""" - # For details on the underlying mechanism see: - # - # https://lmod.readthedocs.io/en/latest/060_locating.html#marking-a-version-as-default - # - spack.cmd.common.modules.one_spec_or_raise(specs) - writer = spack.modules.module_types[_module_type](specs[0]) - - module_folder = os.path.dirname(writer.layout.filename) - module_basename = os.path.basename(writer.layout.filename) - with llnl.util.filesystem.working_dir(module_folder): - if os.path.exists('default') and os.path.islink('default'): - os.remove('default') - os.symlink(module_basename, 'default') - - -callbacks = dict(spack.cmd.common.modules.callbacks.items()) -callbacks['setdefault'] = setdefault - - -def lmod(parser, args): - spack.cmd.common.modules.modules_cmd( - parser, args, module_type=_module_type, callbacks=callbacks - ) diff --git a/lib/spack/spack/cmd/module.py b/lib/spack/spack/cmd/module.py new file mode 100644 index 0000000000..22826afdf3 --- /dev/null +++ b/lib/spack/spack/cmd/module.py @@ -0,0 +1,45 @@ +############################################################################## +# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/spack/spack +# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, 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 Lesser 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 spack.cmd.modules.dotkit +import spack.cmd.modules.lmod +import spack.cmd.modules.tcl + +description = "manipulate module files" +section = "environment" +level = "short" + + +_subcommands = {} + + +def setup_parser(subparser): + sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='module_type') + spack.cmd.modules.dotkit.add_command(sp, _subcommands) + spack.cmd.modules.lmod.add_command(sp, _subcommands) + spack.cmd.modules.tcl.add_command(sp, _subcommands) + + +def module(parser, args): + _subcommands[args.module_type](parser, args) diff --git a/lib/spack/spack/cmd/modules/__init__.py b/lib/spack/spack/cmd/modules/__init__.py new file mode 100644 index 0000000000..784c49daa2 --- /dev/null +++ b/lib/spack/spack/cmd/modules/__init__.py @@ -0,0 +1,346 @@ +############################################################################## +# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/spack/spack +# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, 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 Lesser 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 +############################################################################## +"""Implementation details of the ``spack module`` command.""" + +import collections +import os.path +import shutil + +from llnl.util import filesystem, tty + +import spack.cmd +import spack.modules +import spack.repo + +import spack.cmd.common.arguments as arguments + +description = "manipulate module files" +section = "environment" +level = "short" + + +def setup_parser(subparser): + sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='subparser_name') + + refresh_parser = sp.add_parser('refresh', help='regenerate module files') + refresh_parser.add_argument( + '--delete-tree', + help='delete the module file tree before refresh', + action='store_true' + ) + arguments.add_common_arguments( + refresh_parser, ['constraint', 'yes_to_all'] + ) + + find_parser = sp.add_parser('find', help='find module files for packages') + find_parser.add_argument( + '--full-path', + help='display full path to module file', + action='store_true' + ) + arguments.add_common_arguments( + find_parser, ['constraint', 'recurse_dependencies'] + ) + + rm_parser = sp.add_parser('rm', help='remove module files') + arguments.add_common_arguments( + rm_parser, ['constraint', 'yes_to_all'] + ) + + loads_parser = sp.add_parser( + 'loads', + help='prompt the list of modules associated with a constraint' + ) + loads_parser.add_argument( + '--input-only', action='store_false', dest='shell', + help='generate input for module command (instead of a shell script)' + ) + loads_parser.add_argument( + '-p', '--prefix', dest='prefix', default='', + help='prepend to module names when issuing module load commands' + ) + loads_parser.add_argument( + '-x', '--exclude', dest='exclude', action='append', default=[], + help="exclude package from output; may be specified multiple times" + ) + arguments.add_common_arguments( + loads_parser, ['constraint', 'recurse_dependencies'] + ) + return sp + + +class MultipleSpecsMatch(Exception): + """Raised when multiple specs match a constraint, in a context where + this is not allowed. + """ + + +class NoSpecMatches(Exception): + """Raised when no spec matches a constraint, in a context where + this is not allowed. + """ + + +def one_spec_or_raise(specs): + """Ensures exactly one spec has been selected, or raises the appropriate + exception. + """ + # Ensure a single spec matches the constraint + if len(specs) == 0: + raise NoSpecMatches() + if len(specs) > 1: + raise MultipleSpecsMatch() + + # Get the spec and module type + return specs[0] + + +def loads(module_type, specs, args): + """Prompt the list of modules associated with a list of specs""" + + # Get a comprehensive list of specs + if args.recurse_dependencies: + specs_from_user_constraint = specs[:] + specs = [] + # FIXME : during module file creation nodes seem to be visited + # FIXME : multiple times even if cover='nodes' is given. This + # FIXME : work around permits to get a unique list of spec anyhow. + # FIXME : (same problem as in spack/modules.py) + seen = set() + seen_add = seen.add + for spec in specs_from_user_constraint: + specs.extend( + [item for item in spec.traverse(order='post', cover='nodes') + if not (item in seen or seen_add(item))] + ) + + module_cls = spack.modules.module_types[module_type] + modules = [ + (spec, module_cls(spec).layout.use_name) + for spec in specs if os.path.exists(module_cls(spec).layout.filename) + ] + + module_commands = { + 'tcl': 'module load ', + 'lmod': 'module load ', + 'dotkit': 'use ' + } + + d = { + 'command': '' if not args.shell else module_commands[module_type], + 'prefix': args.prefix + } + + exclude_set = set(args.exclude) + prompt_template = '{comment}{exclude}{command}{prefix}{name}' + for spec, mod in modules: + d['exclude'] = '## ' if spec.name in exclude_set else '' + d['comment'] = '' if not args.shell else '# {0}\n'.format( + spec.format()) + d['name'] = mod + print(prompt_template.format(**d)) + + +def find(module_type, specs, args): + """Returns the module file "use" name if there's a single match. Raises + error messages otherwise. + """ + + spec = one_spec_or_raise(specs) + + # Check if the module file is present + def module_exists(spec): + writer = spack.modules.module_types[module_type](spec) + return os.path.isfile(writer.layout.filename) + + if not module_exists(spec): + msg = 'Even though {1} is installed, ' + msg += 'no {0} module has been generated for it.' + tty.die(msg.format(module_type, spec)) + + # Check if we want to recurse and load all dependencies. In that case + # modify the list of specs adding all the dependencies in post order + if args.recurse_dependencies: + specs = [ + item for item in spec.traverse(order='post', cover='nodes') + if module_exists(item) + ] + + # ... and if it is print its use name or full-path if requested + def module_str(specs): + modules = [] + for x in specs: + writer = spack.modules.module_types[module_type](x) + if args.full_path: + modules.append(writer.layout.filename) + else: + modules.append(writer.layout.use_name) + + return ' '.join(modules) + + print(module_str(specs)) + + +def rm(module_type, specs, args): + """Deletes the module files associated with every spec in specs, for every + module type in module types. + """ + + module_cls = spack.modules.module_types[module_type] + module_exist = lambda x: os.path.exists(module_cls(x).layout.filename) + + specs_with_modules = [spec for spec in specs if module_exist(spec)] + + modules = [module_cls(spec) for spec in specs_with_modules] + + if not modules: + tty.die('No module file matches your query') + + # Ask for confirmation + if not args.yes_to_all: + msg = 'You are about to remove {0} module files for:\n' + tty.msg(msg.format(module_type)) + spack.cmd.display_specs(specs_with_modules, long=True) + print('') + answer = tty.get_yes_or_no('Do you want to proceed?') + if not answer: + tty.die('Will not remove any module files') + + # Remove the module files + for s in modules: + s.remove() + + +def refresh(module_type, specs, args): + """Regenerates the module files for every spec in specs and every module + type in module types. + """ + + # Prompt a message to the user about what is going to change + if not specs: + tty.msg('No package matches your query') + return + + if not args.yes_to_all: + msg = 'You are about to regenerate {types} module files for:\n' + tty.msg(msg.format(types=module_type)) + spack.cmd.display_specs(specs, long=True) + print('') + answer = tty.get_yes_or_no('Do you want to proceed?') + if not answer: + tty.die('Module file regeneration aborted.') + + # Cycle over the module types and regenerate module files + + cls = spack.modules.module_types[module_type] + + # Skip unknown packages. + writers = [ + cls(spec) for spec in specs + if spack.repo.path.exists(spec.name)] + + # Filter blacklisted packages early + writers = [x for x in writers if not x.conf.blacklisted] + + # Detect name clashes in module files + file2writer = collections.defaultdict(list) + for item in writers: + file2writer[item.layout.filename].append(item) + + if len(file2writer) != len(writers): + message = 'Name clashes detected in module files:\n' + for filename, writer_list in file2writer.items(): + if len(writer_list) > 1: + message += '\nfile: {0}\n'.format(filename) + for x in writer_list: + message += 'spec: {0}\n'.format(x.spec.format()) + tty.error(message) + tty.error('Operation aborted') + raise SystemExit(1) + + if len(writers) == 0: + msg = 'Nothing to be done for {0} module files.' + tty.msg(msg.format(module_type)) + return + + # If we arrived here we have at least one writer + module_type_root = writers[0].layout.dirname() + # Proceed regenerating module files + tty.msg('Regenerating {name} module files'.format(name=module_type)) + if os.path.isdir(module_type_root) and args.delete_tree: + shutil.rmtree(module_type_root, ignore_errors=False) + filesystem.mkdirp(module_type_root) + for x in writers: + try: + x.write(overwrite=True) + except Exception as e: + msg = 'Could not write module file [{0}]' + tty.warn(msg.format(x.layout.filename)) + tty.warn('\t--> {0} <--'.format(str(e))) + + +#: Dictionary populated with the list of sub-commands. +#: Each sub-command must be callable and accept 3 arguments: +#: +#: - module_type: the type of module it refers to +#: - specs : the list of specs to be processed +#: - args : namespace containing the parsed command line arguments +callbacks = { + 'refresh': refresh, + 'rm': rm, + 'find': find, + 'loads': loads +} + + +def modules_cmd(parser, args, module_type, callbacks=callbacks): + + # Qualifiers to be used when querying the db for specs + constraint_qualifiers = { + 'refresh': { + 'installed': True, + 'known': True + }, + } + query_args = constraint_qualifiers.get(args.subparser_name, {}) + + # Get the specs that match the query from the DB + specs = args.specs(**query_args) + + try: + + callbacks[args.subparser_name](module_type, specs, args) + + except MultipleSpecsMatch: + msg = "the constraint '{query}' matches multiple packages:\n" + for s in specs: + msg += '\t' + s.cformat() + '\n' + tty.error(msg.format(query=args.constraint)) + tty.die('In this context exactly **one** match is needed: please specify your constraints better.') # NOQA: ignore=E501 + + except NoSpecMatches: + msg = "the constraint '{query}' matches no package." + tty.error(msg.format(query=args.constraint)) + tty.die('In this context exactly **one** match is needed: please specify your constraints better.') # NOQA: ignore=E501 diff --git a/lib/spack/spack/cmd/modules/dotkit.py b/lib/spack/spack/cmd/modules/dotkit.py new file mode 100644 index 0000000000..9c50dfa777 --- /dev/null +++ b/lib/spack/spack/cmd/modules/dotkit.py @@ -0,0 +1,38 @@ +############################################################################## +# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/spack/spack +# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, 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 Lesser 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 functools + +import spack.cmd.modules + + +def add_command(parser, command_dict): + dotkit_parser = parser.add_parser( + 'dotkit', help='manipulate dotkit module files' + ) + spack.cmd.modules.setup_parser(dotkit_parser) + + command_dict['dotkit'] = functools.partial( + spack.cmd.modules.modules_cmd, module_type='dotkit' + ) diff --git a/lib/spack/spack/cmd/modules/lmod.py b/lib/spack/spack/cmd/modules/lmod.py new file mode 100644 index 0000000000..b40024b114 --- /dev/null +++ b/lib/spack/spack/cmd/modules/lmod.py @@ -0,0 +1,69 @@ +############################################################################## +# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/spack/spack +# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, 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 Lesser 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 functools +import os + +import llnl.util.filesystem +import spack.cmd.common.arguments +import spack.cmd.modules + + +def add_command(parser, command_dict): + lmod_parser = parser.add_parser( + 'lmod', help='manipulate hierarchical module files' + ) + sp = spack.cmd.modules.setup_parser(lmod_parser) + + # Set default module file for a package + setdefault_parser = sp.add_parser( + 'setdefault', help='set the default module file for a package' + ) + spack.cmd.common.arguments.add_common_arguments( + setdefault_parser, ['constraint'] + ) + + callbacks = dict(spack.cmd.modules.callbacks.items()) + callbacks['setdefault'] = setdefault + + command_dict['lmod'] = functools.partial( + spack.cmd.modules.modules_cmd, module_type='lmod', callbacks=callbacks + ) + + +def setdefault(module_type, specs, args): + """Set the default module file, when multiple are present""" + # For details on the underlying mechanism see: + # + # https://lmod.readthedocs.io/en/latest/060_locating.html#marking-a-version-as-default + # + spack.cmd.modules.one_spec_or_raise(specs) + writer = spack.modules.module_types['lmod'](specs[0]) + + module_folder = os.path.dirname(writer.layout.filename) + module_basename = os.path.basename(writer.layout.filename) + with llnl.util.filesystem.working_dir(module_folder): + if os.path.exists('default') and os.path.islink('default'): + os.remove('default') + os.symlink(module_basename, 'default') diff --git a/lib/spack/spack/cmd/modules/tcl.py b/lib/spack/spack/cmd/modules/tcl.py new file mode 100644 index 0000000000..f0cf2ed13c --- /dev/null +++ b/lib/spack/spack/cmd/modules/tcl.py @@ -0,0 +1,38 @@ +############################################################################## +# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/spack/spack +# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, 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 Lesser 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 functools + +import spack.cmd.modules + + +def add_command(parser, command_dict): + tcl_parser = parser.add_parser( + 'tcl', help='manipulate non-hierarchical module files' + ) + spack.cmd.modules.setup_parser(tcl_parser) + + command_dict['tcl'] = functools.partial( + spack.cmd.modules.modules_cmd, module_type='tcl' + ) diff --git a/lib/spack/spack/cmd/tcl.py b/lib/spack/spack/cmd/tcl.py deleted file mode 100644 index 56e40e9df9..0000000000 --- a/lib/spack/spack/cmd/tcl.py +++ /dev/null @@ -1,42 +0,0 @@ -############################################################################## -# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as -# published by the Free Software Foundation) version 2.1, 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 Lesser 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 spack.cmd.common.modules - -description = "manipulate non-hierarchical module files" -section = "environment" -level = "short" - -#: Type of the modules managed by this command -_module_type = 'tcl' - - -def setup_parser(subparser): - spack.cmd.common.modules.setup_parser(subparser) - - -def tcl(parser, args): - spack.cmd.common.modules.modules_cmd( - parser, args, module_type=_module_type - ) diff --git a/lib/spack/spack/test/cmd/dotkit.py b/lib/spack/spack/test/cmd/dotkit.py deleted file mode 100644 index a9a3ac5507..0000000000 --- a/lib/spack/spack/test/cmd/dotkit.py +++ /dev/null @@ -1,101 +0,0 @@ -############################################################################## -# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as -# published by the Free Software Foundation) version 2.1, 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 Lesser 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 argparse -import os.path - -import pytest - -import spack.cmd.dotkit -import spack.main -import spack.modules - -dotkit = spack.main.SpackCommand('dotkit') - - -def _get_module_files(args): - specs = args.specs() - writer_cls = spack.modules.module_types['dotkit'] - return [writer_cls(spec).layout.filename for spec in specs] - - -@pytest.fixture( - params=[ - ['rm', 'doesnotexist'], # Try to remove a non existing module - ['find', 'mpileaks'], # Try to find a module with multiple matches - ['find', 'doesnotexist'], # Try to find a module with no matches - ] -) -def failure_args(request): - """A list of arguments that will cause a failure""" - return request.param - - -@pytest.fixture(scope='module') -def parser(): - """Returns the parser for the module command""" - parser = argparse.ArgumentParser() - spack.cmd.dotkit.setup_parser(parser) - return parser - -# TODO : test the --delete-tree option -# TODO : this requires having a separate directory for test modules -# TODO : add tests for loads and find to check the prompt format - - -@pytest.mark.db -@pytest.mark.usefixtures('database') -def test_exit_with_failure(database, failure_args): - with pytest.raises(spack.main.SpackCommandError): - dotkit(*failure_args) - - -@pytest.mark.db -@pytest.mark.usefixtures('database') -@pytest.mark.parametrize('cli_args', [ - ['libelf'], - ['--full-path', 'libelf'] -]) -def test_find(cli_args): - """Tests 'spack dotkit find' under a few common scenarios.""" - dotkit(*(['find'] + cli_args)) - - -@pytest.mark.db -@pytest.mark.usefixtures('database') -def test_remove_and_add_dotkit(parser): - """Tests adding and removing a dotkit module file.""" - - rm_cli_args = ['rm', '-y', 'mpileaks'] - module_files = _get_module_files(parser.parse_args(rm_cli_args)) - for item in module_files: - assert os.path.exists(item) - - dotkit(*rm_cli_args) - for item in module_files: - assert not os.path.exists(item) - - dotkit('refresh', '-y', 'mpileaks') - for item in module_files: - assert os.path.exists(item) diff --git a/lib/spack/spack/test/cmd/lmod.py b/lib/spack/spack/test/cmd/lmod.py deleted file mode 100644 index f8f9c6b6f0..0000000000 --- a/lib/spack/spack/test/cmd/lmod.py +++ /dev/null @@ -1,103 +0,0 @@ -############################################################################## -# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as -# published by the Free Software Foundation) version 2.1, 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 Lesser 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 os.path - -import pytest - -import spack.main -import spack.modules -import spack.spec - -lmod = spack.main.SpackCommand('lmod') - -# Needed to make the fixture work -writer_cls = spack.modules.lmod.LmodModulefileWriter - -# TODO : add tests for loads and find to check the prompt format - - -@pytest.fixture( - params=[ - ['rm', 'doesnotexist'], # Try to remove a non existing module - ['find', 'mpileaks'], # Try to find a module with multiple matches - ['find', 'doesnotexist'], # Try to find a module with no matches - ] -) -def failure_args(request): - """A list of arguments that will cause a failure""" - return request.param - - -def test_exit_with_failure(database, failure_args): - with pytest.raises(spack.main.SpackCommandError): - lmod(*failure_args) - - -def test_setdefault_command( - mutable_database, module_configuration -): - - module_configuration('autoload_direct') - - # Install two different versions of a package - other_spec, preferred = 'a@1.0', 'a@2.0' - - spack.spec.Spec(other_spec).concretized().package.do_install(fake=True) - spack.spec.Spec(preferred).concretized().package.do_install(fake=True) - - writers = { - preferred: writer_cls(spack.spec.Spec(preferred).concretized()), - other_spec: writer_cls(spack.spec.Spec(other_spec).concretized()) - } - - # Create two module files for the same software - lmod('refresh', '-y', '--delete-tree', preferred, other_spec) - - # Assert initial directory state: no link and all module files present - link_name = os.path.join( - os.path.dirname(writers[preferred].layout.filename), - 'default' - ) - for k in preferred, other_spec: - assert os.path.exists(writers[k].layout.filename) - assert not os.path.exists(link_name) - - # Set the default to be the other spec - lmod('setdefault', other_spec) - - # Check that a link named 'default' exists, and points to the right file - for k in preferred, other_spec: - assert os.path.exists(writers[k].layout.filename) - assert os.path.exists(link_name) and os.path.islink(link_name) - assert os.path.realpath(link_name) == writers[other_spec].layout.filename - - # Reset the default to be the preferred spec - lmod('setdefault', preferred) - - # Check that a link named 'default' exists, and points to the right file - for k in preferred, other_spec: - assert os.path.exists(writers[k].layout.filename) - assert os.path.exists(link_name) and os.path.islink(link_name) - assert os.path.realpath(link_name) == writers[preferred].layout.filename diff --git a/lib/spack/spack/test/cmd/module.py b/lib/spack/spack/test/cmd/module.py new file mode 100644 index 0000000000..ca60814147 --- /dev/null +++ b/lib/spack/spack/test/cmd/module.py @@ -0,0 +1,198 @@ +############################################################################## +# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/spack/spack +# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, 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 Lesser 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 os.path + +import pytest + +import spack.main +import spack.modules + +module = spack.main.SpackCommand('module') + + +def _module_files(module_type, *specs): + specs = [spack.spec.Spec(x).concretized() for x in specs] + writer_cls = spack.modules.module_types[module_type] + return [writer_cls(spec).layout.filename for spec in specs] + + +@pytest.fixture( + params=[ + ['rm', 'doesnotexist'], # Try to remove a non existing module + ['find', 'mpileaks'], # Try to find a module with multiple matches + ['find', 'doesnotexist'], # Try to find a module with no matches + ] +) +def failure_args(request): + """A list of arguments that will cause a failure""" + return request.param + + +@pytest.fixture( + params=['dotkit', 'tcl', 'lmod'] +) +def module_type(request): + return request.param + + +# TODO : test the --delete-tree option +# TODO : this requires having a separate directory for test modules +# TODO : add tests for loads and find to check the prompt format + +@pytest.mark.db +def test_exit_with_failure(database, module_type, failure_args): + with pytest.raises(spack.main.SpackCommandError): + module(module_type, *failure_args) + + +@pytest.mark.db +def test_remove_and_add(database, module_type): + """Tests adding and removing a tcl module file.""" + + if module_type == 'lmod': + # TODO: Testing this with lmod requires mocking + # TODO: the core compilers + return + + rm_cli_args = ['rm', '-y', 'mpileaks'] + module_files = _module_files(module_type, 'mpileaks') + for item in module_files: + assert os.path.exists(item) + + module(module_type, *rm_cli_args) + for item in module_files: + assert not os.path.exists(item) + + module(module_type, 'refresh', '-y', 'mpileaks') + for item in module_files: + assert os.path.exists(item) + + +@pytest.mark.db +@pytest.mark.parametrize('cli_args', [ + ['libelf'], + ['--full-path', 'libelf'] +]) +def test_find(database, cli_args, module_type): + if module_type == 'lmod': + # TODO: Testing this with lmod requires mocking + # TODO: the core compilers + return + + module(module_type, *(['find'] + cli_args)) + + +@pytest.mark.db +@pytest.mark.usefixtures('database') +@pytest.mark.regression('2215') +def test_find_fails_on_multiple_matches(): + # As we installed multiple versions of mpileaks, the command will + # fail because of multiple matches + out = module('tcl', 'find', 'mpileaks', fail_on_error=False) + assert module.returncode == 1 + assert 'matches multiple packages' in out + + # Passing multiple packages from the command line also results in the + # same failure + out = module( + 'tcl', 'find', 'mpileaks ^mpich', 'libelf', fail_on_error=False + ) + assert module.returncode == 1 + assert 'matches multiple packages' in out + + +@pytest.mark.db +@pytest.mark.usefixtures('database') +@pytest.mark.regression('2570') +def test_find_fails_on_non_existing_packages(): + # Another way the command might fail is if the package does not exist + out = module('tcl', 'find', 'doesnotexist', fail_on_error=False) + assert module.returncode == 1 + assert 'matches no package' in out + + +@pytest.mark.db +@pytest.mark.usefixtures('database') +def test_find_recursive(): + # If we call find without options it should return only one module + out = module('tcl', 'find', 'mpileaks ^zmpi') + assert len(out.split()) == 1 + + # If instead we call it with the recursive option the length should + # be greater + out = module('tcl', 'find', '-r', 'mpileaks ^zmpi') + assert len(out.split()) > 1 + + +# Needed to make the 'module_configuration' fixture below work +writer_cls = spack.modules.lmod.LmodModulefileWriter + + +@pytest.mark.db +def test_setdefault_command( + mutable_database, module_configuration +): + module_configuration('autoload_direct') + + # Install two different versions of a package + other_spec, preferred = 'a@1.0', 'a@2.0' + + spack.spec.Spec(other_spec).concretized().package.do_install(fake=True) + spack.spec.Spec(preferred).concretized().package.do_install(fake=True) + + writers = { + preferred: writer_cls(spack.spec.Spec(preferred).concretized()), + other_spec: writer_cls(spack.spec.Spec(other_spec).concretized()) + } + + # Create two module files for the same software + module('lmod', 'refresh', '-y', '--delete-tree', preferred, other_spec) + + # Assert initial directory state: no link and all module files present + link_name = os.path.join( + os.path.dirname(writers[preferred].layout.filename), + 'default' + ) + for k in preferred, other_spec: + assert os.path.exists(writers[k].layout.filename) + assert not os.path.exists(link_name) + + # Set the default to be the other spec + module('lmod', 'setdefault', other_spec) + + # Check that a link named 'default' exists, and points to the right file + for k in preferred, other_spec: + assert os.path.exists(writers[k].layout.filename) + assert os.path.exists(link_name) and os.path.islink(link_name) + assert os.path.realpath(link_name) == writers[other_spec].layout.filename + + # Reset the default to be the preferred spec + module('lmod', 'setdefault', preferred) + + # Check that a link named 'default' exists, and points to the right file + for k in preferred, other_spec: + assert os.path.exists(writers[k].layout.filename) + assert os.path.exists(link_name) and os.path.islink(link_name) + assert os.path.realpath(link_name) == writers[preferred].layout.filename diff --git a/lib/spack/spack/test/cmd/tcl.py b/lib/spack/spack/test/cmd/tcl.py deleted file mode 100644 index d5deffce6b..0000000000 --- a/lib/spack/spack/test/cmd/tcl.py +++ /dev/null @@ -1,135 +0,0 @@ -############################################################################## -# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files 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 Lesser General Public License (as -# published by the Free Software Foundation) version 2.1, 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 Lesser 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 argparse -import os.path - -import pytest - -import spack.cmd.tcl -import spack.main -import spack.modules - -tcl = spack.main.SpackCommand('tcl') - - -def _get_module_files(args): - specs = args.specs() - writer_cls = spack.modules.module_types['tcl'] - return [writer_cls(spec).layout.filename for spec in specs] - - -@pytest.fixture( - params=[ - ['rm', 'doesnotexist'], # Try to remove a non existing module - ['find', 'mpileaks'], # Try to find a module with multiple matches - ['find', 'doesnotexist'], # Try to find a module with no matches - ] -) -def failure_args(request): - """A list of arguments that will cause a failure""" - return request.param - - -@pytest.fixture(scope='module') -def parser(): - """Returns the parser for the module command""" - parser = argparse.ArgumentParser() - spack.cmd.tcl.setup_parser(parser) - return parser - -# TODO : test the --delete-tree option -# TODO : this requires having a separate directory for test modules -# TODO : add tests for loads and find to check the prompt format - - -def test_exit_with_failure(database, failure_args): - with pytest.raises(spack.main.SpackCommandError): - tcl(*failure_args) - - -def test_remove_and_add_tcl(database, parser): - """Tests adding and removing a tcl module file.""" - - rm_cli_args = ['rm', '-y', 'mpileaks'] - module_files = _get_module_files(parser.parse_args(rm_cli_args)) - for item in module_files: - assert os.path.exists(item) - - tcl(*rm_cli_args) - for item in module_files: - assert not os.path.exists(item) - - tcl('refresh', '-y', 'mpileaks') - for item in module_files: - assert os.path.exists(item) - - -@pytest.mark.parametrize('cli_args', [ - ['libelf'], - ['--full-path', 'libelf'] -]) -def test_find(database, cli_args): - """Tests 'spack tcl find' under a few common scenarios.""" - tcl(*(['find'] + cli_args)) - - -@pytest.mark.db -@pytest.mark.usefixtures('database') -@pytest.mark.regression('2215') -def test_find_fails_on_multiple_matches(): - # As we installed multiple versions of mpileaks, the command will - # fail because of multiple matches - out = tcl('find', 'mpileaks', fail_on_error=False) - assert tcl.returncode == 1 - assert 'matches multiple packages' in out - - # Passing multiple packages from the command line also results in the - # same failure - out = tcl('find', 'mpileaks ^mpich', 'libelf', fail_on_error=False) - assert tcl.returncode == 1 - assert 'matches multiple packages' in out - - -@pytest.mark.db -@pytest.mark.usefixtures('database') -@pytest.mark.regression('2570') -def test_find_fails_on_non_existing_packages(): - # Another way the command might fail is if the package does not exist - out = tcl('find', 'doesnotexist', fail_on_error=False) - assert tcl.returncode == 1 - assert 'matches no package' in out - - -@pytest.mark.db -@pytest.mark.usefixtures('database') -def test_find_recursive(): - # If we call find without options it should return only one module - out = tcl('find', 'mpileaks ^zmpi') - assert len(out.split()) == 1 - - # If instead we call it with the recursive option the length should - # be greater - out = tcl('find', '-r', 'mpileaks ^zmpi') - assert len(out.split()) > 1 diff --git a/share/spack/setup-env.sh b/share/spack/setup-env.sh index 6e5ab2b897..a0321c9223 100755 --- a/share/spack/setup-env.sh +++ b/share/spack/setup-env.sh @@ -121,25 +121,25 @@ function spack { # If spack module command comes back with an error, do nothing. case $_sp_subcommand in "use") - if _sp_full_spec=$(command spack $_sp_flags dotkit find $_sp_subcommand_args "${_sp_spec[@]}"); then + if _sp_full_spec=$(command spack $_sp_flags module dotkit find $_sp_subcommand_args "${_sp_spec[@]}"); then use $_sp_module_args $_sp_full_spec else $(exit 1) fi ;; "unuse") - if _sp_full_spec=$(command spack $_sp_flags dotkit find $_sp_subcommand_args "${_sp_spec[@]}"); then + if _sp_full_spec=$(command spack $_sp_flags module dotkit find $_sp_subcommand_args "${_sp_spec[@]}"); then unuse $_sp_module_args $_sp_full_spec else $(exit 1) fi ;; "load") - if _sp_full_spec=$(command spack $_sp_flags tcl find $_sp_subcommand_args "${_sp_spec[@]}"); then + if _sp_full_spec=$(command spack $_sp_flags module tcl find $_sp_subcommand_args "${_sp_spec[@]}"); then module load $_sp_module_args $_sp_full_spec else $(exit 1) fi ;; "unload") - if _sp_full_spec=$(command spack $_sp_flags tcl find $_sp_subcommand_args "${_sp_spec[@]}"); then + if _sp_full_spec=$(command spack $_sp_flags module tcl find $_sp_subcommand_args "${_sp_spec[@]}"); then module unload $_sp_module_args $_sp_full_spec else $(exit 1) -- cgit v1.2.3-60-g2f50