#!/bin/bash # -*- coding: utf-8 -*- # Test file for: watch_compile.sh THIS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(realpath "$THIS_DIR/../..")" # Test counter TESTS_RUN=0 TESTS_PASSED=0 TESTS_FAILED=0 # Colors GREEN='\033[0;32m' RED='\033[0;31m' NC='\033[0m' assert_success() { local cmd="$1" local desc="${2:-$cmd}" ((TESTS_RUN++)) if eval "$cmd" > /dev/null 2>&1; then echo -e "${GREEN}✓${NC} $desc" ((TESTS_PASSED++)) else echo -e "${RED}✗${NC} $desc" ((TESTS_FAILED++)) fi } assert_file_exists() { local file="$1" ((TESTS_RUN++)) if [ -f "$file" ]; then echo -e "${GREEN}✓${NC} File exists: $file" ((TESTS_PASSED++)) else echo -e "${RED}✗${NC} File missing: $file" ((TESTS_FAILED++)) fi } # Add your tests here test_placeholder() { echo "TODO: Add tests for watch_compile.sh" } # Run tests main() { echo "Testing: watch_compile.sh" echo "========================================" test_placeholder echo "========================================" echo "Results: $TESTS_PASSED/$TESTS_RUN passed" [ $TESTS_FAILED -gt 0 ] && exit 1 exit 0 } main "$@" # -------------------------------------------------------------------------------- # -------------------------------------------------------------------------------- # -------------------------------------------------------------------------------- # Start of Source Code from: /home/ywatanabe/proj/scitex-writer/scripts/shell/watch_compile.sh # -------------------------------------------------------------------------------- # #!/bin/bash # # -*- coding: utf-8 -*- # # Timestamp: "$(date '+%Y-%m-%d %H:%M:%S') ($(whoami))" # # File: ./paper/scripts/shell/watch_compile.sh # # # Hot-recompile script with file watching and lock management # # ORIG_DIR="$(pwd)" # THIS_DIR="$(cd $(dirname ${BASH_SOURCE[0]}) && pwd)" # PROJECT_ROOT="$(cd "$THIS_DIR/../.." && pwd)" # # # Colors for output # GIT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" # # GRAY='\033[0;90m' # GREEN='\033[0;32m' # YELLOW='\033[0;33m' # RED='\033[0;31m' # NC='\033[0m' # No Color # # echo_info() { echo -e "${GRAY}INFO: $1${NC}"; } # echo_success() { echo -e "${GREEN}SUCC: $1${NC}"; } # echo_warning() { echo -e "${YELLOW}WARN: $1${NC}"; } # echo_error() { echo -e "${RED}ERRO: $1${NC}"; } # echo_header() { echo_info "=== $1 ==="; } # # --------------------------------------- # # # Lock file path # LOCK_FILE="$PROJECT_ROOT/.compile.lock" # WATCH_PID_FILE="$PROJECT_ROOT/.watch.pid" # # # Function to check if compilation is locked # is_locked() { # if [ -f "$LOCK_FILE" ]; then # local lock_pid=$(cat "$LOCK_FILE" 2>/dev/null) # # Check if the process is still running # if [ -n "$lock_pid" ] && kill -0 "$lock_pid" 2>/dev/null; then # return 0 # Locked and process is running # else # # Process is dead, remove stale lock # rm -f "$LOCK_FILE" # return 1 # fi # fi # return 1 # Not locked # } # # # Function to acquire lock # acquire_lock() { # local max_wait=60 # Maximum seconds to wait for lock # local waited=0 # # while is_locked; do # if [ $waited -eq 0 ]; then # echo_warning "Compilation in progress, waiting for lock..." # fi # sleep 1 # waited=$((waited + 1)) # if [ $waited -ge $max_wait ]; then # echo_error "Timeout waiting for compilation lock" # return 1 # fi # done # # # Create lock with our PID # echo $$ > "$LOCK_FILE" # return 0 # } # # # Function to release lock # release_lock() { # if [ -f "$LOCK_FILE" ]; then # local lock_pid=$(cat "$LOCK_FILE" 2>/dev/null) # if [ "$lock_pid" = "$$" ]; then # rm -f "$LOCK_FILE" # fi # fi # } # # # Cleanup function # cleanup() { # echo_info "Stopping watch mode..." # release_lock # rm -f "$WATCH_PID_FILE" # exit 0 # } # # # Trap signals for cleanup # trap cleanup EXIT INT TERM # # # Load configuration from YAML # CONFIG_FILE="$PROJECT_ROOT/config/config_manuscript.yaml" # # # Function to parse YAML value # get_yaml_value() { # local key="$1" # local file="${2:-$CONFIG_FILE}" # grep "^$key:" "$file" | sed 's/^[^:]*:[[:space:]]*//' | sed 's/[[:space:]]*#.*//' # } # # # Function to get YAML array values # get_yaml_array() { # local key="$1" # local file="${2:-$CONFIG_FILE}" # awk "/^$key:/{flag=1; next} /^[^ ]/{flag=0} flag && /^[[:space:]]*-/" "$file" | sed 's/^[[:space:]]*-[[:space:]]*//' # } # # # Load hot-recompile configuration # # Note: YAML key uses "enabled" not "enable" # HOT_RECOMPILE_ENABLED=$(awk '/^hot-recompile:/{flag=1} flag && /^[[:space:]]*enabled:/{print $2; exit}' "$CONFIG_FILE" | grep -o "true\|false") # COMPILE_MODE="${1:-$(get_yaml_value "hot-recompile.mode")}" # Use arg or config # COMPILE_MODE="${COMPILE_MODE:-restart}" # Default to restart if not specified # STABLE_LINK=$(get_yaml_value "hot-recompile.stable_link") # STABLE_LINK="${STABLE_LINK:-./01_manuscript/manuscript-latest.pdf}" # # # Function to compile manuscript # compile_with_lock() { # local compilation_start_file="$PROJECT_ROOT/.compile_start_time" # # # Check if compilation is running # if is_locked; then # local lock_pid=$(cat "$LOCK_FILE" 2>/dev/null) # # if [ "$COMPILE_MODE" = "restart" ]; then # # Check how long compilation has been running # if [ -f "$compilation_start_file" ]; then # local start_time=$(cat "$compilation_start_file") # local current_time=$(date +%s) # local elapsed=$((current_time - start_time)) # # if [ $elapsed -lt 3 ]; then # # Just started, kill and restart # echo_warning "$(date '+%H:%M:%S') - Stopping current compilation (just started)..." # kill -TERM "$lock_pid" 2>/dev/null # sleep 0.5 # rm -f "$LOCK_FILE" "$compilation_start_file" # elif [ $elapsed -gt 15 ]; then # # Taking too long, kill and restart # echo_warning "$(date '+%H:%M:%S') - Stopping stuck compilation (>${elapsed}s)..." # kill -TERM "$lock_pid" 2>/dev/null # sleep 0.5 # rm -f "$LOCK_FILE" "$compilation_start_file" # else # # In the middle, let it finish # echo_info "$(date '+%H:%M:%S') - Waiting for current compilation to finish (${elapsed}s elapsed)..." # return 1 # fi # fi # else # # Wait mode # echo_warning "$(date '+%H:%M:%S') - Compilation in progress, waiting..." # return 1 # fi # fi # # if acquire_lock; then # echo_info "$(date '+%H:%M:%S') - Starting compilation..." # date +%s > "$compilation_start_file" # cd "$PROJECT_ROOT" # ./compile -m # local status=$? # release_lock # rm -f "$compilation_start_file" # # if [ $status -eq 0 ]; then # echo_success "$(date '+%H:%M:%S') - Compilation successful" # # Load configuration to get environment variables # source ./config/load_config.sh manuscript >/dev/null 2>&1 # # # Update symlink to latest archive version (prevents viewing corrupted PDFs during compilation) # local archive_dir="${SCITEX_WRITER_VERSIONS_DIR}" # local latest_archive=$(ls -1 "$archive_dir"/${SCITEX_WRITER_DOC_TYPE}_v[0-9]*.pdf 2>/dev/null | grep -v "_diff.pdf" | sort -V | tail -1) # # if [ -n "$latest_archive" ]; then # # Create relative symlink to archive # cd "${SCITEX_WRITER_ROOT_DIR}" # ln -sf "archive/$(basename "$latest_archive")" "${SCITEX_WRITER_DOC_TYPE}-latest.pdf" # cd - > /dev/null # echo_info " Symlink updated: ${SCITEX_WRITER_DOC_TYPE}-latest.pdf -> archive/$(basename "$latest_archive")" # else # # Fallback if no archive exists # cd "${SCITEX_WRITER_ROOT_DIR}" # ln -sf "${SCITEX_WRITER_DOC_TYPE}.pdf" "${SCITEX_WRITER_DOC_TYPE}-latest.pdf" # cd - > /dev/null # echo_info " Symlink updated: ${SCITEX_WRITER_DOC_TYPE}-latest.pdf -> ${SCITEX_WRITER_DOC_TYPE}.pdf (no archive yet)" # fi # else # echo_error "$(date '+%H:%M:%S') - Compilation failed" # fi # return $status # else # echo_warning "$(date '+%H:%M:%S') - Could not acquire lock" # return 1 # fi # } # # # Function to get list of files to watch from config # get_watch_files() { # # Read watch patterns from YAML config # local patterns=$(awk '/^ watching_files:/,/^[^ ]/' "$CONFIG_FILE" | \ # grep '^[[:space:]]*-' | \ # sed 's/^[[:space:]]*-[[:space:]]*//' | \ # sed 's/"//g') # # # Expand patterns and find matching files # for pattern in $patterns; do # # Skip comments # [[ "$pattern" =~ ^# ]] && continue # # # Expand the pattern (handles wildcards) # if [[ "$pattern" == *"**"* ]]; then # # Handle recursive patterns # local base_dir=$(echo "$pattern" | sed 's/\/\*\*.*//') # local file_pattern=$(echo "$pattern" | sed 's/.*\*\*\///') # find "$PROJECT_ROOT/$base_dir" -type f -name "$file_pattern" 2>/dev/null # elif [[ "$pattern" == *"*"* ]]; then # # Handle simple wildcards # ls $PROJECT_ROOT/$pattern 2>/dev/null # else # # Direct file # [ -f "$PROJECT_ROOT/$pattern" ] && echo "$PROJECT_ROOT/$pattern" # fi # done | sort -u # } # # # Main watch loop # main() { # # Save PID for external monitoring # echo $$ > "$WATCH_PID_FILE" # # # Check if hot-recompile is enabled # if [ "$HOT_RECOMPILE_ENABLED" != "true" ]; then # echo_warning "Hot-recompile is disabled in config. Set hot-recompile.enabled: true to enable." # exit 0 # fi # # # Count files being watched # local watch_count=$(get_watch_files | wc -l) # # echo_success "========================================== # Hot-Recompile Watch Mode Started # ========================================== # Config: $CONFIG_FILE # Mode: ${COMPILE_MODE} (use 'wait' or 'restart' as argument) # Monitoring: $watch_count files from config patterns # Stable PDF: $STABLE_LINK # # For rsync: rsync -avL $STABLE_LINK remote:/path/ # (The -L flag follows symlinks) # # Press Ctrl+C to stop # ==========================================" # # # Initial compilation # compile_with_lock # # # Check for inotifywait (preferred) or fall back to polling # if command -v inotifywait >/dev/null 2>&1; then # echo_info "Using inotify for file watching (efficient)" # # # Watch for changes using inotify # while true; do # inotifywait -r -q -e modify,create,delete,move \ # "$PROJECT_ROOT/01_manuscript/contents/" \ # --exclude '(~$|\.swp$|\.tmp$|#.*#$|\.git)' \ # 2>/dev/null # # # Small delay to batch rapid changes # sleep 0.5 # # # Compile if not locked # compile_with_lock # # echo_info "$(date '+%H:%M:%S') - Waiting for changes..." # done # else # echo_warning "inotifywait not found, using polling mode (less efficient)" # echo_info "Install inotify-tools for better performance" # # # Polling fallback # declare -A file_times # # # Initialize file timestamps # while IFS= read -r file; do # if [ -f "$file" ]; then # file_times["$file"]=$(stat -c %Y "$file" 2>/dev/null) # fi # done < <(get_watch_files) # # # Poll for changes # while true; do # changed=false # # while IFS= read -r file; do # if [ -f "$file" ]; then # current_time=$(stat -c %Y "$file" 2>/dev/null) # if [ "${file_times[$file]}" != "$current_time" ]; then # echo_info "Change detected: $file" # file_times["$file"]=$current_time # changed=true # fi # fi # done < <(get_watch_files) # # if [ "$changed" = true ]; then # compile_with_lock # echo_info "$(date '+%H:%M:%S') - Waiting for changes..." # fi # # sleep 2 # Poll interval # done # fi # } # # # Check if another watch instance is running # if [ -f "$WATCH_PID_FILE" ]; then # old_pid=$(cat "$WATCH_PID_FILE" 2>/dev/null) # if [ -n "$old_pid" ] && kill -0 "$old_pid" 2>/dev/null; then # echo_error "Another watch instance is already running (PID: $old_pid)" # echo_info "Stop it first with: kill $old_pid" # exit 1 # else # rm -f "$WATCH_PID_FILE" # fi # fi # # # Start watching # main "$@" # # # EOF # -------------------------------------------------------------------------------- # End of Source Code from: /home/ywatanabe/proj/scitex-writer/scripts/shell/watch_compile.sh # --------------------------------------------------------------------------------