Created
July 30, 2025 02:58
-
-
Save dliptai/f99cc8e546f6c4f0a0b774ba252058ec to your computer and use it in GitHub Desktop.
Lmod hook to show missing dependencies/versions available when module load fails
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require("strict") | |
| local hook = require("Hook") | |
| local Cache = require("Cache") | |
| local Dbg = require("Dbg") | |
| local dbg = Dbg:dbg() | |
| ------------------------------------------------------------------ | |
| -- Helper functions to parse module strings | |
| ------------------------------------------------------------------ | |
| local function parse_module_string(module_str) | |
| -- Reject invalid strings that start with slash | |
| if string.match(module_str, "^\"/") then | |
| return nil, nil | |
| end | |
| -- Try to parse as "name/version" format | |
| local name, version = string.match(module_str, "\"([^/]*)/(.*)\"") | |
| if name then | |
| return name, version | |
| end | |
| -- Fall back to parsing as "name" only format | |
| name = string.match(module_str, "\"(.*)\"") | |
| return name, nil | |
| end | |
| local function get_available_versions(tbl, module_name) | |
| local versions = {} | |
| local seen = {} -- set-like table to track unique versions | |
| local moduleT = tbl[module_name] | |
| if moduleT then | |
| for _, valueT in pairs(moduleT) do | |
| local version = valueT["Version"] | |
| if not seen[version] then | |
| seen[version] = true | |
| table.insert(versions, version) | |
| end | |
| end | |
| end | |
| table.sort(versions) | |
| return versions | |
| end | |
| local function get_dependencies(tbl, module_name, module_version) | |
| local dependencies = {} | |
| local seen = {} -- set-like table to track unique dependencies | |
| local moduleT = tbl[module_name] | |
| if moduleT then | |
| for key, valueT in pairs(moduleT) do | |
| if valueT["Version"] == module_version then | |
| local parents = valueT["parentAA"] | |
| if parents and #parents == 1 then | |
| local deps = parents[1] | |
| if deps then | |
| local dep_str = table.concat(deps, " ") | |
| -- If dep_str contains gcccore, replace it with gcc | |
| if dep_str:find("gcccore") then | |
| dep_str = dep_str:gsub("gcccore", "gcc") | |
| end | |
| if not seen[dep_str] then | |
| seen[dep_str] = true | |
| table.insert(dependencies, dep_str) | |
| end | |
| end | |
| end | |
| end | |
| end | |
| end | |
| table.sort(dependencies) | |
| return dependencies | |
| end | |
| ------------------------------------------------------------------ | |
| -- Hook to modify error messages. | |
| -- (This gets called on every message, warnings and errors) | |
| ------------------------------------------------------------------ | |
| local function errwarnmsg_hook(kind, key, msg, t) | |
| -- kind is either lmoderror, lmodwarning or lmodmessage | |
| -- msg is the actual message to display (as string) | |
| -- key is a unique key for the message (see messageT.lua) | |
| -- t is a table with the keys used in msg | |
| dbg.start{"errwarnmsg_hook"} | |
| dbg.print{"kind: ", kind," key: ",key,"\n"} | |
| dbg.print{"keys: ", t, "\n"} | |
| if kind == "lmoderror" then | |
| -- Build the cache | |
| local cache = Cache:singleton{buildCache=true} | |
| local _, my_dbT, _, _ = cache:build() | |
| local module_name = nil | |
| local module_version = nil | |
| local module_string = nil | |
| if key == "e_Failed_Load_2" then | |
| -- "e_Failed_Load_2" is the case when the module is known, but cannot be loaded. | |
| -- Table keys are: | |
| -- kB: requested module as string without quotes | |
| -- kA: requested module as string with quotes "" | |
| module_string = t.kA | |
| end | |
| if key == "e_Failed_Load" then | |
| -- "e_Failed_Load" is the case when the module is not known. | |
| -- This may be because: | |
| -- 1. The module doesn't exist | |
| -- 2. The module exists, but not the requested version | |
| -- 3. The module is hidden | |
| -- Table keys are: | |
| -- module_list: requested module as string with quotes "" | |
| module_string = t.module_list | |
| end | |
| if module_string then | |
| module_name, module_version = parse_module_string(module_string) | |
| dbg.print{"Parsed module name: ", module_name, " version: ", module_version, "\n"} | |
| -- If module_name is nil, do nothing | |
| -- If module name is not nil, but module_version is nil, find all the versions of the module in the database | |
| -- If both are not nil, find the specific module in the database and all its dependencies | |
| if module_name then | |
| if not module_version then | |
| -- Find all versions of the module | |
| local versions = get_available_versions(my_dbT, module_name) | |
| -- if versions is not empty, build the message, otherwise show unknown module | |
| if versions and #versions > 0 then | |
| local versions_str = " - " .. module_name .. "/" .. table.concat(versions, "\n - " .. module_name .. "/") | |
| msg = string.format(msg_no_version, module_name, versions_str) | |
| else | |
| msg = string.format(msg_unknown_module, module_string, module_string) | |
| end | |
| else | |
| -- Find the specific module and its dependencies | |
| local dependencies = get_dependencies(my_dbT, module_name, module_version) | |
| local dependencies_str = " - " .. table.concat(dependencies, "\n - ") | |
| msg = string.format(msg_missing_dependencies, module_name, dependencies_str) | |
| end | |
| end | |
| end | |
| end | |
| dbg.fini() | |
| return msg | |
| end | |
| hook.register("errWarnMsgHook", errwarnmsg_hook) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment