Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .hunspell.en.dic
Original file line number Diff line number Diff line change
Expand Up @@ -1521,3 +1521,19 @@ gitignored
ObjCmd
codeload
integrations
evaluateModulerc
mhook
API
api
modnamevr
modulerc's
logRequestedLoad
auditRequestedLoad
blockModule
fd
lindex
hookEvents
runHooks
appdir
build3
rsync
1 change: 1 addition & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ howtos are also available:

* :ref:`add-new-sub-command`
* :ref:`add-new-config-option`
* :ref:`add-new-hook-event`
* :ref:`devel-testsuite`

.. _running-the-tests:
Expand Down
41 changes: 41 additions & 0 deletions MIGRATING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,47 @@ When values are later removed from an environment variable, it is
automatically unset if its resulting value matches the configured initial
value and no explicit reference counter is associated with it.

Hook API
^^^^^^^^

Site code that needs to run right before or after a modulefile or modulerc
evaluation could so far only be attached through the ``trace`` Tcl command or
by renaming an internal :file:`modulecmd.tcl` procedure, both of which bind
to implementation details that may change from one Modules version to the
next.

A new ``add-hook`` siteconfig command is introduced to register a procedure
on one of four stable events without relying on such internal details:
:mhook:`before-modulefile-eval`, :mhook:`after-modulefile-eval`,
:mhook:`before-modulerc-eval` and :mhook:`after-modulerc-eval`. Several
procedures can be registered on the same event; they are then called in
their registration order.

.. code-block:: tcl

proc auditRequestedLoad {modfile modname modnamevr modspec mode requested} {
if {$requested && $mode eq {load}} {
set fd [open /var/log/modules-audit.log a]
puts $fd "[clock format [clock seconds]] $modnamevr"
close $fd
}
}
add-hook before-modulefile-eval auditRequestedLoad

An error raised by a hook procedure is reported but does not abort the
running ``module`` command nor prevent other procedures registered on the
same event from running, since these hooks fire on nearly every modulefile
or modulerc evaluation.

See the *Hooks* section of :manpage:`module(1)` man page for the full
argument contract of each event. The ``trace``/rename techniques remain
available for anything not (yet) covered by a hook event.

These four events are only the first ones introduced through ``add-hook``;
additional hook events will be added in the future as real site needs for
them come up. See :ref:`add-new-hook-event` for the guide contributors can
follow to propose one.


