#!/bin/bash generate_junit_report=0 junit_report_file="" start_time=0 export RPWD=$PWD IS_SOURCED=$( [[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo 1 || echo 0) # Function to exit the script properly if [ "$IS_SOURCED" -eq "0" ]; then SCRIPT=$(readlink -f "$0"); else SCRIPT=$(readlink -f "${BASH_SOURCE[0]}"); fi export MeTTa=$(realpath "$SCRIPT") export METTALOG_DIR=$(dirname "$SCRIPT") # echo "METTALOG_DIR=$METTALOG_DIR" #cd "$METTALOG_DIR" || { echo "Failed to navigate to $METTALOG_DIR"; [[ "$IS_SOURCED" == "1" ]] && return 1 || exit 1; } should_compile=0 never_compile=0 compatio=false #compatio=false RC_OPTIONS=() TIMEOUT=0 verbose="${VERBOSE:-0}" # Use the VERBOSE environment variable or default to '0' (not verbose) OUTPUT_DIR="${METTALOG_OUTPUT}" cd $METTALOG_DIR source ./scripts/ensure_venv cd $RPWD use_docker=auto repl_flag=auto use_rc_file=~/.mettalogrc debug_this_script=false contains_halt=false dry_run=0 # Function to add a flag to a list add_to_list() { local flag=$1 local -n options=$2 if [[ -n "$flag" ]]; then options+=("$flag") fi } # Function to set input, output, and error flags based on the I/O sources set_io_flags() { #local -n STDIO_OPTIONS=$1 # Handle stdin flags if [ -p /dev/stdin ]; then add_to_list "--stdin=pipe" STDIO_OPTIONS elif [ -f /dev/stdin ]; then local input_filename input_filename=$(readlink /proc/self/fd/0) add_to_list "--stdin=file" STDIO_OPTIONS add_to_list "--input-filename=${input_filename}" STDIO_OPTIONS else add_to_list "--stdin=tty" STDIO_OPTIONS fi # Handle stdout flags if [ ! -t 1 ]; then if [ -p /dev/stdout ]; then add_to_list "--stdout=pipe" STDIO_OPTIONS elif [ -f /dev/stdout ]; then local output_filename output_filename=$(readlink /proc/self/fd/1) add_to_list "--stdout=file" STDIO_OPTIONS add_to_list "--output-filename=${output_filename}" STDIO_OPTIONS fi else add_to_list "--stdout=tty" STDIO_OPTIONS fi # Handle stderr flags if [ ! -t 2 ]; then if [ -p /dev/stderr ]; then add_to_list "--stderr=pipe" STDIO_OPTIONS elif [ -f /dev/stderr ]; then local error_filename error_filename=$(readlink /proc/self/fd/2) add_to_list "--stderr=file" STDIO_OPTIONS add_to_list "--error-filename=${error_filename}" STDIO_OPTIONS fi else add_to_list "--stderr=tty" STDIO_OPTIONS fi } # Initialize the STDIO_OPTIONS array STDIO_OPTIONS=() # Set the input, output, and error flags set_io_flags # Function to check if SWI-Prolog is installed check_swipl_installed() { if ! command -v swipl &> /dev/null; then echo -e "\033[0;31mError: SWI-Prolog is not installed. Please install it and try again.\033[0m" exit 1 fi } # Function to check if SWI-Prolog has Janus support (optional, based on your needs) check_janus_support() { if ! swipl -g "use_module(library(janus)), halt(0)." -t "halt(1)" 2>/dev/null; then echo -e "\033[0;31mError: SWI-Prolog does not have Janus support. Please install Janus and try again.\033[0m" python_flag=disable fi } # Capture original auto margins setting and terminal size if [ -t 0 ] ; then original_automargins=$(stty -a | grep -o 'onlcr' || echo 'off') original_size=$(stty size) original_rows=$(echo $original_size | cut -d ' ' -f1) original_cols=$(echo $original_size | cut -d ' ' -f2) fi # Function to reset auto margins and terminal size to their original state reset_settings() { if [ -t 0 ] ; then # Reset auto margins to original state if [ "$original_automargins" == "on" ]; then stty onlcr else stty -onlcr fi # Reset terminal size to original echo -ne "\e[8;${original_rows};${original_cols}t" stty cols "$original_cols" tput smam echo "Settings reset to original." fi } # Function to disable auto margins and set terminal width to 999 columns disable_automargins() { if [ -t 0 ] ; then stty -onlcr # Disable auto margins tput rmam stty cols 999 # Set columns to a large number to prevent wrapping echo "Auto margins disabled, terminal width set to 999 columns." fi } # Function to set traps for clean exit and interruption set_exit_traps() { trap reset_settings EXIT trap 'reset_settings; kill -SIGINT $$' INT } # Initial trap setup for safety # set_exit_traps function quote_arg { # Check if the argument contains single quotes if [[ $1 == *\'* ]]; then # Argument contains single quotes, use double quotes and escape necessary characters local escaped=${1//\\/\\\\} # Escape backslashes escaped=${escaped//\"/\\\"} # Escape double quotes echo "\"$escaped\"" # Wrap in double quotes else # Use single quotes for arguments without single quotes, no escaping needed echo "'$1'" fi } function quote_args_if_needed { local -a quoted_array=() # Initialize an empty array to store the results for arg in "$@"; do # Check if the argument contains spaces, exclamation marks, backslashes, or quote characters if [[ $arg =~ [[:space:]] || $arg == *'!'* || $arg == *'\\'* || $arg == *\"* || $arg == *\'* ]]; then # The argument needs to be quoted local quoted_arg=$(quote_arg "$arg") quoted_array+=("$quoted_arg") else # The argument does not need quoting quoted_array+=("$arg") fi done # Output the array elements without newlines, joined by spaces echo "${quoted_array[*]}" } function remove_quotes { local arg="$1" # Determine the type of quote at the start (if any) local first_char="${arg:0:1}" if [[ $first_char == '"' ]]; then # Double-quoted: Remove leading and trailing quotes and unescape backslashes and double quotes arg="${arg#\"}" arg="${arg%\"}" arg="${arg//\\\\/\\}" arg="${arg//\\\"/\"}" elif [[ $first_char == "'" ]]; then # Single-quoted: Remove leading and trailing quotes # Note: Inside single quotes, backslashes and double quotes are treated literally arg="${arg#\'}" arg="${arg%\'}" fi echo "$arg" } function unquote_arg { # Remove leading and trailing quotes local arg="$1" arg="${arg#\'}" arg="${arg%\'}" arg="${arg#\"}" arg="${arg%\"}" # Unescape escaped characters arg="${arg//\\\\/\\}" # Unescape backslashes arg="${arg//\\\"/\"}" # Unescape double quotes arg="${arg//\\\'/\'}" # Unescape single quotes (if needed, depending on usage context) echo "$arg" } # ANSI escape codes for colors YELLOW='\033[1;33m' BLUE='\033[0;34m' RED='\033[0;31m' GREEN='\033[0;32m' BOLD='\033[1m' # ANSI escape code to reset color NC='\033[0m' # No Color do_DEBUG() { # Calculate the screen width and 74% of it local screen_width=$(tput cols) local threshold=$((screen_width * 74 / 100)) # Construct the debug message local msg="; ${YELLOW}DEBUG${NC} $*" # Calculate the length of the debug message local msg_length=${#msg} if [ "$msg_length" -gt "$threshold" ]; then # If the message is longer than 74% of the screen width, # print a newline before and after the message echo >&2 echo -e "$msg" >&2 echo >&2 else # If the message is not longer than 74% of the screen width, print it as usual echo -e "$msg" >&2 fi } DEBUG() { if [ "$debug_this_script" == "true" ]; then do_DEBUG "$@" elif [ "$dry_run" -eq 1 ]; then do_DEBUG "$@" elif [ "$verbose" -gt 1 ]; then do_DEBUG "$@" elif [[ "$contains_halt" == "true" ]]; then do_DEBUG "$@" fi } IF_REALLY_DO() { if [ "$dry_run" -eq 1 ]; then do_DEBUG "${BLUE}Dry Run:${NC} $*" else DEBUG "${GREEN}Doing:${NC} $*" eval "$@" return $? #|| { DEBUG "${RED}Error executing command:${NC} $*"; return 1; } fi } # Function to resolve path upward but stop one level higher than /usr/ function resolve_upward { local current_dir="$1" while [ "$current_dir" != "/" ]; do if [ "$current_dir" == "/tmp" ]; then echo "/tmp" return fi if [ "$current_dir" == "/usr" ]; then echo "/usr" return fi if [ -d "$current_dir/$2" ]; then echo "$current_dir/$2" return fi current_dir=$(dirname "$current_dir") done } # Initialize the variable to indicate whether to use the test script use_test_script=0 quoted_args=() # Initialize an empty array to store the results for arg in "$@"; do # Check if the argument contains spaces, exclamation marks, backslashes, or quote characters if [[ $arg =~ [[:space:]] || $arg == *'!'* || $arg == *'\\'* || $arg == *\"* || $arg == *\'* ]]; then # The argument needs to be quoted quoted_arg=$(quote_arg "$arg") quoted_args+=("$quoted_arg") else # The argument does not need quoting quoted_args+=("$arg") fi done if [ "$#" -gt 0 ]; then : #echo "ARGS: ${quoted_args[*]}" fi for arg in "$@"; do if [[ "$arg" == "--debugable" ]]; then exec $METTALOG_DIR/scripts/send_keys_debug.sh $(quote_args_if_needed $@) fi done function load_rc_file { local file="$1" # Use the argument as the file local multiline_accumulator="" if [[ ! -f "$file" ]]; then [[ "$verbose" -gt 0 ]] && DEBUG "$file does not exist, returning." return fi if [[ -f "$file" ]]; then DEBUG "reading $file" while IFS= read -r line || [[ -n "$line" ]]; do # Trim leading and trailing whitespace line="${line#"${line%%[![:space:]]*}"}" line="${line%"${line##*[![:space:]]}"}" [[ -z "$line" ]] && continue # Skip empty lines # Check for line continuation (trailing '\') if [[ "$line" =~ \\$ ]]; then multiline_accumulator+="${line%\\} " continue else line="$multiline_accumulator$line" multiline_accumulator="" fi [[ $line =~ ^# ]] && continue # Skip lines that start with a comment # Check if the option is already in $@ local skip=0 for arg in "$@"; do if [[ "$arg" == "$line" ]]; then skip=1 break fi done if [[ "$skip" -eq 1 ]]; then [[ "$verbose" == "1" ]] && DEBUG "Skipping option already in \$@: $line" continue fi RC_OPTIONS+=("$line") [[ "$verbose" -gt 0 ]] && DEBUG "Loaded option: $line" done < "$file" fi local do_args="${RC_OPTIONS[@]}" [[ -z "$do_args" ]] && return DEBUG "load_rc_file $file: ${do_args}" handle_args "${do_args}" } add_to_list() { local item="$1" local -n list_ref="$2" if [[ ! " ${list_ref[*]} " =~ " $item " ]]; then list_ref+=("$item") fi } add_to_front() { local item="$1" local -n list_ref="$2" if [[ ! " ${list_ref[*]} " =~ " $item " ]]; then #PRE_METTALOG_OPTIONS=("--html" "${PRE_METTALOG_OPTIONS[@]}") list_ref=("$item" "${list_ref[@]}") fi } function print_help { echo " Usage: ${MeTTa} [options] ... [-- arg ...passed to your program...]" echo " ${MeTTa} --help Display this message" echo " ${MeTTa} --version Display version information" cat << EOF -x state Start from state (must be first) -g goal Run Prolog-style goal (may be repeated) -G goal Run MeTTa goal (may be repeated) -t toplevel Toplevel goal -f file User initialisation file -F file Site initialisation file -l file Prolog-style script source file -L file MeTTa script source file -s file Prolog-style script source file -S file MeTTa script source file -p alias=path Define file search path 'alias' Note: A bare filename is equivalent to using the -s option for MeTTa scripts. Compilation: ${MeTTa} [options] [-o executable] -c metta-file1 -c metta-file2 ... to compile into executable ... -O Optimised compilation --debug[=bool] Do (not) generate debug info --traditional Disable extensions of version (SWI-Prolog version 7) --abi-version Display ABI version key (and exit) --arch Display architecture (and exit) --dump-runtime-variables[=format] Dump link info in sh(1) format (and exit) Running: --rc File read command line arguments from a file --repl Start the REPL (Read-Eval-Print Loop) after processing metta files. If no metta files are provided, this is the default behavior. --home[=DIR] Print home or use DIR as SWI-Prolog home --stack-limit=size[BKMG] Specify maximum size of stacks --table-space=size[BKMG] Specify maximum size of SLG tables --shared-table-space=size[BKMG] Maximum size of *shared* SLG tables --pce[=bool] Make the xpce gui available --packs[=bool] Do (not) attach add-ons --pldoc[=port] Start PlDoc server [at port] --python[=bool] Enable or disable Python support (default: $python_flag) --tty[=bool] (Dis)allow tty control --quiet[=bool] (-q) Do (not) suppress informational messages Testing: --test Use the test options: --continue Continue running tests (Generating any missing html files) --failures Rerun unsuccessfull tests only --regressions Rerun only tests in which we previously scored 100% --timeout=seconds Kill the script after so many seconds. --html[=bool] Save an HTML file containing terminal output in the same directory as the input file or directory. Defaults to true if exactly one metta file or directory argument was provided --fresh Clean up by deleting any .answers files under directory --clean Clean up by deleting all .html files under directory Debugging: --exec=skip Skip over !exec dirrectives --e=debug Recursively trace Evaluation --time=debug Provide detailed debug-level timing information --case=debug Show extra debug info about case statements --signals[=bool] Do (not) modify signal handling --threads[=bool] Do (not) allow for threads --debug-on-interrupt[=bool] Trap the debugger on interrupt --prolog Drop to the host system debugger --on-error=style One of print, halt or status --on-warning=style One of print, halt or status Boolean options may be written as --name=bool, --name, --no-name or --noname. Both '-' or '_' are accepted as word-separator for long options. Configuration File: This script reads options from the ~/.mettalogrc file, one option per line. Options specified in ~/.mettalogrc are processed before command-line arguments. WAS: ${MeTTa} ${SWI_OPTIONS[*]} -l $INTERP_SRC_DIR/metta_interp.pl -- --python=$python_flag ${PRE_METTALOG_OPTIONS[*]} ${METTALOG_OPTIONS[*]} \\ $METTA_CMD EOF } # Initialize variables SWI_OPTIONS=() METTALOG_OPTIONS=() METTALOG_OPTIONS_LAST=() if [[ "$compatio" == "true" && ! ( $* == *--compatio* ) ]]; then METTALOG_OPTIONS=("--compatio") fi PRE_METTALOG_OPTIONS=() SWI_FLAG_WITH_ARG=false python_flag=enable # Optional: Check if SWI-Prolog has Janus support check_janus_support LIST_OF_FILE_ARGS=() wants_print_help=0 PYSWIP_VERSION="main" # Check if the file exists and Read the first line from the file VERSION_FILE="$METTALOG_DIR/src/version-config" if [ -f "$VERSION_FILE" ]; then read -r FIRST_LINE < "$VERSION_FILE" FIRST_LINE="${FIRST_LINE%"${FIRST_LINE##*[![:space:]]}"}" if [ ! -z "$FIRST_LINE" ]; then if [ -d "$METTALOG_DIR/src/$FIRST_LINE/" ]; then PYSWIP_VERSION="$FIRST_LINE" fi fi fi DefaultSav="Sav.$(hostname).MeTTaLog" function handle_args { SWI_FLAG_WITH_ARG=false METTA_FLAG_WITH_ARG=false SKIP_TO_METTALOG_OPTIONS=false NEXT_ARG_IS_RC_FILE=false PrevDir="${RPWD:-$(pwd)}" # Default to current directory if PrevDir is not set for arg0 in "$@"; do process_arg=true while [ "$process_arg" == "true" ]; do process_arg=false arg=$(remove_quotes "$arg0") # Remove the quotes # Check if the previous argument was --rc if [[ "$NEXT_ARG_IS_RC_FILE" == true ]]; then NEXT_ARG_IS_RC_FILE=false rc_file_path="$PrevDir/$arg" # Resolve file path relative to PrevDir rc_file_path="$(realpath "$rc_file_path")" # Resolve to absolute path load_rc_file "$rc_file_path" use_rc_file="" continue fi # Add support for --rc followed by a file if [[ "$arg" == "--rc" ]]; then NEXT_ARG_IS_RC_FILE=true continue fi if [[ "$arg" == "--norc" ]]; then use_rc_file="" continue fi arg_realpath="$arg" # track file paths but keep going if [[ -f "$arg" || -d "$arg" ]]; then arg_realpath="$(realpath "$arg")" if [[ -f "$arg_realpath" || -d "$arg_realpath" ]]; then add_to_list "$arg_realpath" LIST_OF_FILE_ARGS fi fi if [ "$SWI_FLAG_WITH_ARG" = true ]; then add_to_list "$arg_realpath" SWI_OPTIONS SWI_FLAG_WITH_ARG=false # Reset the flag continue fi case "$arg" in --stack-limit=*|--stack_limit=*|--table-space=*|--shared-table-space=*|--pce=*|--pldoc=*|--tty=*|--packs=*|--signals=*|--sigalert=*|--threads=*|--debug=*|--debug-on-interrupt=*|--quiet=*|--traditional|--home=*|--on-error=*|--on-warning=*) add_to_list "$arg" SWI_OPTIONS #continue ;; -D*=*|-O) add_to_list "$arg" SWI_OPTIONS continue ;; -x|-g|-t|-f|-F|-l|-s|-p|-D) # For options that require an argument in the next round add_to_list "$arg" SWI_OPTIONS SWI_FLAG_WITH_ARG=true continue ;; esac if [[ $SKIP_TO_METTALOG_OPTIONS == true ]]; then METTALOG_OPTIONS+=("$arg") continue fi if [[ "$arg" == "--" ]]; then METTALOG_OPTIONS+=("$arg") SKIP_TO_METTALOG_OPTIONS=true continue fi if [[ "$arg" == "--compatio" ]]; then #debug_this_script=false compatio=true fi if [[ "$arg" == "--debug" ]]; then debug_this_script=true compatio=false fi if [[ "$arg" == "--docker=false" ]]; then use_docker=false fi if [[ "$arg" == "--docker=true" ]]; then use_docker=true fi if [[ "$arg" == "--docker" ]]; then use_docker=true fi # Check if the argument is a directory if [ -d "$arg" ]; then use_test_script=1 fi # Check for specific flags case "$arg" in --fres*|--fail*|--contin*|--regres*|--clean) use_test_script=1 ;; --halt*) contains_halt=true use_test_script=0 ;; esac if [[ "$arg" =~ ^--timeout=([0-9]+)$ ]]; then parsed="${BASH_REMATCH[1]}" #do_DEBUG "TIMEOUT=$TIMEOUT PARSED=$parsed" TIMEOUT=$parsed export TIMEOUT #METTALOG_OPTIONS=("--timeout=$TIMEOUT" "${METTALOG_OPTIONS[@]}") #add_to_list "$arg" METTALOG_OPTIONS continue elif [[ "$arg" =~ ^--python=(enable|disable|false)$ ]]; then python_flag="${BASH_REMATCH[1]}" continue elif [[ "$arg" == "--python" ]]; then python_flag=enable continue elif [[ "$arg" == "--no-python" ]]; then python_flag=false continue elif [[ "$arg" == "--repl" ]]; then #add_to_list "$arg" METTALOG_OPTIONS_LAST repl_flag=true continue elif [[ "$arg" == "--repl=true" ]]; then repl_flag=true continue elif [[ "$arg" == "--repl=false" ]]; then repl_flag=false continue elif [[ "$arg" == "--no-repl" ]]; then repl_flag=false continue elif [[ "$arg" == "--html" ]]; then add_to_list "$arg" METTALOG_OPTIONS html_flag=enable elif [[ "$arg" == "--no-html" ]]; then html_flag=false elif [[ "$arg" == "--prolog" ]]; then add_to_list "$arg" METTALOG_OPTIONS repl_flag=false fi [[ "$arg" == "--help" || "$arg" == "-h" ]] && { wants_print_help=1; EXIT_SCRIPT=0; } [[ "$arg" =~ ^--dump-runtime-variables.*$ || "$arg" == "--abi-version" || "$arg" == "--version" || "$arg" == "--arch" ]] && { swipl $@; EXIT_SCRIPT=0; } [[ "$SWI_FLAG_WITH_ARG" == true ]] && { SWI_OPTIONS+=("$arg"); SWI_FLAG_WITH_ARG=false; continue; } [[ "$METTA_FLAG_WITH_ARG" == true ]] && { METTALOG_OPTIONS+=("\"$arg\""); METTA_FLAG_WITH_ARG=false; continue; } # These options require an argument (like a filename) case $arg in --output=*) add_to_list "$arg" METTALOG_OPTIONS if [[ "$arg" =~ ^--output=(.*)$ ]]; then OUTPUT_DIR="${BASH_REMATCH[1]}" export METTALOG_OUTPUT="${OUTPUT_DIR}" fi continue ;; --log) add_to_list "$arg" PRE_METTALOG_OPTIONS add_to_front "--html" PRE_METTALOG_OPTIONS add_to_front "--debug" PRE_METTALOG_OPTIONS compatio=false html_flag=enable continue ;; --v=*) PYSWIP_VERSION="${arg#*=}" if [ -f "${DefaultSav}" ]; then rm -f "${DefaultSav}" fi never_compile=1 continue ;; -x|-g|-t|-f|-F|-l|-s|-p|--on-error|--on-warning|--home|--stack-limit|--table-space|--shared-table-space|--pldoc) SWI_OPTIONS+=("$arg") SWI_FLAG_WITH_ARG=true continue ;; -G|-L|-S) METTALOG_OPTIONS+=("$arg") METTA_FLAG_WITH_ARG=true continue ;; # These options don't require an argument -O|--traditional|--tty*|--packs*|--signals*|--threads*|--debug*|--debug-on-interrupt*|--quiet*|--pce*) SWI_OPTIONS+=("$arg") continue ;; ---*) DASH2="${arg#-}" PRE_METTALOG_OPTIONS=("$DASH2" "${PRE_METTALOG_OPTIONS[@]}") continue ;; *) add_to_list "$arg" METTALOG_OPTIONS if [[ -f "$arg" || -d "$arg" ]]; then arg_realpath="$(realpath "$arg")" if [[ -f "$arg_realpath" || -d "$arg_realpath" ]]; then add_to_list "$arg_realpath" LIST_OF_FILE_ARGS fi fi ;; esac done done } # pre process command-line arguments for arg in "$@"; do arg=$(remove_quotes "$arg") # Remove the quotes if [[ -f "$arg" || -d "$arg" ]]; then arg_realpath="$(realpath "$arg")" if [[ -f "$arg_realpath" || -d "$arg_realpath" ]]; then add_to_list "$arg_realpath" LIST_OF_FILE_ARGS fi fi done #DEBUG "LIST_OF_FILE_ARGS[0]=${LIST_OF_FILE_ARGS[0]}" DIRNAME="${LIST_OF_FILE_ARGS[0]}" if [[ -f "$DIRNAME" ]]; then HTML_OUT="${DIRNAME}.html" DIRNAME=$(dirname "${DIRNAME}") elif [[ -d "$DIRNAME" ]]; then HTML_OUT="${DIRNAME}/Result.html" else DIRNAME="${PWD}" HTML_OUT="${DIRNAME}/Result.html" fi DIR_RC="$DIRNAME/.mettalogrc" #DEBUG "DIR_RC=$DIR_RC" if [[ -f "$DIR_RC" ]]; then use_rc_file="${DIR_RC}" else use_rc_file="~/.mettalogrc" fi # maybe process arguments from ~/.mettalogrc ? #load_rc_file "~/.mettalogrc" # process actual command-line arguments handle_args "$@" # process specific RC file if [[ -f "${use_rc_file}" ]]; then load_rc_file "${use_rc_file}" fi # Decide on enabling the REPL if [[ "$repl_flag" != "false" ]]; then [[ ${#LIST_OF_FILE_ARGS[@]} -eq 0 ]] && repl_flag=true && add_to_list "--repl" METTALOG_OPTIONS_LAST || true fi if [[ "$repl_flag" == "true" ]]; then TIMEOUT=0 add_to_list "--repl" METTALOG_OPTIONS_LAST fi if [[ -n "$TIMEOUT" && "$TIMEOUT" -gt 0 ]]; then add_to_list "--timeout=$TIMEOUT" PRE_METTALOG_OPTIONS fi # Execute the test script *INSTEAD* if the condition is met if [[ "$use_test_script" -eq 1 ]]; then #if [[ "$contains_halt" == "false" ]]; then #WILL_CALL=" -y --report=n $(quote_args_if_needed ${PRE_METTALOG_OPTIONS[*]}) $(quote_args_if_needed ${METTALOG_OPTIONS[*]}) $(quote_args_if_needed ${METTALOG_OPTIONS_LAST[*]})" WILL_CALL=" -y --report=n $(quote_args_if_needed ${RC_OPTIONS[*]}) $(quote_args_if_needed $@)" echo -e "${GREEN}test_in_metta.sh $WILL_CALL${NC}\n" exec $METTALOG_DIR/scripts/test_in_metta.sh $WILL_CALL #fi fi #export TEE_FILE=${TEE_FILE:-"$METTALOG_DIR/TEE.ansi"} if [ "$use_docker" != "auto" ]; then if [ "$use_docker" == "true" ]; then # Check if the script is running inside a Docker container if [ ! -f /.dockerenv ]; then # Define the name of the Docker image IMAGE_NAME="mettalog" # Check if Docker is installed if command -v docker &> /dev/null; then # Check if the Docker image exists if docker image inspect "$IMAGE_NAME" &> /dev/null; then DEBUG "Updating the Docker image: $IMAGE_NAME" # Create a temporary file to capture the build output temp_file=$(mktemp) # Build the Docker image and redirect stderr to the temporary file if ! (cd $METTALOG_DIR ; docker build . -t "$IMAGE_NAME" > "$temp_file" ) 2>&1; then echo "Docker build failed. Output:" # Tail the last 30 lines of the build output for debugging tail -30 "$temp_file" # Clean up the temporary file rm "$temp_file" exit 1 else # If build succeeds, remove the temporary file rm "$temp_file" DEBUG "Image $IMAGE_NAME successfully updated." fi # Setup additional environment variables or paths SCRIPT_DIR=$(dirname "$(readlink -f "$0")") # Example: UPWARD=$(resolve_upward "$(pwd)") # Run the Docker container with the necessary volumes mounted exec docker run -it \ -v "${SCRIPT_DIR}:/home/user/metta-wam" \ -v "$(pwd):$(pwd)" \ -w "$(pwd)" \ "$IMAGE_NAME" \ /home/user/metta-wam/mettalog "$@" else DEBUG "Image $IMAGE_NAME is not installed. Continuing with script..." fi else DEBUG "Docker is not installed. Continuing with script..." fi fi fi fi set -e DEBUG "SWI_OPTIONS: ${SWI_OPTIONS[@]}" DEBUG "PRE_METTALOG_OPTIONS: ${PRE_METTALOG_OPTIONS[@]}" DEBUG "LIST_OF_FILE_ARGS: ${LIST_OF_FILE_ARGS[@]}" DEBUG "METTALOG_OPTIONS: ${METTALOG_OPTIONS[@]}" # Store the initial PYTHONPATH for later comparison initial_pythonpath="$PYTHONPATH" # Add DIRNAME to PYTHONPATH if it's a valid path and not already present if [[ -d "$DIRNAME" && ":$PYTHONPATH:" != *":$DIRNAME:"* ]]; then export PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}$DIRNAME" fi # Add src to PYTHONPATH if not already present [[ ":$PYTHONPATH:" != *":$METTALOG_DIR/src:"* ]] && export PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}$METTALOG_DIR/src" # If PYTHONPATH has changed, echo the new value if [[ "$PYTHONPATH" != "$initial_pythonpath" ]]; then : #DEBUG ";; Updated PYTHONPATH: $PYTHONPATH" fi export RUST_BACKTRACE=full PYSWIP_VERSION="${PYSWIP_VERSION%/}" # Directory containing the .pl files if [ -f "$PYSWIP_VERSION/metta_interp.pl" ]; then INTERP_SRC_DIR="$PYSWIP_VERSION" else if [ -f "$PYSWIP_VERSION/src/canary/metta_interp.pl" ]; then INTERP_SRC_DIR="$PYSWIP_VERSION/src/canary" else INTERP_SRC_DIR="$METTALOG_DIR/src/$PYSWIP_VERSION" fi fi INTERP_SRC_DIR="$(realpath "${INTERP_SRC_DIR}")" DEBUG "INTERP_SRC_DIR=$INTERP_SRC_DIR" # Initialize a flag to check if any file is newer or if reference file is missing if [[ $never_compile -eq 0 ]]; then # Reference file reference_file=$(find . -maxdepth 1 -type f -name "${DefaultSav}*" -not -name "*.*" -printf "%T@ %p\n" | sort -k1,1nr | head -n 1 | cut -f2- -d" ") if [[ -z "$reference_file" ]]; then reference_file="$METTALOG_DIR/${DefaultSav}" fi # Check if ${DefaultSav} exists if [[ ! -e "$reference_file" ]]; then DEBUG "Reference file $reference_file does not exist. Compiler will be called." should_compile=1 else # Iterate over each .pl file to check if it's newer for pl_file in "$INTERP_SRC_DIR"/*_*.pl; do if [[ ! -e "$pl_file" ]]; then DEBUG "No matching .pl files found in $INTERP_SRC_DIR." #exit 1 fi # Check if this .pl file is newer than the reference file if [[ "$pl_file" -nt "$reference_file" ]]; then DEBUG "$pl_file is newer than $reference_file." should_compile=1 break # No need to check further, exit loop fi done fi fi # If any newer file found or reference file missing, call the compiler if [[ $should_compile -eq 1 ]]; then #if [[ -f "$reference_file" ]]; then # DEBUG "Calling compiler from $reference_file..." # swipl -x $reference_file -g qsave_program -g halt #else rm -f $reference_file if [[ "$never_compile" -eq 0 ]]; then : #DEBUG "Compiling $reference_file" #swipl -l src/$PYSWIP_VERSION/metta_interp.pl -g qcompile_mettalog -- --exeout=$reference_file fi fi reference_file=$(find . -maxdepth 1 -type f -name "${DefaultSav}*" -not -name "*.*" -printf "%T@ %p\n" | sort -k1,1nr | head -n 1 | cut -f2- -d" ") if [[ -z "$reference_file" ]]; then reference_file="$METTALOG_DIR/${DefaultSav}" fi if [[ -f "$reference_file" ]]; then : # Placeholder in case something needs to be done here # rm -f $reference_file fi if [[ -f "$reference_file" ]]; then MLOG="$reference_file" if [[ "${#SWI_OPTIONS[@]}" -gt 0 ]]; then MLOG="swipl -x $reference_file ${SWI_OPTIONS[*]}" fi if [[ "$never_compile" -eq 1 ]]; then MLOG="swipl ${SWI_OPTIONS[*]} $INTERP_SRC_DIR/metta_interp.pl" fi else MLOG="swipl ${SWI_OPTIONS[*]} $INTERP_SRC_DIR/metta_interp.pl" fi #add_to_list "--log" METTALOG_OPTIONS #html_flag=enable #add_to_list "--html" METTALOG_OPTIONS STDIO_OPTIONS=() set_io_flags STDIO_OPTIONS # Generate the final command METTA_CMD="$MLOG -- --python=$python_flag -- ${PRE_METTALOG_OPTIONS[*]} ${METTALOG_OPTIONS[*]} ${METTALOG_OPTIONS_LAST[*]} ${STDIO_OPTIONS[*]}" OS=$(uname) TIMEOUT_CMD="timeout" if [[ "$OS" == "Darwin" ]]; then # macOS if command -v gtimeout >/dev/null 2>&1; then TIMEOUT_CMD="gtimeout" else DEBUG "Please install coreutils using Homebrew to get the gtimeout command." [[ "$IS_SOURCED" == "1" ]] && return 1 || exit 1 fi fi # Initialize the variable to store the exit status of METTA_CMD METTA_CMD_EXIT_STATUS=666 TEMP_EXIT_CODE_FILE="$(mktemp)" # Set a trap to ensure stty sane is run on script exit or interruption cleanup() { if [ -t 0 ] ; then stty sane fi if [[ -f "$TEMP_EXIT_CODE_FILE" ]]; then METTA_CMD_EXIT_STATUS=$(<"$TEMP_EXIT_CODE_FILE") else METTA_CMD_EXIT_STATUS=${METTA_CMD_EXIT_STATUS:-$?} fi do_DEBUG "Exit code of METTA_CMD: $METTA_CMD_EXIT_STATUS" if [ ! -z "$TEE_FILE" ];then if [ ! -z "$HTML_OUT" ];then IF_REALLY_DO "ansi2html -u < '$TEE_FILE' > '$HTML_OUT'" HTML_OUT= fi IF_REALLY_DO "rm -f '$TEE_FILE'" TEE_FILE= fi if [ "$IS_SOURCED" -eq 1 ]; then return $METTA_CMD_EXIT_STATUS else exit $METTA_CMD_EXIT_STATUS fi } # Trap exit signal to execute cleanup function trap cleanup EXIT if [[ -n "$TIMEOUT" && "$TIMEOUT" -gt 0 ]]; then export TIMEOUT METTA_CMD="$TIMEOUT_CMD --preserve-status --foreground --signal=SIGTERM --kill-after=5s $TIMEOUT ${METTA_CMD}" fi function escape_quotes { local value="$1" echo "${value//\"/\\\"}" } cd "${RPWD}" set +e # Function to execute the command with the appropriate redirections execute_with_pipes() { local input_redirection="" local output_redirection="" local error_redirection="" # Handle stdin redirection if [[ "${STDIO_OPTIONS[*]}" =~ "--stdin=file" ]]; then input_file=$(echo "${STDIO_OPTIONS[*]}" | grep -oP '(?<=--input-filename=)[^ ]+') input_redirection="<\"$input_file\"" elif [[ "${STDIO_OPTIONS[*]}" =~ "--stdin=pipe" ]]; then # stdin is already a pipe, no action needed : else # stdin is a tty, no redirection needed : fi # Handle stdout redirection if [[ "${STDIO_OPTIONS[*]}" =~ "--stdout=file" ]]; then output_file=$(echo "${STDIO_OPTIONS[*]}" | grep -oP '(?<=--output-filename=)[^ ]+') output_redirection=">\"$output_file\"" elif [[ "${STDIO_OPTIONS[*]}" =~ "--stdout=pipe" ]]; then # stdout is already a pipe, no action needed : else # stdout is a tty, no redirection needed : fi # Handle stderr redirection if [[ "${STDIO_OPTIONS[*]}" =~ "--stderr=file" ]]; then error_file=$(echo "${STDIO_OPTIONS[*]}" | grep -oP '(?<=--error-filename=)[^ ]+') error_redirection="2>\"$error_file\"" elif [[ "${STDIO_OPTIONS[*]}" =~ "--stderr=pipe" ]]; then # stderr is already a pipe, no action needed : else # stderr is a tty, no redirection needed : fi # Construct the full command with all necessary redirections local full_command="eval \"$METTA_CMD\" $input_redirection $output_redirection $error_redirection" # Check if SWI-Prolog is installed check_swipl_installed # Execute the command using IF_REALLY_DO IF_REALLY_DO "$full_command" } # Conditional to check if html_flag is enabled if [[ "$html_flag" == "enable" ]]; then # Generate a random filename for TEE_FILE with date,time,PID random_suffix=$(date +"%Y%m%d_%H%M")_$$ TEE_FILE="$METTALOG_DIR/TEE_$random_suffix.ansi" export TEE_FILE if [ ! -z "$HTML_OUT" ];then HTML_OUT=$(realpath --relative-to="$(pwd)" "$HTML_OUT") if [ ! -z "$OUTPUT_DIR" ] ;then export METTALOG_OUTPUT="${OUTPUT_DIR}" HTML_OUT="${OUTPUT_DIR}/${HTML_OUT}" fi export HTML_FILE="${HTML_OUT}" fi export TYPESCRIPT=1 if [[ "$OS" == "Darwin" ]]; then # macOS METTA_CMD="/usr/bin/script -q -f -a \"$TEE_FILE\" -c \\\"$(printf '%q ' ${METTA_CMD[@]})\\\"" else # Assume Linux METTA_CMD="/usr/bin/script -q -f --force -e -a \"$TEE_FILE\" -c \\\"$(echo "${METTA_CMD}" | sed 's/"/\\"/g')\\\"" fi [[ "$wants_print_help" == "1" ]] && { print_help; } DEBUG "" DEBUG "Afterwhich ansi2html -u < $TEE_FILE > '$HTML_OUT'" DEBUG "" [[ -n "${EXIT_SCRIPT+x}" ]] && { [[ "$IS_SOURCED" == "1" ]] && return "$EXIT_SCRIPT" || exit "$EXIT_SCRIPT"; } IF_REALLY_DO "touch '$TEE_FILE'" IF_REALLY_DO "chmod 777 '$TEE_FILE'" IF_REALLY_DO "cat /dev/null > '$TEE_FILE'" if [[ "$contains_halt" == "true" ]]; then do_DEBUG "METTA_CMD: $METTA_CMD" fi execute_with_pipes echo $? > "$TEMP_EXIT_CODE_FILE" else [[ "$wants_print_help" == "1" ]] && { print_help; [[ "$IS_SOURCED" == "1" ]] && return "$EXIT_SCRIPT" || exit "$EXIT_SCRIPT"; } [[ -n "${EXIT_SCRIPT+x}" ]] && { [[ "$IS_SOURCED" == "1" ]] && return "$EXIT_SCRIPT" || exit "$EXIT_SCRIPT"; } if [ ! -z "$OUTPUT_DIR" ] ;then export METTALOG_OUTPUT="${OUTPUT_DIR}" fi if [[ "$contains_halt" == "true" ]]; then do_DEBUG "METTA_CMD: $METTA_CMD" fi execute_with_pipes IF_REALLY_DO "echo $? > '$TEMP_EXIT_CODE_FILE'" fi cd "${RPWD}"