diff options
| author | davidovski <david@davidovski.xyz> | 2026-10-01 10:34:33 +0100 |
|---|---|---|
| committer | davidovski <david@davidovski.xyz> | 2026-10-01 10:34:33 +0100 |
| commit | eca55e60c29440c4e2e007b54ea1a1983e965954 (patch) | |
| tree | a98173c09f69864cc25395bfdab67d682434854b /src/util/shmk/shmk.sh | |
| parent | f20bd9d6369c2e1246dcdd4af26b085da99c18e0 (diff) | |
Diffstat (limited to 'src/util/shmk/shmk.sh')
| -rwxr-xr-x | src/util/shmk/shmk.sh | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/util/shmk/shmk.sh b/src/util/shmk/shmk.sh new file mode 100755 index 0000000..be5f00b --- /dev/null +++ b/src/util/shmk/shmk.sh @@ -0,0 +1,283 @@ +#!/bin/sh +# +# A tool to build a single executable from a collection of shell scripts + +#include colors +#include libhbar + +PREFIX=${PREFIX:-/usr} + +usage () { + cat << EOF +${BLUE}Available Options: + +EOF +} + +# find a file in the search tree +# +# _find_file filename +# +_find_file () { + for p in $search_path; do + for f in $p/$1.sh $p/$1; do + $verbose \ + && printf "${LIGHT_BLACK}checking: $f\n" 1>&2 + + [ -f "$f" ] && { + $verbose \ + && printf "${LIGHT_BLACK}found: $f for $1\n" 1>&2 + echo "$f" + return 0 + } + done + done + return 1 +} + +# parse the lines of a file +# +# _parse_lines < file +# +_parse_lines () { + + # keep a running log of which files we've already included + included="" + + # go through the lines + while IFS= read -r line; do + case "$line" in + "#include "*) + # find the file + file="$(_find_file ${line#\#include})" + + # if we have already included this file, do not parse it + case "$included" in + *"$file"*) + $verbose \ + && printf "${LIGHT_BLACK}already included $file, won't include twice\n" 1>&2 + ;; + *) + cat $file | _parse_lines + included="$included $file" + esac + ;; + + "#>"*) + eval ${line#'#>'} + ;; + "#!"*) + printf "%s\n" "$line" + ;; + "#"*);; + *) + printf "%s\n" "$line" + ;; + esac + done +} + +# 'build' a shell file too an output +# goes through the file without running it and interprets all shmk commands +# +# build_shmk file [output] +# +build_shmk () { + [ -f "$1" ] || return 1 + [ ! -z "$2" ] && output="$2" + [ -z "$output" ] && output=$(basename $1) + output=./${output%.*} + + $clean && { + rm -f $output + exit + } + + $verbose && echo "building $1 to $output" + + [ ! -d "${output%/*}" ] && mkdir -p "${output%/*}" + + cat $1 \ + | _parse_lines > ${output} && + [ ! -z ${entry_function} ] && echo "$entry_function" >> ${output} + chmod +x ${output} +} + +# take a .shmk file and build it +# +interpret_shmk () { + . $1 + local cmdlist="" + + ## TODO override if already set + DIST="./dist" + BUILDS="$(sed -rn "s/^build_(.*)\s*\(\)\s*\{/\1/p" $1) $BUILDS" + PROGS="$(sed -rn "s/^prog_(.*)\s*\(\)\s*\{/\1/p" $1) $PROGS " + LIBS="$(sed -rn "s/^lib_(.*)\s*\(\)\s*\{/\1/p" $1) $LIBS" + CHECKS="$(sed -rn "s/^check_(.*)\s*\(\)\s*\{/\1/p" $1) $CHECKS" + + shift + [ -z "$1" ] && set -- clean build check + while [ ! -z "$1" ]; do + case "$1" in + # install programs and libraries to system + # + install) + for prog in $PROGS; do + prog=$(basename $prog) + prog="${prog%.*}" + cmdlist="$cmdlist + install -Dm755 $DIST/$prog ${DESTDIR}/${PREFIX}/bin/$prog #Install program $prog" + done + for lib in $LIBS; do + lib=$(basename $lib) + lib="${lib%.*}" + cmdlist="$cmdlist + install -Dm755 $DIST/$lib ${DESTDIR}/${PREFIX}/lib/$lib.sh #Install library $lib" + done + ;; + + # install programs and libraries to system + # + uninstall) + for prog in $PROGS; do + prog=$(basename $prog) + prog="${prog%.*}" + cmdlist="$cmdlist + rm ${DESTDIR}/${PREFIX}/bin/$prog #Uninstall program $prog" + done + for lib in $LIBS; do + lib=$(basename $lib) + lib="${lib%.*}" + cmdlist="$cmdlist + rm $DIST/$lib ${DESTDIR}/${PREFIX}/lib/$lib.sh #Uninstall library $lib" + done + ;; + + clean) + [ -d "$DIST" ] && cmdlist="$cmdlist + rm -r $DIST #Clean" + ;; + + check) + for check in $CHECKS; do + command -v check_$check > /dev/null 2>&1 && cmdlist="$cmdlist + check_$check #Check $check" + done + ;; + + *) # build all programs + search_path="$DIST $search_path" + + for lib in $LIBS; do + name=$(basename $lib) + build_cmd=lib_$lib + command -v $build_cmd > /dev/null 2>&1 || { + build_cmd="build_shmk $lib $DIST/$name" + } + cmdlist="$cmdlist + $build_cmd #Build library $name" + done + + for prog in $PROGS; do + name=$(basename $prog) + build_cmd=prog_$prog + command -v $build_cmd > /dev/null 2>&1 || { + build_cmd="build_shmk $prog $DIST/$name" + } + cmdlist="$cmdlist + $build_cmd #Build program $name" + done + + # build all libs + ;; + esac + shift + done + + local out len i + $verbose && out=/dev/stdout || out=/dev/null + len=$(($(echo "$cmdlist" | wc -l)-1)) + i=-1 + echo "$cmdlist" | while read -r cmd; do + i=$((i+1)) + outstring="$(printf "${LIGHT_BLACK}[${LIGHT_BLUE}%s${LIGHT_BLACK}/${LIGHT_BLUE}%s${LIGHT_BLACK}] ${LIGHT_WHITE}%s" \ + "$i" "$len" "${cmd#*#}")" + + printf "\r%s\r" "${outstring}" #"$($verbose && printf "\n\n" || printf "\n")" + if ! $verbose && command -v hbar > /dev/null 2>&1; then + printf "\n" + hbar -l 1 -r "${RESET}" -c "${BG_BLUE}" -n "$i" "$len" + fi + + ${cmd%#*} 2>&1 > $out || { + printf "\n${RED}Error $?\n" + exit 1 + } + done + printf "\n" +} + +search_path=". +$DIST/ +/usr/lib/ +/usr/local/lib/ +/usr/share/shmk/ +" + +verbose=false +clean=false + +# test shmk was invoked on a file if so put the filename at the end because it will break getopts +# this is a sketchy workaround because the shebang will always put the +# filename of the project file as the first arg, so this way we can +# happily introduce more arguments +[ -f "$1" ] && { + set -- $* $1 +} + +while getopts ":e:I:o:chv" opt; do + case "${opt}" in + e) + entry_function="$OPTARG" + ;; + o) + output="$OPTARG" + ;; + c) + clean=true + ;; + I) + search_path=" + $OPTARG + $search_path + " + ;; + v) + verbose=true + ;; + h) + usage && exit 0 + ;; + esac +done + +shift $((OPTIND-1)) + +[ -z $1 ] && { + exit 1 +} + +echo "$0 $*" + +[ -f "$1" ] && shebang="$(head -1 $1)" + +case "$shebang" in + "#!"*"shmk"*) + interpret_shmk $@ + ;; + *) + build_shmk $@ + ;; +esac + |
