Loading...
No commits yet
Not committed History
Blame
command_switching.src • 17.0 KB
#!/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