Files
bake/bake

167 lines
5.2 KiB
Bash
Executable File

#!/bin/bash
## VERSION 22.4.5
## BASH-MAKE ==========================================================
## Utility to mimick some of GNU Make behavior, but run in a single
## bash shell. This executable can be used as the Bakefile, too,
## by adding the functions under this text, and setting
## STANDALONE=true
##
# Target functions MUST be of format: "name() { # Menu description"
set -e
# In standalone mode, this script also contains targets
STANDALONE=false
if [[ $STANDALONE = true ]]; then
BAKEFILE=$0
else
if [[ -z "$BAKEFILE" ]]; then
# BAKEFILE not set externally
for BAKEDEFAULT in Bakefile bakefile /dev/null; do
if [[ -f ./$BAKEDEFAULT ]]; then
BAKEFILE=./$BAKEDEFAULT
break
fi
done
fi
fi
_autocomplete() {
printf -v SELF "%s/%s" "$( cd "$(dirname "$0")" ; pwd -P )" "$( basename "$0" )"
echo '_bake_complete() {
local BAKE="'$SELF'"
local curr_arg
curr_arg=${COMP_WORDS[COMP_CWORD]}
if [[ $COMP_CWORD -eq 1 ]]; then
COMPREPLY=( $(compgen -W "$( $BAKE __list__ )" -- $curr_arg ) );
fi
if [[ $COMP_CWORD -gt 1 ]]; then
compopt -o nospace
COMPREPLY=($(compgen -W "$( $BAKE _complete_${COMP_WORDS[1]} $curr_arg )" -- "$curr_arg"))
fi
}
complete -F _bake_complete bake
# Run me as: source <( $BAKE __autocomplete__ )
'
exit; }
_flines() { cat "$BAKEFILE" | \
grep -E '^([^_][a-zA-Z0-9_\.\-]+)\(\) {.*'; }
_menu() { _flines | while read line; do
if [[ "$line" =~ ([a-zA-Z0-9_\.\-]+)\(\).\{[\ #]*(.*) ]]; then
printf "\e[33m%+20s \e[36m [ %-50s ]\e[0m\n" \
"${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
fi
done; }
_help() {
cat <<EOF
Bash-Make: bake
Runs bash functions from Bakefile, like make would from Makefile.
Reserved arguments:
-h This help. Add function name to get help for the target.
-f [Bakefile] Choose a different Bakefile than the default.
-l List targets
-m Menu of targets with 'smenu' command
__autocomplete__ get autocomplete function for bash
Example Bakefile
# help() { # This help. Note the exact function declaration + docs format.
# # function names may contain characters: a-zA-Z0-9._-
# # functions starting with _ are not listed
# _menu
# }
# echo foo # NOTE: Bakefile gets sourced when read!
# default() { # Default function will be run if present, and no target passed
# # Extra help to be read with -h
# echo "I'm running!"
# _smenu # interactive menu with 'smenu' command
# }
EOF
}
_itemhelp() { funfound=0; cat "$BAKEFILE" | while read line; do
if [[ "$line" =~ ^("$1")\(\).\{[\ #]*(.*) ]]; then
printf "\e[33m%s \e[36m %s\e[0m\n" \
"${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
funfound=1
else
if [[ $funfound -eq 1 ]]; then
if [[ "$line" =~ ^[\ ]*#[\ ](.*) ]]; then
printf "\e[36m%s\e[0m\n" \
"${BASH_REMATCH[1]}"
else
funfound=0
fi
fi
fi
done; }
_smenu() { while true; do
selection=$( _flines | sed 's/().*//' | \
smenu -m "Select target (q: exit)" -n 22 -t 1 -a c:2,b i:7,b m:7,bu )
[[ -n "$selection" ]] && { _source_bakefile && "$selection"; } || { exit; }
done; exit; }
_commands() { _flines | sed 's/().*//' | while read line; do
printf "^%s$|" "$line"
done | sed 's/.$//'; }
_source_bakefile() {
if [[ $STANDALONE = false ]]; then
if [[ ! -f "$BAKEFILE" ]]; then
echo No Bakefile present >&2
exit 1
fi
. "$BAKEFILE"
fi; }
_update_self() {
curl -S -f -o "$0" -z "$0" \
"https://raw.githubusercontent.com/moonq/bake/main/bake"
chmod +x "$0"
}
for (( i=1; i<=$#; i++ )); do
if [[ $i -eq 1 ]]; then
if [[ "${!i}" = "__list__" ]]; then _flines | sed 's/().*//'; exit; fi
if [[ "${!i}" = "__update__" ]]; then _update_self; exit; fi
if [[ "${!i}" = "__autocomplete__" ]]; then _autocomplete; exit; fi
if [[ "${!i}" = "-v" ]]; then grep "^## VERSION" "$0"; exit; fi
fi
j=$(( i + 1 ))
if [[ "${!i}" = "-m" ]]; then _smenu; exit; fi
if [[ "${!i}" = "-l" ]]; then _menu; exit; fi
if [[ "${!i}" = "-f" ]]; then BAKEFILE="${!j}"; REMOVEARG="${i}"; fi
if [[ "${!i}" = "-h" ]]; then
if [[ -n "${!j}" ]]; then _itemhelp "${!j}"; else _help; fi; exit
fi
done
if [[ -n "$REMOVEARG" ]]; then
# remove args if Bakefile was modified
set -- "${@:1:REMOVEARG-1}" "${@:REMOVEARG+2}"
fi
# Run commands
_source_bakefile
if [[ -z "$1" ]]; then
# No argument given
if [[ "$( type -t default )" = function ]]; then
# Run 'default' if exists
default
else
# or show menu
_menu
fi
else
# Argument given
if [[ "$1" =~ $( _commands ) ]]; then
# Argument is one of the targets:
echo "Running target: $1"
"$@"
elif [[ "$1" =~ "_complete_"* ]]; then
if [[ "$( type -t $1 )" = function ]]; then
# target is a command line completer
"$@"
else
# but there is no defined completion
compgen -o plusdirs -f -- "${@: -1}"
fi
else
# no such command
echo "Command '$1' not recognized"
_menu
fi
fi