v5.6
----
Expand Down
11 changes: 11 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,17 @@ Modules 5.7.0 (not yet released)
to 1024 characters, which could corrupt the system ``PATH`` when installing
from a shell with an already long inherited ``PATH`` (e.g. a Visual Studio
Developer Prompt). (fix issue #654)
* Doc: add :ref:`hook-api` design notes.
* Add the ``add-hook`` siteconfig command to register a procedure on one of
4 stable hook events (:mhook:`before-modulefile-eval`,
:mhook:`after-modulefile-eval`, :mhook:`before-modulerc-eval`,
:mhook:`after-modulerc-eval`), so sites no longer need to rely on ``trace``
or procedure renaming, which bind to internal implementation details, to
run code before or after a modulefile or modulerc evaluation. (fix issue
#607)
* Doc: add :ref:`add-new-hook-event` guide describing how to contribute a
new hook event.
* Doc: add :ref:`sync-remote-appdir` cookbook recipe.


.. _5.6 release notes:
Expand Down
1 change: 1 addition & 0 deletions doc/example/sync-remote-appdir/.module_appdir_map
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo/2.1 foo-2.1-build3
9 changes: 9 additions & 0 deletions doc/example/sync-remote-appdir/initrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#%Module5.7

# give the 'remote' module tag its own abbreviation and color, and make
# sure it is not persisted onto a module once it gets loaded (see the "Sync
# remote application directories on first load" cookbook recipe)
module config tag_abbrev "+remote=R"
module config colors "+R=38;5;202"
module config tag_color_name +remote
module config non_exportable_tags +remote
21 changes: 21 additions & 0 deletions doc/example/sync-remote-appdir/modulefiles/.modulerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#%Module5.7

# tag every module listed in the remote application directory map as
# 'remote', unless its application directory has already been synced to
# local disk (see siteconfig.tcl for how the sync itself is triggered)
set mapfile [file join /remote_apps .module_appdir_map]
if {[file readable $mapfile]} {
set fid [open $mapfile r]
set fdata [split [read $fid] "\n"]
close $fid
foreach fline $fdata {
if {[llength $fline] != 2} {
continue
}
lassign $fline modnamevr appdir
set syncedfile [file join /local_apps ".$appdir.synced"]
if {![file exists $syncedfile]} {
module-tag remote $modnamevr
}
}
}
5 changes: 5 additions & 0 deletions doc/example/sync-remote-appdir/modulefiles/foo/2.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#%Module5.7

set appdir foo-2.1-build3
prepend-path PATH /local_apps/$appdir/bin
prepend-path LD_LIBRARY_PATH /local_apps/$appdir/lib
74 changes: 74 additions & 0 deletions doc/example/sync-remote-appdir/siteconfig.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#
# siteconfig.tcl - Site specific configuration script that copies, the first
# time a module tagged 'remote' is loaded, its application directory from
# a remote network share to local disk with rsync, so this and every later
# load of the same module read from local disk instead of the network
# share.
#
# Author: Xavier Delaruelle <xavier.delaruelle@cea.fr>
# Compatibility: Modules v5.7+
#
# Installation: put this file in the 'etc' directory of your Modules
# installation. Refer to the "Modulecmd startup" section in the
# module(1) man page to get this location.

# root of the mounted network share and of its local counterpart
set g_remoteAppDir /remote_apps
set g_localAppDir /local_apps

# return the application directory basename mapped to given bare module name
# and version, or an empty string if this module has no mapped directory
proc getAppDirBasename {modname} {
set mapfile [file join $::g_remoteAppDir .module_appdir_map]
if {![file readable $mapfile]} {
return {}
}
set fid [open $mapfile r]
set fdata [split [read $fid] "\n"]
close $fid
foreach fline $fdata {
if {[llength $fline] == 2 && [lindex $fline 0] eq $modname} {
return [lindex $fline 1]
}
}
return {}
}

# copy application directory from the remote network share to local disk, on
# the first load of a module tagged 'remote' (see the modulepath root
# .modulerc file for how this tag gets applied)
proc syncRemoteAppDir {modfile modname modnamevr modspec mode requested} {
if {$mode ne {load}} {
return
}
set itrp [getCurrentModfileInterpName]
if {![interp eval $itrp {module-info tags remote}]} {
return
}

set appdir [getAppDirBasename $modname]
if {$appdir eq {}} {
return
}

set syncedfile [file join $::g_localAppDir ".$appdir.synced"]
if {[file exists $syncedfile]} {
return
}

report "Syncing '$appdir' application directory from remote share..."
file mkdir $::g_localAppDir
set srcdir [file join $::g_remoteAppDir $appdir]
set destdir [file join $::g_localAppDir $appdir]
if {[catch {exec rsync -a --delete $srcdir/ $destdir/} errMsg]} {
reportError "Failed to sync '$appdir' from remote share\n$errMsg"
return
}

# mark this application directory as synced so it does not get copied
# again on a later load
close [open $syncedfile w]
}
add-hook before-modulefile-eval syncRemoteAppDir

# vim:set tabstop=3 shiftwidth=3 expandtab autoindent:
18 changes: 18 additions & 0 deletions doc/source/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,24 @@ user environment is already configured.

Starting version ``5.5``, support for Windows *pwsh* shell is introduced.

Siteconfig hooks
""""""""""""""""

Starting version ``5.7``, the ``add-hook`` siteconfig command is introduced
to register a procedure to run on a hook event, as a replacement for the
``trace``/procedure-renaming techniques that bind to internal
implementation details.

The following hook events appeared on Modules 5.

+------------+-----------------------------------------------------------------+
| Introduced | New hook events |
| in version | |
+============+=================================================================+
| 5.7 | :mhook:`before-modulefile-eval`, :mhook:`after-modulefile-eval`,|
| | :mhook:`before-modulerc-eval`, :mhook:`after-modulerc-eval` |
+------------+-----------------------------------------------------------------+

Command line switches
"""""""""""""""""""""

Expand Down
4 changes: 4 additions & 0 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,10 @@ def setup(app):
objname='siteconfig variable',
indextemplate='pair: %s; siteconfig variable',
parse_node=parse_cmd_args_node)
app.add_object_type(directivename='mhook', rolename='mhook',
objname='hook event',
indextemplate='pair: %s; hook event',
parse_node=parse_cmd_args_node)
app.add_object_type('instopt', 'instopt',
objname='installation option',
indextemplate='pair: %s; installation option',
Expand Down
Loading
Loading