#!/bin/bash # -*- coding: utf-8 -*- # Timestamp: "2025-11-11 07:18:17 (ywatanabe)" # File: /home/ywatanabe/proj/scitex-writer/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