#!/bin/bash
# -*- coding: utf-8 -*-
# File: scripts/shell/modules/command_switching.src
THIS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ---------------------------------------
# 1. Native Commands - If available do not build containers at all
# 2. Singularity
# 3. Docker
# Shared module for resolving LaTeX-related commands
# Provides functions to get commands for pdflatex, bibtex, latexdiff, etc.
# with automatic fallback to module loading or container usage
source ./config/load_config.sh $SCITEX_WRITER_DOC_TYPE
# Cache frequently-used checks to avoid redundant calls
_CACHED_CONTAINER_RUNTIME=""
_CACHE_CHECKED=false
_CACHED_MODULE_AVAILABLE=""
_MODULE_CACHE_CHECKED=false
# Setup container path (Singularity/Apptainer)
setup_latex_container() {
# Check if container path is already set
if [ -n "$SCITEX_WRITER_TEXLIVE_APPTAINER_SIF" ] && [ -f "$SCITEX_WRITER_TEXLIVE_APPTAINER_SIF" ]; then
return 0
fi
# Use config value if set, otherwise use project directory default
if [ -z "$SCITEX_WRITER_TEXLIVE_APPTAINER_SIF" ]; then
# Use project directory to ensure availability across HPC nodes
local project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../" && pwd)"
SCITEX_WRITER_TEXLIVE_APPTAINER_SIF="${project_root}/.cache/containers/texlive_container.sif"
fi
if [ ! -f "$SCITEX_WRITER_TEXLIVE_APPTAINER_SIF" ]; then
echo_info " Downloading TeXLive container (one-time setup)..."
mkdir -p "$(dirname $SCITEX_WRITER_TEXLIVE_APPTAINER_SIF)"
# Try Apptainer first, then Singularity
if command -v apptainer &>/dev/null; then
apptainer pull "$SCITEX_WRITER_TEXLIVE_APPTAINER_SIF" docker://texlive/texlive:latest >/dev/null 2>&1
elif command -v singularity &>/dev/null; then
singularity pull "$SCITEX_WRITER_TEXLIVE_APPTAINER_SIF" docker://texlive/texlive:latest >/dev/null 2>&1
else
return 1
fi
if [ -f "$SCITEX_WRITER_TEXLIVE_APPTAINER_SIF" ]; then
echo_success " Container downloaded to $SCITEX_WRITER_TEXLIVE_APPTAINER_SIF"
export SCITEX_WRITER_TEXLIVE_APPTAINER_SIF="$SCITEX_WRITER_TEXLIVE_APPTAINER_SIF"
else
return 1
fi
fi
return 0
}
# Get container runtime command if available (cached)
get_container_runtime() {
# Use cache if available
if [ "$_CACHE_CHECKED" = true ]; then
echo "$_CACHED_CONTAINER_RUNTIME"
return
fi
# Check once and cache
if command -v apptainer &>/dev/null; then
_CACHED_CONTAINER_RUNTIME="apptainer"
elif command -v singularity &>/dev/null; then
_CACHED_CONTAINER_RUNTIME="singularity"
else
_CACHED_CONTAINER_RUNTIME=""
fi
_CACHE_CHECKED=true
echo "$_CACHED_CONTAINER_RUNTIME"
}
# Load texlive module if available (cached)
load_texlive_module() {
# Use cache if available
if [ "$_MODULE_CACHE_CHECKED" = true ]; then
if [ "$_CACHED_MODULE_AVAILABLE" = "true" ]; then
module load texlive &>/dev/null
return 0
else
return 1
fi
fi
# Check once and cache
if command -v module &>/dev/null && module avail texlive &>/dev/null 2>&1; then
_CACHED_MODULE_AVAILABLE="true"
_MODULE_CACHE_CHECKED=true
module load texlive &>/dev/null
return 0
else
_CACHED_MODULE_AVAILABLE="false"
_MODULE_CACHE_CHECKED=true
return 1
fi
}
# Get pdflatex command with fallbacks
# Priority: 1. Native → 2. Singularity → 3. Docker
get_cmd_pdflatex() {
local working_dir="${1:-$(pwd)}"
local abs_dir=$(realpath "$working_dir")
# First choice: Native pdflatex (fastest)
if command -v pdflatex &>/dev/null; then
if pdflatex --version &>/dev/null 2>&1; then
echo "pdflatex"
return 0
fi
fi
# Second choice: Module system (HPC)
if load_texlive_module && command -v pdflatex &>/dev/null; then
echo "pdflatex"
return 0
fi
# Third choice: Singularity/Apptainer (reproducible)
local runtime=$(get_container_runtime)
if [ -n "$runtime" ] && setup_latex_container; then
echo "$runtime exec --bind ${abs_dir}:${abs_dir} --pwd ${abs_dir} $SCITEX_WRITER_TEXLIVE_APPTAINER_SIF pdflatex"
return 0
fi
return 1
}
# Get bibtex command with fallbacks
# Priority: 1. Native → 2. Module → 3. Singularity
get_cmd_bibtex() {
local working_dir="${1:-$(pwd)}"
local abs_dir=$(realpath "$working_dir")
# First choice: Native bibtex (fastest)
if command -v bibtex &>/dev/null; then
if bibtex --version &>/dev/null 2>&1; then
echo "bibtex"
return 0
fi
fi
# Second choice: Module system (HPC)
if load_texlive_module && command -v bibtex &>/dev/null; then
echo "bibtex"
return 0
fi
# Third choice: Singularity/Apptainer (reproducible)
local runtime=$(get_container_runtime)
if [ -n "$runtime" ] && setup_latex_container; then
echo "$runtime exec --bind ${abs_dir}:${abs_dir} --pwd ${abs_dir} $SCITEX_WRITER_TEXLIVE_APPTAINER_SIF bibtex"
return 0
fi
return 1
}
# Get latexdiff command with fallbacks
# Priority: 1. Native → 2. Module → 3. Singularity
get_cmd_latexdiff() {
local working_dir="${1:-$(pwd)}"
local abs_dir=$(realpath "$working_dir")
# First choice: Native latexdiff (fastest)
if [ -x "/usr/bin/latexdiff" ] || [ -x "/usr/local/bin/latexdiff" ]; then
echo "latexdiff"
return 0
fi
if command -v latexdiff &>/dev/null; then
echo "latexdiff"
return 0
fi
# Second choice: Module system (HPC)
if load_texlive_module && command -v latexdiff &>/dev/null; then
echo "latexdiff"
return 0
fi
# Third choice: Singularity/Apptainer (reproducible)
local runtime=$(get_container_runtime)
if [ -n "$runtime" ] && setup_latex_container; then
echo "$runtime exec --bind ${abs_dir}:${abs_dir} --pwd ${abs_dir} $SCITEX_WRITER_TEXLIVE_APPTAINER_SIF latexdiff"
return 0
fi
return 1
}
# Get texcount command with fallbacks
# Priority: 1. Native → 2. Module → 3. Singularity
get_cmd_texcount() {
local working_dir="${1:-$(pwd)}"
local abs_dir=$(realpath "$working_dir")
# First choice: Native texcount (fastest)
if command -v texcount &>/dev/null && texcount --version &>/dev/null 2>&1; then
echo "texcount"
return 0
fi
# Second choice: Module system (HPC)
if load_texlive_module && command -v texcount &>/dev/null; then
echo "texcount"
return 0
fi
# Third choice: Singularity/Apptainer (reproducible)
local runtime=$(get_container_runtime)
if [ -n "$runtime" ] && setup_latex_container; then
echo "$runtime exec --bind ${abs_dir}:${abs_dir} --pwd ${abs_dir} $SCITEX_WRITER_TEXLIVE_APPTAINER_SIF texcount"
return 0
fi
return 1
}
# Get latexmk command with fallbacks
# Priority: 1. Native → 2. Module → 3. Singularity
get_cmd_latexmk() {
local working_dir="${1:-$(pwd)}"
local abs_dir=$(realpath "$working_dir")
# First choice: Native latexmk (fastest)
if command -v latexmk &>/dev/null && latexmk -version &>/dev/null 2>&1; then
echo "latexmk"
return 0
fi
# Second choice: Module system (HPC)
if load_texlive_module && command -v latexmk &>/dev/null; then
echo "latexmk"
return 0
fi
# Third choice: Singularity/Apptainer (reproducible)
local runtime=$(get_container_runtime)
if [ -n "$runtime" ] && setup_latex_container; then
echo "$runtime exec --bind ${abs_dir}:${abs_dir} --pwd ${abs_dir} $SCITEX_WRITER_TEXLIVE_APPTAINER_SIF latexmk"
return 0
fi
return 1
}
# Get tectonic command with fallbacks
# Priority: 1. Native → 2. Container (Tectonic has its own Docker image)
get_cmd_tectonic() {
local working_dir="${1:-$(pwd)}"
local abs_dir=$(realpath "$working_dir")
# First choice: Native tectonic (fastest)
if command -v tectonic &>/dev/null && tectonic --version &>/dev/null 2>&1; then
echo "tectonic"
return 0
fi
# Second choice: Container (Apptainer/Singularity with official Tectonic image)
local runtime=$(get_container_runtime)
if [ -n "$runtime" ] && setup_tectonic_container; then
echo "$runtime exec --bind ${abs_dir}:${abs_dir} --pwd ${abs_dir} $SCITEX_WRITER_TECTONIC_APPTAINER_SIF tectonic"
return 0
fi
# Note: No module system support for Tectonic (not typically available in HPC)
return 1
}
# Setup Tectonic container (for tectonic)
setup_tectonic_container() {
# Check if container path is already set
if [ -n "$SCITEX_WRITER_TECTONIC_APPTAINER_SIF" ] && [ -f "$SCITEX_WRITER_TECTONIC_APPTAINER_SIF" ]; then
return 0
fi
# Use config value if set, otherwise use project directory default
if [ -z "$SCITEX_WRITER_TECTONIC_APPTAINER_SIF" ]; then
# Use project directory to ensure availability across HPC nodes
local project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../" && pwd)"
SCITEX_WRITER_TECTONIC_APPTAINER_SIF="${project_root}/.cache/containers/tectonic_container.sif"
fi
if [ ! -f "$SCITEX_WRITER_TECTONIC_APPTAINER_SIF" ]; then
echo_info " Downloading Tectonic container (one-time setup)..."
mkdir -p "$(dirname $SCITEX_WRITER_TECTONIC_APPTAINER_SIF)"
# Try Apptainer first, then Singularity
# Using official Tectonic Docker image
if command -v apptainer &>/dev/null; then
apptainer pull "$SCITEX_WRITER_TECTONIC_APPTAINER_SIF" docker://tectonic/tectonic:latest >/dev/null 2>&1
elif command -v singularity &>/dev/null; then
singularity pull "$SCITEX_WRITER_TECTONIC_APPTAINER_SIF" docker://tectonic/tectonic:latest >/dev/null 2>&1
else
return 1
fi
if [ -f "$SCITEX_WRITER_TECTONIC_APPTAINER_SIF" ]; then
echo_success " Container downloaded to $SCITEX_WRITER_TECTONIC_APPTAINER_SIF"
export SCITEX_WRITER_TECTONIC_APPTAINER_SIF="$SCITEX_WRITER_TECTONIC_APPTAINER_SIF"
else
return 1
fi
fi
return 0
}
# Setup Mermaid container (for mmdc)
setup_mermaid_container() {
# Check if container path is already set
if [ -n "$SCITEX_WRITER_MERMAID_APPTAINER_SIF" ] && [ -f "$SCITEX_WRITER_MERMAID_APPTAINER_SIF" ]; then
return 0
fi
# Use config value if set, otherwise use project directory default
if [ -z "$SCITEX_WRITER_MERMAID_APPTAINER_SIF" ]; then
# Use project directory to ensure availability across HPC nodes
local project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../" && pwd)"
SCITEX_WRITER_MERMAID_APPTAINER_SIF="${project_root}/.cache/containers/mermaid_container.sif"
fi
if [ ! -f "$SCITEX_WRITER_MERMAID_APPTAINER_SIF" ]; then
echo_info " Downloading Mermaid container (one-time setup)..."
mkdir -p "$(dirname $SCITEX_WRITER_MERMAID_APPTAINER_SIF)"
# Try Apptainer first, then Singularity
if command -v apptainer &>/dev/null; then
apptainer pull "$SCITEX_WRITER_MERMAID_APPTAINER_SIF" docker://minlag/mermaid-cli:latest >/dev/null 2>&1
elif command -v singularity &>/dev/null; then
singularity pull "$SCITEX_WRITER_MERMAID_APPTAINER_SIF" docker://minlag/mermaid-cli:latest >/dev/null 2>&1
else
return 1
fi
if [ -f "$SCITEX_WRITER_MERMAID_APPTAINER_SIF" ]; then
echo_success " Container downloaded to $SCITEX_WRITER_MERMAID_APPTAINER_SIF"
export SCITEX_WRITER_MERMAID_APPTAINER_SIF="$SCITEX_WRITER_MERMAID_APPTAINER_SIF"
else
return 1
fi
fi
return 0
}
# Get mmdc command with fallbacks
get_cmd_mmdc() {
local working_dir="${1:-$(pwd)}"
local abs_dir=$(realpath "$working_dir")
# First choice: Native mmdc (fastest and most compatible)
if command -v mmdc &>/dev/null; then
# Test if mmdc works (don't require puppeteer check, it's too slow)
if mmdc --version &>/dev/null 2>&1; then
echo "mmdc"
return 0
fi
fi
# Second choice: Container (Apptainer/Singularity)
local runtime=$(get_container_runtime)
if [ -n "$runtime" ] && setup_mermaid_container; then
# Use puppeteer config if it exists, otherwise run without it
local project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../" && pwd)"
local puppeteer_config="${project_root}/.cache/puppeteer-config.json"
if [ -f "$puppeteer_config" ]; then
echo "$runtime exec --bind ${abs_dir}:${abs_dir} --bind ${puppeteer_config}:${puppeteer_config}:ro --pwd ${abs_dir} $SCITEX_WRITER_MERMAID_APPTAINER_SIF /home/mermaidcli/node_modules/.bin/mmdc -p ${puppeteer_config}"
else
echo "$runtime exec --bind ${abs_dir}:${abs_dir} --pwd ${abs_dir} $SCITEX_WRITER_MERMAID_APPTAINER_SIF /home/mermaidcli/node_modules/.bin/mmdc"
fi
return 0
fi
# Third choice: Suggest installation
if command -v npm &>/dev/null; then
echo_info " mmdc not found, consider: npm install -g @mermaid-js/mermaid-cli"
fi
return 1
}
# Check if LaTeX commands are available
check_latex_commands_available() {
local pdflatex_cmd=$(get_cmd_pdflatex)
local bibtex_cmd=$(get_cmd_bibtex)
if [ -z "$pdflatex_cmd" ] || [ -z "$bibtex_cmd" ]; then
return 1
fi
return 0
}
# Setup ImageMagick container
setup_imagemagick_container() {
# Check if container path is already set
if [ -n "$SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF" ] && [ -f "$SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF" ]; then
return 0
fi
# Use config value if set, otherwise use project directory default
if [ -z "$SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF" ]; then
# Use project directory to ensure availability across HPC nodes
local project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../" && pwd)"
SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF="${project_root}/.cache/containers/imagemagick_container.sif"
fi
if [ ! -f "$SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF" ]; then
echo_info " Downloading ImageMagick container (one-time setup)..."
mkdir -p "$(dirname $SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF)"
# Try Apptainer first, then Singularity
if command -v apptainer &>/dev/null; then
apptainer pull "$SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF" docker://dpokidov/imagemagick:latest >/dev/null 2>&1
elif command -v singularity &>/dev/null; then
singularity pull "$SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF" docker://dpokidov/imagemagick:latest >/dev/null 2>&1
else
return 1
fi
if [ -f "$SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF" ]; then
echo_success " Container downloaded to $SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF"
export SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF="$SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF"
else
return 1
fi
fi
return 0
}
# Get convert command with fallbacks
# Priority: 1. Native → 2. Singularity → 3. Module
get_cmd_convert() {
local working_dir="${1:-$(pwd)}"
local abs_dir=$(realpath "$working_dir")
# First choice: Native magick (ImageMagick v7+) or convert (v6)
if command -v magick &>/dev/null; then
echo "magick" # In v7, just use 'magick' not 'magick convert'
return 0
elif command -v convert &>/dev/null; then
echo "convert"
return 0
fi
# Second choice: Container (Apptainer/Singularity)
local runtime=$(get_container_runtime)
if [ -n "$runtime" ] && setup_imagemagick_container; then
# Container likely has v7, so use magick to avoid deprecation warning
echo "$runtime exec --bind ${abs_dir}:${abs_dir} --pwd ${abs_dir} $SCITEX_WRITER_IMAGEMAGICK_APPTAINER_SIF magick 2>/dev/null"
return 0
fi
# Third choice: Module system (rare for ImageMagick)
if command -v module &>/dev/null && module avail imagemagick &>/dev/null 2>&1; then
module load imagemagick &>/dev/null
if command -v magick &>/dev/null; then
echo "magick"
return 0
elif command -v convert &>/dev/null; then
echo "convert"
return 0
fi
fi
return 1
}
# Export functions and cache variables for use in other scripts
export -f setup_latex_container
export -f setup_tectonic_container
export -f setup_mermaid_container
export -f setup_imagemagick_container
export -f get_container_runtime
export -f load_texlive_module
export -f get_cmd_pdflatex
export -f get_cmd_bibtex
export -f get_cmd_latexdiff
export -f get_cmd_latexmk
export -f get_cmd_tectonic
export -f get_cmd_texcount
export -f get_cmd_mmdc
export -f get_cmd_convert
export -f check_latex_commands_available
# Export cache variables so subprocesses can use cached values
export _CACHED_CONTAINER_RUNTIME
export _CACHE_CHECKED
export _CACHED_MODULE_AVAILABLE
export _MODULE_CACHE_CHECKED
# EOF