diff --git a/.hunspell.en.dic b/.hunspell.en.dic index d4180db77..d1d50bce1 100644 --- a/.hunspell.en.dic +++ b/.hunspell.en.dic @@ -1634,3 +1634,7 @@ defineModStartNbProc isIcase CVE backport +moduleversion +myModuleFullName +myModuleName +myModuleVersion diff --git a/NEWS.rst b/NEWS.rst index 7045c54b1..ae6582beb 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -15,6 +15,9 @@ Modules 5.8.0 (not yet released) designated by :instopt:`--with-tcl` when :instopt:`--with-tclsh` is not set, so that installed scripts run with the same Tcl the extension library is built against. (fix issue #717) +* Add :mfcmd:`module-info modulename` and :mfcmd:`module-info + moduleversion` modulefile commands to get the name part and + the version part of currently evaluating modulefile. (fix issue #713) .. _5.7 release notes: diff --git a/doc/source/changes.rst b/doc/source/changes.rst index 75e9360a7..21f1a90c7 100644 --- a/doc/source/changes.rst +++ b/doc/source/changes.rst @@ -1148,6 +1148,9 @@ The following modulefile Tcl commands appeared on Modules 5. +------------+-----------------------------------------------------------------+ | 5.7 | :mfcmd:`modulepath-ignore` | +------------+-----------------------------------------------------------------+ +| 5.8 | :mfcmd:`module-info modulename`, | +| | :mfcmd:`module-info moduleversion` | ++------------+-----------------------------------------------------------------+ :mfcmd:`system`, :mfcmd:`is-used` diff --git a/doc/source/cookbook/module-info-name-return-basename.rst b/doc/source/cookbook/module-info-name-return-basename.rst index d1cff303b..1260e21df 100644 --- a/doc/source/cookbook/module-info-name-return-basename.rst +++ b/doc/source/cookbook/module-info-name-return-basename.rst @@ -11,6 +11,15 @@ as once loaded this module is identified by its full pathname. This recipe describes a way to get back the behavior of Modules compatibility version for the ``module-info name`` modulefile command. +.. note:: + + Starting Modules 5.8, the :mfcmd:`module-info moduleversion` + modulefile command returns the part of the module name after its last + ``/`` character, which is the file basename when module is specified as a + full path. It can be used instead of this recipe when the goal is to get + this basename rather than to change the result of :mfcmd:`module-info + name`. + Implementation -------------- diff --git a/doc/source/modulefile.rst b/doc/source/modulefile.rst index e5ba90233..9c43e3240 100644 --- a/doc/source/modulefile.rst +++ b/doc/source/modulefile.rst @@ -875,6 +875,45 @@ the *modulefile* is being loaded. ``refresh``, ``nonpersist`` (alias of ``refresh``), ``display``, ``help``, ``test``, ``whatis`` or ``scan``. + **module-info modulename** + + Return the name of currently evaluating *modulefile* without its version + part, which is the string returned by :mfcmd:`module-info + name` without the last ``/`` character and what follows it. + Returns ``app`` for a *modulefile* named ``app/1.2.3`` and ``compiler/gcc`` + for ``compiler/gcc/12.1``. Returns the full name if it contains no ``/`` + character. + + If currently evaluating *modulefile* has been specified as a full path, + :mfcmd:`module-info name` returns this full path, so + :mfcmd:`module-info modulename` returns the directory part of + this path. + + .. only:: html or latex + + .. versionadded:: 5.8 + + **module-info moduleversion** + + Return the version of currently evaluating *modulefile*, which is the part + after the last ``/`` character of the string returned by + :mfcmd:`module-info name`. Returns ``1.2.3`` for a + *modulefile* named ``app/1.2.3`` and ``12.1`` for ``compiler/gcc/12.1``. + Returns an empty string if the name contains no ``/`` character. + + Symbolic versions and aliases are resolved as for :mfcmd:`module-info + name`, so the version of the resolved *modulefile* is + returned. Variants are not included in the returned string. + + If currently evaluating *modulefile* has been specified as a full path, + :mfcmd:`module-info name` returns this full path, so + :mfcmd:`module-info moduleversion` returns the file basename of + this path. + + .. only:: html or latex + + .. versionadded:: 5.8 + **module-info name** Return the name of the *modulefile*. This is not the full pathname for @@ -886,6 +925,10 @@ the *modulefile* is being loaded. See :mfcmd:`getvariant` command or :mfvar:`ModuleVariant` array variable to get defined variant values for currently evaluating *modulefile*. + See :mfcmd:`module-info modulename` and :mfcmd:`module-info + moduleversion` to get separately the name and version parts of + the returned string. + **module-info shell** [shellname] Return the current shell under which :file:`modulecmd.tcl` was invoked if diff --git a/doc/source/other-implementations.rst b/doc/source/other-implementations.rst index 203e8b56e..d1a3d0364 100644 --- a/doc/source/other-implementations.rst +++ b/doc/source/other-implementations.rst @@ -56,8 +56,9 @@ table highlights features that are unique to each implementation. * Handle modulefile outside modulepath * :ref:`Case insensitive module load` * Automatic ``latest`` and ``loaded`` symbols - * ``alias``, ``command``, ``loaded``, ``tags``, ``usergroups`` and - ``username`` sub-commands of :mfcmd:`module-info` + * ``alias``, ``command``, ``loaded``, ``modulename``, + ``moduleversion``, ``tags``, ``usergroups`` and ``username`` + sub-commands of :mfcmd:`module-info` * :ref:`Super-sticky modules` * :ref:`Fine-tuned output configuration<--output>` * :ref:`Editing modulefiles` @@ -217,6 +218,12 @@ implementation. - |foo@5.0:7.0|_ * - ``latest("foo")`` - |foo@latest|_ + * - ``myModuleFullName()`` + - :mfcmd:`module-info name` + * - ``myModuleName()`` + - :mfcmd:`module-info modulename` + * - ``myModuleVersion()`` + - :mfcmd:`module-info moduleversion` .. _Irreversible module actions: https://lmod.readthedocs.io/en/latest/370_irreversible.html .. _NAG file: https://lmod.readthedocs.io/en/latest/140_deprecating_modules.html diff --git a/doc/source/user-guide.rst b/doc/source/user-guide.rst index 617294bad..ca741f9b5 100644 --- a/doc/source/user-guide.rst +++ b/doc/source/user-guide.rst @@ -770,6 +770,42 @@ This allows users to write: :ps:`$` module load openmpi %gcc +``module-info`` +^^^^^^^^^^^^^^^ + +The :mfcmd:`module-info` modulefile command provides information about the +current evaluation context, like the name of the module being evaluated or +the evaluation mode. + +The :mfcmd:`module-info name` sub-command returns the full name +of the evaluating module, like ``app/1.2.3``. Modulefiles often need only +the version part of this name, to build an installation path for instance. +The :mfcmd:`module-info moduleversion` sub-command returns this +part, what follows the last ``/`` character of the module name, and the +:mfcmd:`module-info modulename` sub-command returns what comes +before this character. + +For example: + +.. code-block:: tcl + + #%Module + + set prefix /opt/[module-info modulename]/[module-info moduleversion] + prepend-path PATH $prefix/bin + +For a module named ``compiler/gcc/12.1``, :mfcmd:`module-info +modulename` returns ``compiler/gcc`` and :mfcmd:`module-info +moduleversion` returns ``12.1``. If the module name contains no +``/`` character, the full name is returned as name part and the version part +is an empty string. + +.. tip:: + + Symbolic versions and aliases are resolved before the modulefile is + evaluated, so these sub-commands always report the name of the resolved + modulefile. + Advanced manipulations ---------------------- @@ -1085,9 +1121,7 @@ name being evaluated. #%Module - set version [lindex [split [module-info name] /] end] - - setenv APP_VERSION $version + setenv APP_VERSION [module-info moduleversion] Module logger ^^^^^^^^^^^^^ diff --git a/share/nagelfar/syntaxdb_modulefile.tcl b/share/nagelfar/syntaxdb_modulefile.tcl index 2d89c54ef..016a13974 100644 --- a/share/nagelfar/syntaxdb_modulefile.tcl +++ b/share/nagelfar/syntaxdb_modulefile.tcl @@ -164,6 +164,8 @@ set {::syntax(module-info alias)} 1 set {::syntax(module-info command)} x? set {::syntax(module-info loaded)} 1 set {::syntax(module-info mode)} x? +set {::syntax(module-info modulename)} 0 +set {::syntax(module-info moduleversion)} 0 set {::syntax(module-info name)} 0 set {::syntax(module-info shell)} x? set {::syntax(module-info shelltype)} x? @@ -240,9 +242,9 @@ set ::subCmd(module) {add load add-any load-any swap switch try-add try-load\ initadd initload initclear initlist initprepend initunload initrm\ initswap initswitch list purge reload restore save savelist saverm disable\ saveshow describe apropos keyword search test update whatis} -set ::subCmd(module-info) {alias command loaded mode name shell shelltype\ - specified symbols tags type usergroups username version user trace\ - tracepat flags} +set ::subCmd(module-info) {alias command loaded mode modulename moduleversion\ + name shell shelltype specified symbols tags type usergroups username\ + version user trace tracepat flags} set ::subCmd(uname) {sysname nodename domain release version machine} # option diff --git a/share/nagelfar/syntaxdb_modulerc.tcl b/share/nagelfar/syntaxdb_modulerc.tcl index 65d487e21..38b7444b0 100644 --- a/share/nagelfar/syntaxdb_modulerc.tcl +++ b/share/nagelfar/syntaxdb_modulerc.tcl @@ -60,6 +60,8 @@ set {::syntax(module-info alias)} 1 set {::syntax(module-info command)} x? set {::syntax(module-info loaded)} 1 set {::syntax(module-info mode)} x? +set {::syntax(module-info modulename)} 0 +set {::syntax(module-info moduleversion)} 0 set {::syntax(module-info name)} 0 set {::syntax(module-info shell)} x? set {::syntax(module-info shelltype)} x? @@ -96,9 +98,9 @@ set {::return(module-info flags)} int # subCmd set ::subCmd(lsb-release) {id release codename} -set ::subCmd(module-info) {alias command loaded mode name shell shelltype\ - specified symbols tags type usergroups username version user trace\ - tracepat flags} +set ::subCmd(module-info) {alias command loaded mode modulename moduleversion\ + name shell shelltype specified symbols tags type usergroups username\ + version user trace tracepat flags} set ::subCmd(uname) {sysname nodename domain release version machine} # option diff --git a/tcl/mfcmd.tcl b/tcl/mfcmd.tcl index a5700d72e..6c0417663 100644 --- a/tcl/mfcmd.tcl +++ b/tcl/mfcmd.tcl @@ -60,6 +60,25 @@ proc module-info {what {more {}}} { name { return [currentState modulename] } + modulename { + set modname [currentState modulename] + set idx [string last / $modname] + # whole name is the name part if it has no version separator + if {$idx == -1} { + return $modname + } else { + return [string range $modname 0 $idx-1] + } + } + moduleversion { + set modname [currentState modulename] + set idx [string last / $modname] + if {$idx == -1} { + return {} + } else { + return [string range $modname $idx+1 end] + } + } specified { return [currentState specifiedname] } diff --git a/testsuite/modulefiles.5/.modulerc b/testsuite/modulefiles.5/.modulerc new file mode 100644 index 000000000..c04838d42 --- /dev/null +++ b/testsuite/modulefiles.5/.modulerc @@ -0,0 +1,9 @@ +#%Module +if {[info exists env(TESTSUITE_INFO_MODULENAME)]} { + switch -- $env(TESTSUITE_INFO_MODULENAME) { + sym { + module-version info-name/1.2.3 sym + module-alias info-name-alias info-name/sub/2.0 + } + } +} diff --git a/testsuite/modulefiles.5/info-name/1.10 b/testsuite/modulefiles.5/info-name/1.10 new file mode 100644 index 000000000..91a77dbf9 --- /dev/null +++ b/testsuite/modulefiles.5/info-name/1.10 @@ -0,0 +1,4 @@ +#%Module +setenv TS_NAME [module-info name] +setenv TS_MODULENAME [module-info modulename] +setenv TS_MODULEVERSION [module-info moduleversion] diff --git a/testsuite/modulefiles.5/info-name/1.2.3 b/testsuite/modulefiles.5/info-name/1.2.3 new file mode 100644 index 000000000..91a77dbf9 --- /dev/null +++ b/testsuite/modulefiles.5/info-name/1.2.3 @@ -0,0 +1,4 @@ +#%Module +setenv TS_NAME [module-info name] +setenv TS_MODULENAME [module-info modulename] +setenv TS_MODULEVERSION [module-info moduleversion] diff --git a/testsuite/modulefiles.5/info-name/sub/2.0 b/testsuite/modulefiles.5/info-name/sub/2.0 new file mode 100644 index 000000000..91a77dbf9 --- /dev/null +++ b/testsuite/modulefiles.5/info-name/sub/2.0 @@ -0,0 +1,4 @@ +#%Module +setenv TS_NAME [module-info name] +setenv TS_MODULENAME [module-info modulename] +setenv TS_MODULEVERSION [module-info moduleversion] diff --git a/testsuite/modulefiles.5/info-noversion b/testsuite/modulefiles.5/info-noversion new file mode 100644 index 000000000..91a77dbf9 --- /dev/null +++ b/testsuite/modulefiles.5/info-noversion @@ -0,0 +1,4 @@ +#%Module +setenv TS_NAME [module-info name] +setenv TS_MODULENAME [module-info modulename] +setenv TS_MODULEVERSION [module-info moduleversion] diff --git a/testsuite/modules.50-cmds/289-info-modulename.exp b/testsuite/modules.50-cmds/289-info-modulename.exp new file mode 100644 index 000000000..e658d7509 --- /dev/null +++ b/testsuite/modules.50-cmds/289-info-modulename.exp @@ -0,0 +1,137 @@ +############################################################################## +# Modules Revision 3.0 +# Providing a flexible user environment +# +# File: modules.50-cmds/%M% +# Revision: %I% +# First Edition: 2026/10/27 +# Last Mod.: %U%, %G% +# +# Authors: Xavier Delaruelle, xavier.delaruelle@cea.fr +# +# Description: Testuite testsequence +# Command: load, display, unload +# Modulefiles: info-name, info-noversion +# Sub-Command: +# +# Comment: %C{ +# Check 'module-info modulename' and 'module-info moduleversion' +# modulefile commands +# }C% +# +############################################################################## + +set mp $modpath.5 +set mpre [regsub -all "\(\[.+?\]\)" $mp {\\\1}] + +# setup specific environment +setenv_path_var MODULEPATH $mp + + +# regular module name made of name and version +set ans [list] +lappend ans [list set TS_MODULENAME info-name] +lappend ans [list set _LMFILES_ $mp/info-name/1.2.3] +lappend ans [list set LOADEDMODULES info-name/1.2.3] +lappend ans [list set TS_MODULEVERSION 1.2.3] +lappend ans [list set TS_NAME info-name/1.2.3] +testouterr_cmd sh {load info-name/1.2.3} $ans {} + +skip_if_quick_mode + +# deep module name, version is the last element only +set ans [list] +if {[is_conf_enabled advversspec implicitdefault]} { + lappend ans [list set __MODULES_LMALTNAME info-name/sub/2.0&as|info-name/default&as|info-name/latest&as|info-name/sub/default&as|info-name/sub/latest] +} +lappend ans [list set TS_MODULENAME info-name/sub] +lappend ans [list set _LMFILES_ $mp/info-name/sub/2.0] +lappend ans [list set LOADEDMODULES info-name/sub/2.0] +lappend ans [list set TS_MODULEVERSION 2.0] +lappend ans [list set TS_NAME info-name/sub/2.0] +testouterr_cmd sh {load info-name/sub/2.0} $ans {} + +# version string that looks like a number is returned untouched +set ans [list] +lappend ans [list set TS_MODULENAME info-name] +lappend ans [list set _LMFILES_ $mp/info-name/1.10] +lappend ans [list set LOADEDMODULES info-name/1.10] +lappend ans [list set TS_MODULEVERSION 1.10] +lappend ans [list set TS_NAME info-name/1.10] +testouterr_cmd sh {load info-name/1.10} $ans {} + +# module name without version +set ans [list] +lappend ans [list set TS_MODULENAME info-noversion] +lappend ans [list set _LMFILES_ $mp/info-noversion] +lappend ans [list set LOADEDMODULES info-noversion] +lappend ans [list set TS_MODULEVERSION {}] +lappend ans [list set TS_NAME info-noversion] +testouterr_cmd sh {load info-noversion} $ans {} + +# symbolic version and alias are resolved like module-info name does +setenv_var TESTSUITE_INFO_MODULENAME sym + +set ans [list] +lappend ans [list set __MODULES_LMALTNAME info-name/1.2.3&info-name/sym] +lappend ans [list set TS_MODULENAME info-name] +lappend ans [list set _LMFILES_ $mp/info-name/1.2.3] +lappend ans [list set LOADEDMODULES info-name/1.2.3] +lappend ans [list set TS_MODULEVERSION 1.2.3] +lappend ans [list set TS_NAME info-name/1.2.3] +testouterr_cmd sh {load info-name/sym} $ans {} + +set altname info-name/sub/2.0&al|info-name-alias +if {[is_conf_enabled advversspec implicitdefault]} { + append altname &as|info-name/default&as|info-name/latest&as|info-name/sub/default&as|info-name/sub/latest +} +set ans [list] +lappend ans [list set __MODULES_LMALTNAME $altname] +lappend ans [list set TS_MODULENAME info-name/sub] +lappend ans [list set _LMFILES_ $mp/info-name/sub/2.0] +lappend ans [list set LOADEDMODULES info-name/sub/2.0] +lappend ans [list set TS_MODULEVERSION 2.0] +lappend ans [list set TS_NAME info-name/sub/2.0] +testouterr_cmd sh {load info-name-alias} $ans {} + +unsetenv_var TESTSUITE_INFO_MODULENAME + +# module specified as a full path: name is the full path to modulefile +set ans [list] +lappend ans [list set TS_MODULENAME $mp/info-name] +lappend ans [list set _LMFILES_ $mp/info-name/1.2.3] +lappend ans [list set LOADEDMODULES $mp/info-name/1.2.3] +lappend ans [list set TS_MODULEVERSION 1.2.3] +lappend ans [list set TS_NAME $mp/info-name/1.2.3] +testouterr_cmd sh "load $mp/info-name/1.2.3" $ans {} + +# display evaluation +set tserr "$modlin +$mpre/info-name/1.2.3: + +setenv\t\tTS_NAME info-name/1.2.3 +setenv\t\tTS_MODULENAME info-name +setenv\t\tTS_MODULEVERSION 1.2.3 +$modlin" +testouterr_cmd_re sh {display info-name/1.2.3} OK $tserr + +# unload evaluation +setenv_loaded_module [list info-name/sub/2.0] [list $mp/info-name/sub/2.0] +setenv_var TS_NAME info-name/sub/2.0 +setenv_var TS_MODULENAME info-name/sub +setenv_var TS_MODULEVERSION 2.0 + +set ans [list] +lappend ans [list unset TS_MODULENAME] +lappend ans [list unset _LMFILES_] +lappend ans [list unset LOADEDMODULES] +lappend ans [list unset TS_MODULEVERSION] +lappend ans [list unset TS_NAME] +testouterr_cmd sh {unload info-name/sub/2.0} $ans {} + + +# +# Cleanup +# + +reset_test_env