\documentclass{beamer}

% You can also use a 16:9 aspect ratio:
%\documentclass[aspectratio=169]{beamer}
\usetheme{TACC16}

% It's possible to move the footer to the right:
%\usetheme[rightfooter]{TACC16}

% page 
%\begin{frame}{}
%  \begin{itemize}
%    \item
%  \end{itemize}
%\end{frame}
%
%% page 
%\begin{frame}[fragile]
%    \frametitle{}
% {\tiny
%    \begin{semiverbatim}
%    \end{semiverbatim}
%}
%  \begin{itemize}
%    \item
%  \end{itemize}
%
%\end{frame}

\begin{document}
\title[Lmod]{How TCL break, puts \& help messages are handled by Lmod}
\author{Robert McLay} 
\date{January 10, 2023}

% page 1
\frame{\titlepage} 


% page 2
\begin{frame}{Outline}
  \center{\includegraphics[width=.9\textwidth]{Lmod-4color@2x.png}}
  \begin{itemize}
    \item Review of how TCL modulefiles are evaluated
    \item How .version and .modulerc file are evaluated
    \item Support for bare TCL break (LmodBreak())
    \item Support for TCL's puts
    \item Capturing help message from TCL modulefiles 
  \end{itemize}
\end{frame}

% page 3
\begin{frame}{How Lmod handles TCL modulefiles}
  \begin{itemize}
    \item Use tcl2lua.tcl to read the modulefile.
    \item It evaluates all pure TCL code
    \item It outputs Lua strings for all module commands (setenv, etc)
    \item Lmod evalutes Lua output from tcl2lua.tcl
    \item Means that all TCL if stmts are evaluated by tcl2lua.tcl
  \end{itemize}
\end{frame}

% page 4
\begin{frame}{How Lmod handles TCL modulefiles (II)}
  \begin{itemize}
    \item Remember that tcl2lua.tcl is a separate code written in TCL
    \item It doesn't have access to the internal Lmod structures
    \item There is only a command-line interface between the two programs.
  \end{itemize}
\end{frame}

% page 5
\begin{frame}[fragile]
    \frametitle{When things go awry}
  \begin{itemize}
    \item Suppose you have TCL modules \textbf{Centos} and \textbf{B}
  \end{itemize}
 {\tiny
    \begin{semiverbatim}
Centos::

    #%Module
    setenv SYSTEM\_NAME Centos

And B::

    #%Module
    module load Centos

    if \{ \$env(SYSTEM\_NAME) == "Centos" \} \{
       # do something
    \}

    \end{semiverbatim}
}
\end{frame}

% page 6
\begin{frame}[fragile]
    \frametitle{Converting the TCL \textbf{B} into Lua}
 {\tiny
    \begin{semiverbatim}
   load("Centos")
   LmodError("can't read \"env(SYSTEM\_NAME)\": no such variable")
    \end{semiverbatim}
}
  \begin{itemize}
    \item Trouble: the TCL \textbf{load} command $\Rightarrow$
      \texttt{load("Centos")}
    \item Cannot get the TCL load command to be evaluated before the
      TCL if block
  \end{itemize}

\end{frame}

% page 7
\begin{frame}{How .version \& .modulerc are eval'ed}
  \begin{itemize}
    \item Lmod uses the RC2lua.tcl script to convert to Lua
    \item It only knows \texttt{module-version},
      \texttt{module-alias}, ...
    \item It doesn't know about \texttt{setenv}
    \item I don't know what setenv means here
  \end{itemize}
\end{frame}

% page 8
\begin{frame}[fragile]
    \frametitle{How Lmod implements TCL break}
 {\tiny
    \begin{semiverbatim}
    set a 10
    while \{\$a < 20 \} \{
       puts "value of a: \$a"
       incr acl2
       if \{ \$a > 15\} \{
          break
       \}
    \}
    \end{semiverbatim}
}
  \begin{itemize}
    \item Normal use: exit from loop.
    \item A bare TCL break is normally an error
    \item Lmod (and Tmod) stops evaluating current module.
    \item Lmod keeps all previous module evaluations intact
    \item Lmod continues evaluating after break
    \item Not sure what Tmod3 and Tmod do w.r.t. break
  \end{itemize}
\end{frame}


% page 9
\begin{frame}{Examples}
  \begin{itemize}
    \item \texttt{module load A B brkModule D}
    \item modules A and B are still loaded 
    \item brkModule is essentially ignored
    \item D is loaded.
    \item \texttt{module load A B errModule D}
    \item Lmod internally loads A \& B
    \item Loading errModule fails
    \item No new modules loaded.
  \end{itemize}
\end{frame}

% page 10
\begin{frame}{How Lmod supports break}
  \begin{itemize}
    \item Special code in tcl2lua.tcl to handle a bare break
    \item Lmod has to recover from a rejected modulefile
  \end{itemize}
\end{frame}



% page 11
\begin{frame}[fragile]
    \frametitle{How tcl2lua.tcl handles bare break}
 {\tiny
    \begin{semiverbatim}
 set errorVal [interp eval \$child \{
     set returnVal 0
     ...
     set sourceFailed [catch \{source \$ModulesCurrentModulefile \} errorMsg]
     if \{ \$g\_help ...\} \{
       ...
     \}
     if \{\$sourceFailed\} \{
        if \{ \$sourceFailed == 3 || \$errorMsg == {invoked "break" outside of a loop}\} \{
           set returnVal 1
           myBreak             # output "LmodBreak into Global 
           showResults         # Write output
           return \$returnVal   # return with error status
        \}
        reportError \$errorMsg   # output error message
        set returnVal 1         # return with error status
     \}
     showResults                 # Write output for normal translation
     return \$returnVal           # return with OK status
 \}]
    \end{semiverbatim}
}
  \begin{itemize}
    \item A bare break is an error in TCL
    \item tcl2lua.tcl captures that
    \item generates ''LmodBreak()''
  \end{itemize}

\end{frame}

% page 12
\begin{frame}{How Lmod handles LmodBreak()}
  \begin{itemize}
    \item Lmod maintains a stack of module ``states''
    \item It is called ``FrameStk``
    \item It contains:
      \begin{enumerate}
        \item VarT: new env vars values
        \item ModuleTable: The currently loaded modules
        \item mname:  Current module object to be loaded.
      \end{enumerate}
    \item Support for FrameStk was added with Lmod 7 rewrite
    \item Correct support for Break was added in 8.7+
  \end{itemize}
\end{frame}

% page 13
\begin{frame}{FrameStk action during module loads}
  \begin{itemize}
    \item Each module load creates a new FrameStk entry
    \item Currently loaded module succeeds $\Rightarrow$ overwrites
      previous entry
    \item Break causes the current entry to be thrown away
  \end{itemize}
\end{frame}

% page 14
\begin{frame}[fragile]
    \frametitle{Another Break example}
 {\tiny
    \begin{semiverbatim}
\$ cat StdEnv.lua
load("A")        
load("B")        
load("BRK")        
load("D")

\$ ml StdEnv; ml
Currently loaded modules:
  1) A   2) B  3) D
    \end{semiverbatim}
}
  \begin{itemize}
    \item The contents of the \texttt{BRK} module are ignored
  \end{itemize}

