1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
|
##########################################################################
# INTERP.TCL, sub-interpreter management procedures
# Copyright (C) 2002-2004 Mark Lakata
# Copyright (C) 2004-2017 Kent Mein
# Copyright (C) 2016-2025 Xavier Delaruelle
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
##########################################################################
# dummy proc to disable modulefile commands on some evaluation modes
proc nop {args} {}
# dummy proc for commands available on other Modules flavor but not here
proc nimp {cmd args} {
reportWarning "'$cmd' command not implemented"
}
# Get identifier name of current Tcl modulefile interpreter. An interp is
# dedicated to each mode/auto_handling option value/depth level of modulefile
# interpretation
proc getCurrentModfileInterpName {} {
return __modfile_[currentState mode]_[getConf auto_handling]_[depthState\
modulename]
}
# synchronize environment variable change over all started sub interpreters
proc interp-sync-env {op var {val {}}} {
set envvar ::env($var)
##nagelfar vartype envvar varName
# apply operation to main interpreter
switch -- $op {
set { set $envvar $val }
unset { unset $envvar }
}
# apply operation to each sub-interpreters if not found autosynced
if {[llength [interp slaves]]} {
reportDebug "$op var='$envvar', val='$val' on interp(s) [interp slaves]"
foreach itrp [interp slaves] {
switch -- $op {
set {
# no value pre-check on Windows platform as an empty value set
# means unsetting variable which lead querying value to error
if {[getState is_win] || ![interp eval $itrp [list info exists\
$envvar]] || [interp eval $itrp [list set $envvar]] ne\
$val} {
interp eval $itrp [list set $envvar $val]
}
}
unset {
if {[interp eval $itrp [list info exists $envvar]]} {
interp eval $itrp [list unset $envvar]
}
}
}
}
}
}
# Initialize list of interp alias commands to define for given evaluation mode
# and auto_handling enablement
proc initModfileModeAliases {mode auto aliasesVN aliasesPassArgVN\
tracesVN} {
global g_modfilePerModeAliases
upvar #0 $aliasesVN aliases
upvar #0 $aliasesPassArgVN aliasesPassArg
upvar #0 $tracesVN traces
if {![info exists g_modfilePerModeAliases]} {
set ::g_modfileBaseAliases [list versioncmp versioncmp getenv getenv\
getvariant getvariant is-loaded is-loaded is-saved is-saved is-used\
is-used is-avail is-avail uname uname module-info module-info\
modulepath-label modulepath-label exit exitModfileCmd reportCmdTrace\
reportCmdTrace reportWarning reportWarning reportError reportError\
incrErrorCount incrErrorCount report report isWin initStateIsWin\
puts putsModfileCmd getModuleContent getModuleContent lsb-release\
lsb-release getModuleHelpLines getModuleHelpLines]
if {[getConf source_cache]} {
lappend ::g_modfileBaseAliases source sourceModfileCmd
}
# list of alias commands whose target procedure is adapted according to
# the evaluation mode
set ::g_modfileEvalModes {load unload display help test whatis refresh\
scan}
##nagelfar ignore #49 Too long line
array set g_modfilePerModeAliases {
add-property {add-property nop reportCmd nop nop nop nop nop}
always-load {always-load nop reportCmd nop nop nop nop always-load-sc}
append-path {append-path append-path-un append-path append-path append-path edit-path-wh nop edit-path-sc}
chdir {chdir nop reportCmd nop nop nop nop chdir-sc }
complete {complete complete-un reportCmd nop nop nop complete complete-sc }
conflict {conflict nop reportCmd nop nop nop nop conflict-sc }
depends-on {prereqAllModfileCmd nop reportCmd nop nop nop nop prereq-all-sc}
depends-on-any {prereqAnyModfileCmd nop reportCmd nop nop nop nop prereq-sc }
extensions {provide nop reportCmd nop nop nop nop provide-sc }
family {family family-un reportCmd nop nop nop nop family-sc }
haveDynamicMPATH {nop nop nop nop nop nop nop nop }
hide-modulefile {hide-modulefile hide-modulefile hide-modulefile hide-modulefile hide-modulefile hide-modulefile nop nop }
hide-version {hide-modulefile hide-modulefile hide-modulefile hide-modulefile hide-modulefile hide-modulefile nop nop }
module {module module reportCmd nop nop nop nop module-sc }
module-alias {module-alias module-alias module-alias module-alias module-alias module-alias nop nop }
module-log {nimp nimp reportCmd nop nop nop nop nop }
module-trace {nimp nimp reportCmd nop nop nop nop nop }
module-user {nimp nimp reportCmd nop nop nop nop nop }
module-verbosity {nimp nimp reportCmd nop nop nop nop nop }
module-version {module-version module-version module-version module-version module-version module-version nop nop }
module-virtual {module-virtual module-virtual module-virtual module-virtual module-virtual module-virtual nop nop }
module-forbid {module-forbid module-forbid module-forbid module-forbid module-forbid module-forbid nop nop }
module-help {nop nop reportCmd module-help nop nop nop nop }
module-hide {module-hide module-hide module-hide module-hide module-hide module-hide nop nop }
module-tag {module-tag module-tag module-tag module-tag module-tag module-tag nop nop }
module-warn {module-warn module-warn module-warn module-warn module-warn module-warn nop nop }
module-whatis {nop nop reportCmd nop nop module-whatis nop nop }
prepend-path {prepend-path prepend-path-un prepend-path prepend-path prepend-path edit-path-wh nop edit-path-sc}
prereq-all {prereqAllModfileCmd nop reportCmd nop nop nop nop prereq-all-sc}
prereq-any {prereqAnyModfileCmd nop reportCmd nop nop nop nop prereq-sc }
prereq {prereqAnyModfileCmd nop reportCmd nop nop nop nop prereq-sc }
provide {provide nop reportCmd nop nop nop nop provide-sc }
pushenv {pushenv pushenv-un pushenv pushenv pushenv pushenv-wh nop pushenv-sc }
remove-path {remove-path remove-path-un remove-path remove-path remove-path edit-path-wh nop edit-path-sc}
remove-property {nop nop nop nop nop nop nop nop }
require-fullname {require-fullname nop reportCmd nop nop nop nop nop }
set-alias {set-alias set-alias-un reportCmd nop nop nop set-alias set-alias-sc}
set-function {set-function set-function-un reportCmd nop nop nop set-function set-function-sc}
setenv {setenv setenv-un setenv setenv setenv setenv-wh nop setenv-sc }
source-sh {source-sh source-sh-un source-sh-di nop nop nop source-sh source-sh }
system {system system reportCmd nop nop nop nop nop }
unique-name-conflict {unique-name-conflict nop nop nop nop nop nop nop }
uncomplete {uncomplete nop reportCmd nop nop nop nop uncomplete-sc}
unset-alias {unset-alias nop reportCmd nop nop nop nop unset-alias-sc}
unset-function {unset-function nop reportCmd nop nop nop nop unset-function-sc}
unsetenv {unsetenv unsetenv-un unsetenv unsetenv unsetenv unsetenv-wh nop unsetenv-sc }
variant {variant variant variant variant variant variant-wh variant variant-sc }
x-resource {x-resource x-resource reportCmd nop nop nop nop nop }
}
}
# alias commands where interpreter ref should be passed as argument
array set aliasesPassArg [list getvariant [list __itrp__] puts [list\
__itrp__] variant [list __itrp__] source [list __itrp__]]
# initialize list with all commands not dependent of the evaluation mode
array set aliases $::g_modfileBaseAliases
# add site-specific command aliases for modulefile interp
if {[info exists ::modulefile_extra_cmds]} {
if {[catch {array set aliases $::modulefile_extra_cmds} errorMsg]} {
knerror "Invalid value '$::modulefile_extra_cmds' ($errorMsg)\nfor\
siteconfig variable 'modulefile_extra_cmds'"
}
}
# add alias commands whose target command vary depending on the eval mode
set modeidx [lsearch -exact $::g_modfileEvalModes $mode]
foreach alias [array names g_modfilePerModeAliases] {
set aliastarget [set aliases($alias) [lindex\
$g_modfilePerModeAliases($alias) $modeidx]]
# some target procedures need command name as first arg
if {$aliastarget in {reportCmd nimp edit-path-wh edit-path-sc}} {
set aliasesPassArg($alias) [list $alias]
# prereq commands need auto_handling state as first arg
} elseif {$mode eq {load} && $alias in {prereq prereq-any prereq-all\
depends-on depends-on-any}} {
set aliasesPassArg($alias) [list 0 $auto]
# associate a trace command if per-mode alias command is not reportCmd
# in display mode (except for source-sh and unique-name-conflict)
} elseif {$mode eq {display} && $alias ni {source-sh\
unique-name-conflict}} {
set traces($alias) reportCmdTrace
}
}
}
proc execute-modulefile {modfile modname modnamevrvar modspec requested\
{up_namevr 1} {fetch_tags 1} {modpath {}}} {
# link to modnamevr variable name from calling ctx if content update asked
if {$up_namevr} {
upvar $modnamevrvar modnamevr
} else {
set modnamevr $modnamevrvar
}
lappendState modulefile $modfile
lappendState modulename $modname
lappendState modulenamevr $modnamevr
lappendState specifiedname $modspec
lappendState modulepath $modpath
set mode [currentState mode]
lappendState debug_msg_prefix\
"\[#[depthState modulename]:$mode:$modname\] "
# skip modulefile if interpretation has been inhibited
if {[getState inhibit_interp]} {
reportDebug "skipping $modfile"
return 1
}
reportTrace "'$modfile' as '$modname'" {Evaluate modulefile}
# reset modulefile-specific information
set ::g_help_lines {}
# gather all tags of evaluated modulefile
if {$fetch_tags} {
cacheCurrentModules 0
collectModuleTags $modnamevr
}
if {![info exists ::g_modfileUntrackVars]} {
# list variable that should not be tracked for saving
array set ::g_modfileUntrackVars [list ModulesCurrentModulefile 1 env 1]
# commands that should be renamed before aliases setup
array set ::g_modfileRenameCmds [list puts _puts]
}
# dedicate an interpreter per mode and per level of interpretation to have
# a dedicated interpreter in case of cascaded multi-mode interpretations
set itrp [getCurrentModfileInterpName]
# evaluation mode-specific configuration
set autosuf [expr {[getConf auto_handling] ? {AH} : {}}]
set dumpCommandsVN g_modfile${mode}${autosuf}Commands
set aliasesVN g_modfile${mode}${autosuf}Aliases
set aliasesPassArgVN g_modfile${mode}${autosuf}AliasesPassArg
set tracesVN g_modfile${mode}${autosuf}Traces
##nagelfar ignore Suspicious variable name
if {![info exists ::$aliasesVN]} {
##nagelfar vartype aliasesVN varName
##nagelfar vartype aliasesPassArgVN varName
##nagelfar vartype tracesVN varName
initModfileModeAliases $mode [getConf auto_handling] $aliasesVN\
$aliasesPassArgVN $tracesVN
}
# variable to define in modulefile interp
if {![info exists ::g_modfileBaseVars]} {
# record module tool properties
set ::g_modfileBaseVars [list ModuleTool Modules ModuleToolVersion\
[getState modules_release]]
if {[info exists ::modulefile_extra_vars]} {
if {([llength $::modulefile_extra_vars] % 2) != 0} {
knerror "Invalid value '$::modulefile_extra_vars' (list must have\
an even number of elements)\nfor siteconfig variable\
'modulefile_extra_vars'"
}
foreach {var val} $::modulefile_extra_vars {
if {[string first { } $var] != -1} {
knerror "Invalid variable name '$var'\ndefined in siteconfig\
variable 'modulefile_extra_vars'"
}
}
lappend ::g_modfileBaseVars {*}$::modulefile_extra_vars
}
}
# create modulefile interpreter at first interpretation
if {![interp exists $itrp]} {
reportDebug "creating interp $itrp"
interp create $itrp
# initialize global static variables for modulefile interp
foreach {var val} $::g_modfileBaseVars {
interp eval $itrp set ::$var "{$val}"
}
# dump initial interpreter state to restore it before each modulefile
# interpretation. use same dump state for all modes/levels
if {![info exists ::g_modfileVars]} {
dumpInterpState $itrp g_modfileVars g_modfileArrayVars\
g_modfileUntrackVars g_modfileProcs
}
# interp has just been created
set fresh 1
} else {
set fresh 0
}
# reset interp state command before each interpretation
resetInterpState $itrp $fresh g_modfileVars g_modfileArrayVars\
g_modfileUntrackVars g_modfileProcs $aliasesVN $aliasesPassArgVN\
$tracesVN g_modfileRenameCmds $dumpCommandsVN
set vr_spec_list [getVariantListFromVersSpec $modnamevr]
set failed_eval [catch {evaluateModulefile $itrp $modfile $vr_spec_list}\
errorMsg]
set eval_return_code [renderModulefileEvalError $itrp $mode $modfile\
$failed_eval $errorMsg]
if {$mode eq {load} && ![isStateDefined rc_running]} {
if {[catch {checkModuleConflict $modname $modnamevr} errorMsg]} {
reportError $errorMsg
set eval_return_code 1
}
}
# check if mod name version and variant has changed (default variant set)
# update modnamevr if so and collect tags applying to new name
if {$up_namevr} {
set newmodnamevr "{$modname}"
if {[set vr [getVariantList $modname 1]] ne {}} {
append newmodnamevr " $vr"
}
if {$modnamevr ne $newmodnamevr} {
set modnamevr_tag_list [getTagList $modnamevr $modfile]
set modnamevr_extratag_list [getExtraTagList $modnamevr]
lassign [parseModuleSpecification 0 0 0 0 {*}$newmodnamevr] modnamevr
# $up_namevr is only enabled when $fetch_tags is also enabled
collectModuleTags $modnamevr
# set tags applying to previous name (without default variant set)
# not to forget extra defined tags
setModuleTag $modnamevr {*}$modnamevr_tag_list
setModuleExtraTag $modnamevr {*}$modnamevr_extratag_list
}
}
# check if special tags now applies and require to raise an error
if {$mode ne {unload}} {
if {[isModuleTagged $modnamevr forbidden 1 $modfile]} {
set eval_return_code 1
reportError [getForbiddenMsg $modnamevr $modfile]
}
}
if {$mode ni {unload refresh scan whatis}} {
if {[isModuleTagged $modnamevr nearly-forbidden 1 $modfile]} {
reportWarning [getNearlyForbiddenMsg $modnamevr $modfile]
}
if {[isModuleTagged $modnamevr warning 1 $modfile]} {
reportWarning [getWarningMsg $modnamevr $modfile]
}
}
# record all module evaluated in scan structure for negation pattern search
if {$mode eq {scan}} {
recordScanModuleElt modulename all
}
# skip evaluation log for internal evaluation mode
if {$mode ni {refresh scan whatis}} {
set log_info_list [list {mode} $mode module $modnamevr specified\
$modspec modulefile $modfile {requested} $requested]
set log_event [expr {$requested ? {requested_eval} : {auto_eval}}]
logEvent $log_event {*}$log_info_list
}
reportDebug "exiting $modfile"
lpopState debug_msg_prefix
lpopState modulepath
lpopState specifiedname
lpopState modulename
lpopState modulenamevr
lpopState modulefile
return $eval_return_code
}
proc evaluateModulefile {itrp mod_file vr_spec_list} {
# reset modulefile-specific variable before each interpretation
interp eval $itrp set ::ModulesCurrentModulefile "{$mod_file}"
interp eval $itrp set vrspeclist "{$vr_spec_list}"
# eval then call for specific proc depending mode under same catch
##nagelfar ignore +3 Suspicious # char
interp eval $itrp {
info script $::ModulesCurrentModulefile
# raise conflict error if one name of currently loading module is shared
# by an already loaded module
unique-name-conflict
eval [getModuleContent $::ModulesCurrentModulefile]
# raise error if a variant specified is not defined in modulefile
set vrerrlist {}
foreach vrspec $vrspeclist {
set vrname [lindex $vrspec 0]
if {![info exists ::ModuleVariant($vrname)]} {
lappend vrerrlist "Unknown variant '$vrname' specified"
}
}
# report all unknown variants specified, raise error on last report
# take caution with vrerrlist variable as we are in mod_file eval ctx
if {[info exists vrerrlist] && [llength $vrerrlist]} {
for {set i 0} {$i < ([llength $vrerrlist] - 1)} {incr i} {
reportError [lindex $vrerrlist $i]
}
error [lindex $vrerrlist $i] {} MODULES_ERR_GLOBAL
}
switch -- [module-info mode] {
help {
foreach help_line [getModuleHelpLines] {
report $help_line
}
if {[info procs ModulesHelp] eq {ModulesHelp}} {
ModulesHelp
} elseif {![llength [getModuleHelpLines]]} {
reportWarning "Unable to find ModulesHelp in\
$::ModulesCurrentModulefile."
}
}
display {
if {[info procs ModulesDisplay] eq {ModulesDisplay}} {
ModulesDisplay
}
}
test {
if {[info procs ModulesTest] eq {ModulesTest}} {
if {[string is true -strict [ModulesTest]]} {
report {Test result: PASS}
} else {
report {Test result: FAIL}
incrErrorCount
}
} else {
reportWarning "Unable to find ModulesTest in\
$::ModulesCurrentModulefile."
}
}
}
}
}
proc renderModulefileEvalError {itrp mode mod_file failed_eval error_msg} {
if {!$failed_eval} {
return 0
}
set eval_return_code 1
# no error in case of "continue" command
# catch continue even if called outside of a loop
if {$error_msg eq {invoked "continue" outside of a loop} || $failed_eval\
== 4} {
set eval_return_code 0
unset error_msg
# catch break even if called outside of a loop
# on Darwin, error is different: no errorCode & return code set to 3
} elseif {$error_msg eq {invoked "break" outside of a loop} || ($error_msg\
eq {} && [getInterpVar $itrp ::errorInfo] eq {}) ||\
(![isInterpVarDefined $itrp ::errorCode] && $failed_eval == 3)} {
# report load/unload/refresh evaluation break if verbosity level
# >= normal, no error count raise during scan evaluation
if {$mode in {load unload refresh} && [isVerbosityLevel normal]} {
set error_msg {Module evaluation aborted}
} else {
unset error_msg
if {$mode ne {scan}} {
set raise_error_count 1
}
}
} else {
switch -- [getInterpVar $itrp errorCode] {
MODULES_ERR_READ {}
MODULES_ERR_VALIDITY {
set error_msg [formatMessageInModule $error_msg $mod_file]
set internal_bug 1
}
MODULES_ERR_SUBFAILED {
# error counter and message already handled, just return err code
unset error_msg
}
MODULES_ERR_GLOBAL {}
default {
set error_msg [formatInterpErrStackTrace $itrp $mod_file]
set internal_bug 1
}
}
}
# split force mode management code as non-unload modes still have a
# specific behavior (message returned as error and error exit code set)
if {$mode eq {unload}} {
if {[getState force]} {
set eval_return_code 0
}
if {[info exists error_msg]} {
if {[info exists internal_bug]} {
reportInternalBugOrWarningIfForced $error_msg
} else {
reportErrorOrWarningIfForced $error_msg
}
}
} else {
if {[info exists error_msg]} {
if {[info exists internal_bug]} {
reportInternalBug $error_msg
} else {
reportError $error_msg
}
} elseif {[info exists raise_error_count]} {
incrErrorCount
}
}
return $eval_return_code
}
# Raise an error if a conflict violation is detected. This is done after
# modulefile eval to give it a chance to solve its conflicts during eval
proc checkModuleConflict {mod_name mod_name_vr} {
set mod_con_list [getModuleLoadedConflict $mod_name]
if {[llength $mod_con_list]} {
set is_con_mod_conun [isModuleEvaluated conun $mod_name_vr {}\
{*}$mod_con_list]
set is_con_mod_loading [is-loading {*}$mod_con_list]
if {!$is_con_mod_conun || $is_con_mod_loading} {
set con_msg [getPresentConflictErrorMsg $mod_name_vr $mod_con_list\
$is_con_mod_loading]
} else {
set con_msg {}
}
set msgrecid [currentState msgrecordid]
if {![isConflictErrorAlreadyReported $msgrecid $mod_con_list]} {
knerrorOrWarningIfForced $con_msg
}
}
}
# Smaller subset than main module load... This function runs modulerc and
# .version files
proc execute-modulerc {modfile modname modspec} {
lappendState modulefile $modfile
# push name to be found by module-alias and version
lappendState modulename $modname
lappendState specifiedname $modspec
set ::ModulesVersion {}
lappendState debug_msg_prefix "\[#[depthState modulename]:$modname\] "
if {![info exists ::g_modrcUntrackVars]} {
# list variable that should not be tracked for saving
array set ::g_modrcUntrackVars [list ModulesCurrentModulefile 1\
ModulesVersion 1 env 1]
# commands that should be renamed before aliases setup
array set ::g_modrcRenameCmds [list]
# list interpreter alias commands to define
array set ::g_modrcAliases [list uname uname system system versioncmp\
versioncmp hide-modulefile hide-modulefile hide-version\
hide-modulefile is-loaded is-loaded is-used is-used module-version\
module-version module-alias module-alias module-virtual\
module-virtual module-forbid module-forbid module-hide module-hide\
module-tag module-tag module-info module-info modulepath-label\
modulepath-label setModulesVersion setModulesVersion\
getModuleContent getModuleContent lsb-release lsb-release\
module-warn module-warn]
if {[getConf source_cache]} {
set ::g_modrcAliases(source) sourceModfileCmd
}
# add site-specific command aliases for modulerc interp
if {[info exists ::modulerc_extra_cmds]} {
if {[catch {array set ::g_modrcAliases $::modulerc_extra_cmds}\
errorMsg]} {
knerror "Invalid value '$::modulerc_extra_cmds' ($errorMsg)\nfor\
siteconfig variable 'modulerc_extra_cmds'"
}
}
# alias commands where an argument should be passed
array set ::g_modrcAliasesPassArg [list source [list __itrp__]]
# trace commands that should be associated to aliases
array set ::g_modrcAliasesTraces [list]
# variable to define in modulerc interp
set ::g_modrcBaseVars [list ModuleTool Modules ModuleToolVersion\
[getState modules_release]]
if {[info exists ::modulerc_extra_vars]} {
if {([llength $::modulerc_extra_vars] % 2) != 0} {
knerror "Invalid value '$::modulerc_extra_vars' (list must have\
an even number of elements)\nfor siteconfig variable\
'modulerc_extra_vars'"
}
foreach {var val} $::modulerc_extra_vars {
if {[string first { } $var] != -1} {
knerror "Invalid variable name '$var'\ndefined in siteconfig\
variable 'modulerc_extra_vars'"
}
}
lappend ::g_modrcBaseVars {*}$::modulerc_extra_vars
}
}
# dedicate an interpreter per level of interpretation to have in case of
# cascaded interpretations a specific interpreter per level
set itrp __modrc_[depthState modulename]
reportTrace '$modfile' {Evaluate modulerc}
# create modulerc interpreter at first interpretation
if {![interp exists $itrp]} {
reportDebug "creating interp $itrp"
interp create $itrp
# initialize global static variables for modulerc interp
foreach {var val} $::g_modrcBaseVars {
interp eval $itrp set ::$var "{$val}"
}
# dump initial interpreter state to restore it before each modulerc
# interpretation. use same dump state for all levels
if {![info exists ::g_modrcVars]} {
dumpInterpState $itrp g_modrcVars g_modrcArrayVars\
g_modrcUntrackVars g_modrcProcs
}
# interp has just been created
set fresh 1
} else {
set fresh 0
}
# reset interp state command before each interpretation
resetInterpState $itrp $fresh g_modrcVars g_modrcArrayVars\
g_modrcUntrackVars g_modrcProcs g_modrcAliases g_modrcAliasesPassArg\
g_modrcAliasesTraces g_modrcRenameCmds g_modrcCommands
set failed_eval [catch {evaluateModulerc $itrp $modfile} errorMsg]
renderModulercEvalError $itrp $modfile $failed_eval $errorMsg
# default version set via ModulesVersion variable in .version file
# override previously defined default version for modname
lassign [getModuleNameVersion] mod modname modversion
if {$modversion eq {.version} && $::ModulesVersion ne {}} {
# ModulesVersion should target an element in current directory
if {[string first / $::ModulesVersion] == -1} {
setModuleResolution $modname/default $modname/$::ModulesVersion\
default
} else {
reportError "Invalid ModulesVersion '$::ModulesVersion' defined"
}
}
lpopState debug_msg_prefix
lpopState specifiedname
lpopState modulename
lpopState modulefile
return $::ModulesVersion
}
proc evaluateModulerc {itrp mod_file} {
interp eval $itrp set ::ModulesCurrentModulefile "{$mod_file}"
interp eval $itrp {set ::ModulesVersion {}}
# create an alias ModuleVersion on ModulesVersion
interp eval $itrp {upvar 0 ::ModulesVersion ::ModuleVersion}
##nagelfar ignore +4 Suspicious # char
interp eval $itrp {
info script $::ModulesCurrentModulefile
eval [getModuleContent $::ModulesCurrentModulefile]
# pass ModulesVersion value to main interp
if {[info exists ::ModulesVersion]} {
setModulesVersion $::ModulesVersion
}
}
}
proc renderModulercEvalError {itrp mod_file failed_eval error_msg} {
if {!$failed_eval} {
return 0
}
# no error if rc file cannot be read
switch -- [getInterpVar $itrp errorCode] {
MODULES_ERR_READ {}
MODULES_ERR_VALIDITY {reportInternalBug $error_msg $mod_file}
default {reportInternalBug [formatInterpErrStackTrace $itrp $mod_file]}
}
return 1
}
proc isInterpVarDefined {itrp var_name} {
return [interp eval $itrp info exists $var_name]
}
proc getInterpVar {itrp var_name {val_if_unset {}}} {
if {[isInterpVarDefined $itrp $var_name]} {
return [interp eval $itrp set $var_name]
} else {
return $val_if_unset
}
}
# format error stack trace to report modulefile information only
proc formatInterpErrStackTrace {itrp modfile} {
return [formatErrStackTrace [getInterpVar $itrp ::errorInfo] $modfile\
[concat [interp eval $itrp info procs] [interp eval $itrp info\
commands]]]
}
# Save list of the defined procedure and the global variables with their
# associated values set in sub interpreter passed as argument. Global
# structures are used to save these information and the name of these
# structures are provided as argument.
proc dumpInterpState {itrp dumpVarsVN dumpArrayVarsVN untrackVarsVN\
dumpProcsVN} {
upvar #0 $dumpVarsVN dumpVars
upvar #0 $dumpArrayVarsVN dumpArrayVars
upvar #0 $untrackVarsVN untrackVars
upvar #0 $dumpProcsVN dumpProcs
regexp {^__[a-z]+} $itrp itrpkind
# save name and value for any other global variables
foreach var [$itrp eval {info globals}] {
if {![info exists untrackVars($var)]} {
reportDebug "saving for $itrpkind var $var"
if {[$itrp eval array exists ::$var]} {
set dumpVars($var) [$itrp eval array get ::$var]
set dumpArrayVars($var) 1
} else {
set dumpVars($var) [$itrp eval set ::$var]
}
}
}
# save name of every defined procedures
foreach var [$itrp eval {info procs}] {
set dumpProcs($var) 1
}
reportDebug "saving for $itrpkind proc list [array names dumpProcs]"
}
# Define commands to be known by sub interpreter.
proc initInterpCommands {itrp fresh aliasesVN aliasesPassArgVN tracesVN\
renameCmdsVN} {
upvar #0 $aliasesVN aliases
upvar #0 $aliasesPassArgVN aliasesPassArg
upvar #0 $tracesVN traces
upvar #0 $renameCmdsVN renameCmds
# rename some commands on freshly created interp before aliases defined
# below overwrite them
if {$fresh} {
foreach cmd [array names renameCmds] {
$itrp eval rename $cmd $renameCmds($cmd)
}
}
# set interpreter alias commands each time to guaranty them being
# defined and not overridden by modulefile or modulerc content
foreach alias [array names aliases] {
if {[info exists aliasesPassArg($alias)]} {
set aliasargs $aliasesPassArg($alias)
# pass current itrp reference on special keyword
if {[lindex $aliasargs 0] eq {__itrp__}} {
lset aliasargs 0 $itrp
}
interp alias $itrp $alias {} $aliases($alias) {*}$aliasargs
} else {
interp alias $itrp $alias {} $aliases($alias)
}
}
if {$fresh} {
# trace each modulefile command call if verbosity is set to debug (when
# higher verbosity level is set all cmds are already traced) and timer
# mode is disabled
if {[getConf verbosity] eq {debug} && ![getState timer]} {
interp alias $itrp reportTraceExecEnter {} reportTraceExecEnter
foreach alias [array names aliases] {
# exclude internal commands expoxed to modulerc/file interpreter
# exclude cachefile commands
if {$alias ni {report reportDebug reportError reportWarning\
reportCmdTrace incrErrorCount reportInternalBug\
formatErrStackTrace isVerbosityLevel modulefile-content\
modulerc-content modulefile-invalid limited-access-file\
limited-access-directory}} {
interp eval $itrp [list trace add execution $alias enter\
reportTraceExecEnter]
}
}
}
}
foreach alias [array names traces] {
interp eval $itrp [list trace add execution $alias leave\
$traces($alias)]
}
}
# Restore initial setup of sub interpreter passed as argument based on
# global structure previously filled with initial list of defined procedure
# and values of global variable.
proc resetInterpState {itrp fresh dumpVarsVN dumpArrayVarsVN untrackVarsVN\
dumpProcsVN aliasesVN aliasesPassArgVN tracesVN renameCmdsVN\
dumpCommandsVN} {
upvar #0 $dumpVarsVN dumpVars
upvar #0 $dumpArrayVarsVN dumpArrayVars
upvar #0 $untrackVarsVN untrackVars
upvar #0 $dumpProcsVN dumpProcs
upvar #0 $dumpCommandsVN dumpCommands
# look at list of defined procedures and delete those not part of the
# initial state list. do not check if they have been altered as no vital
# procedures lied there. note that if a Tcl command has been overridden
# by a proc, it will be removed here and command will also disappear
foreach var [$itrp eval {info procs}] {
if {![info exists dumpProcs($var)]} {
reportDebug "removing on $itrp proc $var"
$itrp eval [list rename $var {}]
}
}
##nagelfar vartype aliasesVN varName
##nagelfar vartype aliasesPassArgVN varName
##nagelfar vartype tracesVN varName
##nagelfar vartype renameCmdsVN varName
# rename some commands and set aliases on interpreter
initInterpCommands $itrp $fresh $aliasesVN $aliasesPassArgVN $tracesVN\
$renameCmdsVN
# dump interpreter command list here on first time as aliases should be
# set prior to be found on this list for correct match
if {![info exists dumpCommands]} {
set dumpCommands [$itrp eval {info commands}]
reportDebug "saving for $itrp command list $dumpCommands"
# if current interpreter command list does not match initial list it
# means that at least one command has been altered so we need to recreate
# interpreter to guaranty proper functioning
} elseif {$dumpCommands ne [$itrp eval {info commands}]} {
reportDebug "missing command(s), recreating interp $itrp"
interp delete $itrp
interp create $itrp
initInterpCommands $itrp 1 $aliasesVN $aliasesPassArgVN $tracesVN\
$renameCmdsVN
}
# check every global variables currently set and correct them to restore
# initial interpreter state. work on variables at the very end to ensure
# procedures and commands are correctly defined
foreach var [$itrp eval {info globals}] {
if {![info exists untrackVars($var)]} {
if {![info exists dumpVars($var)]} {
reportDebug "removing on $itrp var $var"
$itrp eval unset -nocomplain ::$var
} elseif {![info exists dumpArrayVars($var)]} {
if {$dumpVars($var) ne [$itrp eval set ::$var]} {
reportDebug "restoring on $itrp var $var"
if {[string is list $dumpVars($var)] && [llength\
$dumpVars($var)] > 1} {
# restore value as list
$itrp eval set ::$var [list $dumpVars($var)]
} else {
# brace value to be able to restore empty string
$itrp eval set ::$var "{$dumpVars($var)}"
}
}
} else {
if {$dumpVars($var) ne [$itrp eval array get ::$var]} {
reportDebug "restoring on $itrp var $var"
$itrp eval array set ::$var [list $dumpVars($var)]
}
}
}
}
}
# ;;; Local Variables: ***
# ;;; mode:tcl ***
# ;;; End: ***
# vim:set tabstop=3 shiftwidth=3 expandtab autoindent:
|