\end{frame}

% page 15
\begin{frame}{Handling TCL puts}
  \begin{itemize}
    \item TCL \texttt{puts} $\Rightarrow$ calls \texttt{myPuts} thru child interpreter
    \item puts and myPuts takes upto 3 arguments
    \item It took years to get this correct
    \item \texttt{myPuts} write to a global array in tcl2lua.tcl
    \item the \texttt{showResults} sends it to stdout for lua to
      evaluate
    \item Message sent to stderr use LmodMsgRaw() function
  \end{itemize}
\end{frame}

% page 16
\begin{frame}{\texttt{myPuts} arguments}
  \begin{itemize}
     \item puts can only have 1 to 3 arguments
     \item puts <-nonewline> <channel> msg
     \item puts msg           $\Rightarrow$ writes to stdout (at end)
     \item puts stdout msg    $\Rightarrow$ writes to stdout (at end)
     \item puts stderr msg    $\Rightarrow$ writes to stderr
     \item puts prestdout msg $\Rightarrow$ writes to stdout but at the beginning of output
  \end{itemize}
\end{frame}


% page 17
\begin{frame}[fragile]
    \frametitle{Handling TCL help messages}
 {\tiny
    \begin{semiverbatim}
proc ModulesHelp { } {
    puts stderr "The TACC Amber installation ..."
}

Lmod wants:
help ([===[The TACC Amber installation ...]===])
    \end{semiverbatim}
}
  \begin{itemize}
    \item Converting TCL help message was tricky
    \item tcl2lua.tcl has to capture the output when executing
      ModulesHelp
    \item myPuts has a special mode when running ModulesHelp
  \end{itemize}

\end{frame}

% page 18
\begin{frame}[fragile]
    \frametitle{}
 {\tiny
    \begin{semiverbatim}
  if \{ \$g\_help && [info procs "ModulesHelp"] == "ModulesHelp" \} \{
      set start "help(\textbackslash[===\textbackslash["
      set end   "\textbackslash]===\textbackslash])"
      setPutMode "inHelp"
      myPuts stdout \$start
      catch { ModulesHelp } errMsg
      myPuts stdout \$end
      setPutMode "normal"
  \}
    \end{semiverbatim}
}
  \begin{itemize}
    \item in ``inHelp'' mode output to stderr is written to stdout
  \end{itemize}
\end{frame}


% page 19
\begin{frame}[fragile]
    \frametitle{Help Conversion Example}
 {\tiny
    \begin{semiverbatim}
help([===[
The TACC Amber installation only includes the parallel Sander/pmemd modules.
The Amber modulefile defines the following environment variables: ...

Version 9
]===])
    \end{semiverbatim}
}
  \begin{itemize}
    \item This way help message work the same with Lua and TCL modulefiles
  \end{itemize}

\end{frame}

% page 20
\begin{frame}{Conclusions}
  \begin{itemize}
    \item TCL to Lua conversion works well 
    \item But it is NOT perfect.
    \item TCL Break, puts and help message required special foo
  \end{itemize}
\end{frame}

% page 21
\begin{frame}{Next Time}
  \begin{itemize}
    \item How to use \texttt{check\_module\_tree\_syntax}
  \end{itemize}
\end{frame}

% page 22
\begin{frame}{Future Topics}
  \begin{itemize}
    \item I am on vacation (a.k.a holiday) in early February
    \item Next Meeting will be Feb 14th 
  \end{itemize}
\end{frame}

\end{document}
