diff --git a/README.md b/README.md index 94dcc5f..80f3e08 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ bes-build is a bash script to build bash program. the building process simply consist to append shell script files from your `src/` project directory into a single `dist/project` executable file + +since version 0.5 bes-build attempt to be a dependency manager like `composer` (for php projects) but in a more extra-light way + + ### Install ``` @@ -40,10 +44,32 @@ using bes-build script require you to conform to these following rules : |--- file3.sh ``` * each `src/` shell file require a `shebang` on first line (**#!/bin/bash**) -* src/main.sh file is append to the end of the build file +* `src/main.sh` file is append to the end of the build file * we strongly recommand you to use function and prefix function name ```shell bes.install(){ ... } ``` + +### Depency Manager Requirements + +**note** This functionnality is still in progress +to use `bes-build` like a dependency manager, you need a `bes.ini` file in your application root path + +```ini +[require] +bes.echo = 1.0 +``` + +then you can run the `update` command before building + +```shell +bes-build update +``` + +`bes-build` call `git` on a `vendor` directory and clone the require lib + +on next build, bes-build will append the dependencies + + diff --git a/dist/bes-build b/dist/bes-build index 4c835fd..9de7d38 100755 --- a/dist/bes-build +++ b/dist/bes-build @@ -1,5 +1,6 @@ #!/bin/bash +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bes.build(){ bes.echo.title "building project" "$APP_NAME" if [ -d "$APP_DIR/src" ]; then @@ -19,6 +20,24 @@ bes.build(){ bes.echo.state $? fi echo "#!/bin/bash" > $APP_BIN + bes.echo.action "reading ${Coff}dependencies" + for vendor in "$APP_DIR/vendor/*"; do + if [ "$(basename $vendor)" != "." ] && [ "$(basename $vendor)" != ".." ]; then + local vendorName="$(basename $vendor)" + for project in "$vendor/*"; do + if [ "$(basename $project)" != "." ] && [ "$(basename $project)" != ".." ]; then + for entry in "$project/src"/*.sh; do + local vendorName="$(basename $vendor)" + local project="$(basename $(dirname $(dirname $entry)))" + bes.echo " ${Cspe}- ${Cok}appending ${Cusa}$vendorName/$project/${Coff}src/$(basename $entry)" + tail -n +2 "$APP_DIR/vendor/$vendorName/$project/src/$(basename $entry)" >> "$APP_BIN" + done + fi + done + fi + done + bes.echo.state 0 + bes.echo.action "reading ${Coff}src/" for entry in "$APP_DIR/src"/*.sh; do if [ "$(basename $entry)" != "main.sh" ]; then @@ -42,11 +61,13 @@ bes.build(){ fi } -BES_TERM_WIDTH=105 - BES_NOCOLOR=0 - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -if [ "$BES_NOCOLOR" -eq 0 ]; then +bes.echo.boot () +{ + BES_TERM_WIDTH=${BES_TERM_WIDTH:-105} + BES_NOCOLOR=${BES_NOCOLOR:-0} + + if [ "$BES_NOCOLOR" -eq 0 ]; then Cok="\033[0;38;5;43m"; Cko="\033[0;38;5;217m" Coff="\033[m"; Ctitle="\033[1;48;5;24;1;38;5;15m" Cdone="\033[1;48;5;36;1;38;5;15m"; Cfail="\033[1;48;5;196;1;38;5;15m" @@ -57,9 +78,11 @@ if [ "$BES_NOCOLOR" -eq 0 ]; then Copt="\033[1;38;5;81m"; Csep="\033[1;38;5;241m" Cerr="\033[1;38;5;196m"; Ccom="\033[0;38;5;139m" Csection="\033[1;38;5;97m"; Caction="\033[0;38;5;37m" -fi + fi +} # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo(){ +bes.echo () +{ local msg=${1:-''} local isAction=${2:-'0'} local symbol=${3:-' *'} @@ -80,24 +103,31 @@ bes.echo(){ fi } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.action(){ +bes.echo.action () +{ bes.echo "$1" 1 } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.title(){ +bes.echo.title () +{ + echo bes.echo " ${Citem}☪ ${Csection}$1 ${Cspe}$2${Coff}" echo } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.keyval(){ +bes.echo.keyval () +{ local c=': ' if [ ! "$BES_NOCOLOR" = 1 ]; then c="$Citem: ${Cval}" fi - bes.echo " $1 $c$2" 1 " " + local len="%-15s " +# printf "%s %s [UP]\n" $PROC_NAME "${line:${#PROC_NAME}}" + bes.echo "$(printf $len $1) $c$2 " 1 " " } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.state(){ +bes.echo.state () +{ local len=8 printf "%0.s " $(seq 1 $(($BES_TERM_WIDTH-${len}))) if [ "$1" = 0 ]; then @@ -107,7 +137,8 @@ bes.echo.state(){ fi } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.rs(){ +bes.echo.rs () +{ local rs=${1:-0} if [ "$rs" -eq 0 ]; then echo -e " ${Cdone} done ${Coff}" @@ -116,11 +147,13 @@ bes.echo.rs(){ fi } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.error(){ +bes.echo.error () +{ echo -e "\n${Cerr} error : ${Coff}\n\t$1 ${Coff}\n" } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.sepline(){ +bes.echo.sepline () +{ local char=${1:-'_'} local width=${2:-$BES_TERM_WIDTH} echo -ne "${Csep} " @@ -128,7 +161,8 @@ bes.sepline(){ echo -e "${Coff}" } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.title(){ +bes.echo.app () +{ local msg=${1:-''} local version=${2:-''} local author=${3:-'a-Sansara'} @@ -136,14 +170,15 @@ bes.title(){ msg="$msg ${Cval}v$version" fi local len="$1${version}license : GNU GPL v3 author:$author" - bes.sepline + bes.echo.sepline echo -ne "\n $Ctitle $msg $Coff" printf "%0.s " $(seq 1 $(($BES_TERM_WIDTH-${#len}-15))) echo -e " ${Cmeta}license : ${Coff}GNU GPL v3 ${Cmeta}author : ${Cval}$author" - bes.sepline + bes.echo.sepline } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.color.map(){ +bes.echo.colormap () +{ for fgbg in 38 48 ; do for color in {0..256} ; do echo -en "\e[${fgbg};5;${color}m ${color}\t\e[0m" @@ -154,7 +189,299 @@ bes.color.map(){ echo done } +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bes.echo.boot +# bes alter '__' to '_' +# +# > https://github.com/rudimeier/bash_ini_parser +# +# Copyright (c) 2009 Kevin Porter / Advanced Web Construction Ltd +# (http://coding.tinternet.info, http://webutils.co.uk) +# Copyright (c) 2010-2014 Ruediger Meier +# (https://github.com/rudimeier/) +# +# License: BSD-3-Clause, see LICENSE file +# +# Simple INI file parser. +# +# See README for usage. +# +# +function read_ini() +{ + # Be strict with the prefix, since it's going to be run through eval + function check_prefix() + { + if ! [[ "${VARNAME_PREFIX}" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] ;then + echo "read_ini: invalid prefix '${VARNAME_PREFIX}'" >&2 + return 1 + fi + } + + function check_ini_file() + { + if [ ! -r "$INI_FILE" ] ;then + echo "read_ini: '${INI_FILE}' doesn't exist or not" \ + "readable" >&2 + return 1 + fi + } + + # enable some optional shell behavior (shopt) + function pollute_bash() + { + if ! shopt -q extglob ;then + SWITCH_SHOPT="${SWITCH_SHOPT} extglob" + fi + if ! shopt -q nocasematch ;then + SWITCH_SHOPT="${SWITCH_SHOPT} nocasematch" + fi + shopt -q -s ${SWITCH_SHOPT} + } + + # unset all local functions and restore shopt settings before returning + # from read_ini() + function cleanup_bash() + { + shopt -q -u ${SWITCH_SHOPT} + unset -f check_prefix check_ini_file pollute_bash cleanup_bash + } + + local INI_FILE="" + local INI_SECTION="" + + # {{{ START Deal with command line args + + # Set defaults + local BOOLEANS=1 + local VARNAME_PREFIX=INI + local CLEAN_ENV=0 + + # {{{ START Options + + # Available options: + # --boolean Whether to recognise special boolean values: ie for 'yes', 'true' + # and 'on' return 1; for 'no', 'false' and 'off' return 0. Quoted + # values will be left as strings + # Default: on + # + # --prefix=STRING String to begin all returned variables with (followed by '_'). + # Default: INI + # + # First non-option arg is filename, second is section name + + while [ $# -gt 0 ] + do + + case $1 in + + --clean | -c ) + CLEAN_ENV=1 + ;; + + --booleans | -b ) + shift + BOOLEANS=$1 + ;; + + --prefix | -p ) + shift + VARNAME_PREFIX=$1 + ;; + + * ) + if [ -z "$INI_FILE" ] + then + INI_FILE=$1 + else + if [ -z "$INI_SECTION" ] + then + INI_SECTION=$1 + fi + fi + ;; + + esac + + shift + done + + if [ -z "$INI_FILE" ] && [ "${CLEAN_ENV}" = 0 ] ;then + echo -e "Usage: read_ini [-c] [-b 0| -b 1]] [-p PREFIX] FILE"\ + "[SECTION]\n or read_ini -c [-p PREFIX]" >&2 + cleanup_bash + return 1 + fi + + if ! check_prefix ;then + cleanup_bash + return 1 + fi + + local INI_ALL_VARNAME="${VARNAME_PREFIX}_ALL_VARS" + local INI_ALL_SECTION="${VARNAME_PREFIX}_ALL_SECTIONS" + local INI_NUMSECTIONS_VARNAME="${VARNAME_PREFIX}_NUMSECTIONS" + if [ "${CLEAN_ENV}" = 1 ] ;then + eval unset "\$${INI_ALL_VARNAME}" + fi + unset ${INI_ALL_VARNAME} + unset ${INI_ALL_SECTION} + unset ${INI_NUMSECTIONS_VARNAME} + + if [ -z "$INI_FILE" ] ;then + cleanup_bash + return 0 + fi + + if ! check_ini_file ;then + cleanup_bash + return 1 + fi + + # Sanitise BOOLEANS - interpret "0" as 0, anything else as 1 + if [ "$BOOLEANS" != "0" ] + then + BOOLEANS=1 + fi + + + # }}} END Options + + # }}} END Deal with command line args + + local LINE_NUM=0 + local SECTIONS_NUM=0 + local SECTION="" + + # IFS is used in "read" and we want to switch it within the loop + local IFS=$' \t\n' + local IFS_OLD="${IFS}" + + # we need some optional shell behavior (shopt) but want to restore + # current settings before returning + local SWITCH_SHOPT="" + pollute_bash + + while read -r line || [ -n "$line" ] + do +#echo line = "$line" + + ((LINE_NUM++)) + + # Skip blank lines and comments + if [ -z "$line" -o "${line:0:1}" = ";" -o "${line:0:1}" = "#" ] + then + continue + fi + + # Section marker? + if [[ "${line}" =~ ^\[[a-zA-Z0-9_]{1,}\]$ ]] + then + + # Set SECTION var to name of section (strip [ and ] from section marker) + SECTION="${line#[}" + SECTION="${SECTION%]}" + eval "${INI_ALL_SECTION}=\"\${${INI_ALL_SECTION}# } $SECTION\"" + ((SECTIONS_NUM++)) + + continue + fi + + # Are we getting only a specific section? And are we currently in it? + if [ ! -z "$INI_SECTION" ] + then + if [ "$SECTION" != "$INI_SECTION" ] + then + continue + fi + fi + + # Valid var/value line? (check for variable name and then '=') + if ! [[ "${line}" =~ ^[a-zA-Z0-9._]{1,}[[:space:]]*= ]] + then + echo "Error: Invalid line:" >&2 + echo " ${LINE_NUM}: $line" >&2 + cleanup_bash + return 1 + fi + + + # split line at "=" sign + IFS="=" + read -r VAR VAL <<< "${line}" + IFS="${IFS_OLD}" + + # delete spaces around the equal sign (using extglob) + VAR="${VAR%%+([[:space:]])}" + VAL="${VAL##+([[:space:]])}" + VAR=$(echo $VAR) + + + # Construct variable name: + # ${VARNAME_PREFIX}_$SECTION_$VAR + # Or if not in a section: + # ${VARNAME_PREFIX}_$VAR + # In both cases, full stops ('.') are replaced with underscores ('_') + if [ -z "$SECTION" ] + then + VARNAME=${VARNAME_PREFIX}_${VAR//./_} + else + VARNAME=${VARNAME_PREFIX}_${SECTION}_${VAR//./_} + fi + eval "${INI_ALL_VARNAME}=\"\${${INI_ALL_VARNAME}# } ${VARNAME}\"" + + if [[ "${VAL}" =~ ^\".*\"$ ]] + then + # remove existing double quotes + VAL="${VAL##\"}" + VAL="${VAL%%\"}" + elif [[ "${VAL}" =~ ^\'.*\'$ ]] + then + # remove existing single quotes + VAL="${VAL##\'}" + VAL="${VAL%%\'}" + elif [ "$BOOLEANS" = 1 ] + then + # Value is not enclosed in quotes + # Booleans processing is switched on, check for special boolean + # values and convert + + # here we compare case insensitive because + # "shopt nocasematch" + case "$VAL" in + yes | true | on ) + VAL=1 + ;; + no | false | off ) + VAL=0 + ;; + esac + fi + + + # enclose the value in single quotes and escape any + # single quotes and backslashes that may be in the value + VAL="${VAL//\\/\\\\}" + VAL="\$'${VAL//\'/\'}'" + + eval "$VARNAME=$VAL" + done <"${INI_FILE}" + + # return also the number of parsed sections + eval "$INI_NUMSECTIONS_VARNAME=$SECTIONS_NUM" + + cleanup_bash +} + + +# < https://github.com/rudimeier/bash_ini_parser + +bes.ini(){ + read_ini $* +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bes.install(){ local path=${1:-/usr/local/bin} local done=1 @@ -179,6 +506,100 @@ bes.install(){ bes.echo.rs $done } +BES_LIB="echo" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bes.inlist () +{ + local rs=1 + if [[ "$2" =~ (^|[[:space:]])"$1"($|[[:space:]]) ]] ; then + rs=0 + fi + return $rs +} +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bes.update () +{ + bes.ini $APP_DIR/bes.ini -p bes -b 1 + + bes.echo.title "Reading Project" $APP_NAME + bes.echo.keyval path $APP_DIR + local keys="vendor name version license author" + local value="" + for key in $keys; do + value="bes_project_$key" + if [ ! -z "${!value}" ]; then + bes.echo.keyval $key "${!value}" + fi + done + + bes.ini "$APP_DIR/bes.ini" require -p bes -b 1 + local prefix="bes_require" + local key="" + if [ ! -z "${bes_ALL_VARS}" ]; then + bes.echo.title "Checking Dependencies" + for name in ${bes_ALL_VARS}; do + key=${name:${#prefix}+1} + bes.echo.keyval ${key//_/.} ${!name} + echo + local project=${key#*_} + local vendor=${key%_*} + local version=${!name} + + if [ "$vendor" = "bes" ]; then + if bes.inlist "$project" "$BES_LIB"; then + if [ ! -d "$APP_DIR/vendor/$vendor" ]; then + bes.echo.action "creating vendor directory ${Cusa}$vendor" + mkdir -p "$APP_DIR/vendor/$vendor" + else + bes.echo.action "checking vendor directory ${Cusa}$vendor" + fi + bes.echo.state $? + cd "$APP_DIR/vendor/$vendor" + bes.echo.action "updating repository $Cusa$vendor.$project ${Coff}:$Cusa $version" + if [ ! -d "$project" ]; then + git clone -q "https://git.pluie.org/meta-tech/$vendor-$project" "$project" 2>&1 >/dev/null + #~ bes.echo.state $? + cd $project + else + cd $project + git fetch --all -q 2>&1 >/dev/null + #~ bes.echo.state $? + fi + #~ bes.echo.action "checkout to version $Cusa$version" + local branch=$(git branch --no-color | grep \* | cut -d ' ' -f2-) + # branch=${branch:5: -3} + if [ "$branch" != "$version" ]; then + git checkout -q -b $version 2>&1 >/dev/null + fi + bes.echo.state $? + for entry in "$APP_DIR/vendor/$vendor/$project/src"/*.sh; do + bes.echo " ${Cspe}- ${Cok}set for autoloading ${Coff}src/$(basename $entry)" + # tail -n +2 "$entry" >> "$APP_BIN" + done + bes.echo.state $? + fi + fi + bes.echo.rs + + local req=${!name} + local path=${req#*:} + local repo=${req%:*} + local repoName=bes_repo_$repo + local repoVar=${!repoName} + local src=${!repoName}$path + #~ echo "$name : ${!name}" + #~ echo "\$req : ${req}" + #~ echo "\$repo : ${repo}" + #~ echo "\$path : ${path}" + #~ echo "\$repoName : ${repoName}" + #~ echo "\$repoVar : ${repoVar}" + #~ echo "\$src : ${src}" + done + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bes.usage(){ echo -e " ${Cusa}Usage :${Coff}\n ${Ccom}\tBuild current project (overwrite existing build) @@ -195,21 +616,25 @@ bes.usage(){ echo -e "${Coff}" } -BES_BUILD_VERSION=0.4 +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +BES_BUILD_VERSION=0.5 APP_DIR=$(pwd) APP_NAME=$(basename $(pwd)) APP_BIN=$APP_DIR/dist/$APP_NAME +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bes.main(){ if [ "$1" = "version" ] || [ "$1" = "-v" ]; then echo $BES_BUILD_VERSION else - bes.title 'bes-build' $BES_BUILD_VERSION + bes.echo.app 'bes-build' $BES_BUILD_VERSION echo if [ "$1" = "install" ] || [ "$1" = "-i" ]; then bes.install "$2" elif [ "$1" = "help" ] || [ "$1" = "-h" ]; then bes.usage + elif [ "$1" = "update" ] || [ "$1" = "-u" ]; then + bes.update elif [ -z "$1" ] || [ "$1" = "backup" ] || [ "$1" = "-b" ]; then bes.build "$1" fi diff --git a/src/build.sh b/src/build.sh index 5141f44..5a4f195 100644 --- a/src/build.sh +++ b/src/build.sh @@ -1,5 +1,6 @@ #!/bin/bash +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bes.build(){ bes.echo.title "building project" "$APP_NAME" if [ -d "$APP_DIR/src" ]; then @@ -19,6 +20,24 @@ bes.build(){ bes.echo.state $? fi echo "#!/bin/bash" > $APP_BIN + bes.echo.action "reading ${Coff}dependencies" + for vendor in "$APP_DIR/vendor/*"; do + if [ "$(basename $vendor)" != "." ] && [ "$(basename $vendor)" != ".." ]; then + local vendorName="$(basename $vendor)" + for project in "$vendor/*"; do + if [ "$(basename $project)" != "." ] && [ "$(basename $project)" != ".." ]; then + for entry in "$project/src"/*.sh; do + local vendorName="$(basename $vendor)" + local project="$(basename $(dirname $(dirname $entry)))" + bes.echo " ${Cspe}- ${Cok}appending ${Cusa}$vendorName/$project/${Coff}src/$(basename $entry)" + tail -n +2 "$APP_DIR/vendor/$vendorName/$project/src/$(basename $entry)" >> "$APP_BIN" + done + fi + done + fi + done + bes.echo.state 0 + bes.echo.action "reading ${Coff}src/" for entry in "$APP_DIR/src"/*.sh; do if [ "$(basename $entry)" != "main.sh" ]; then diff --git a/src/display.sh b/src/echo.sh similarity index 84% rename from src/display.sh rename to src/echo.sh index 89caeed..90de873 100755 --- a/src/display.sh +++ b/src/echo.sh @@ -1,10 +1,12 @@ #!/bin/bash -BES_TERM_WIDTH=105 - BES_NOCOLOR=0 - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -if [ "$BES_NOCOLOR" -eq 0 ]; then +bes.echo.boot () +{ + BES_TERM_WIDTH=${BES_TERM_WIDTH:-105} + BES_NOCOLOR=${BES_NOCOLOR:-0} + + if [ "$BES_NOCOLOR" -eq 0 ]; then Cok="\033[0;38;5;43m"; Cko="\033[0;38;5;217m" Coff="\033[m"; Ctitle="\033[1;48;5;24;1;38;5;15m" Cdone="\033[1;48;5;36;1;38;5;15m"; Cfail="\033[1;48;5;196;1;38;5;15m" @@ -15,9 +17,11 @@ if [ "$BES_NOCOLOR" -eq 0 ]; then Copt="\033[1;38;5;81m"; Csep="\033[1;38;5;241m" Cerr="\033[1;38;5;196m"; Ccom="\033[0;38;5;139m" Csection="\033[1;38;5;97m"; Caction="\033[0;38;5;37m" -fi + fi +} # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo(){ +bes.echo () +{ local msg=${1:-''} local isAction=${2:-'0'} local symbol=${3:-' *'} @@ -38,24 +42,31 @@ bes.echo(){ fi } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.action(){ +bes.echo.action () +{ bes.echo "$1" 1 } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.title(){ +bes.echo.title () +{ + echo bes.echo " ${Citem}☪ ${Csection}$1 ${Cspe}$2${Coff}" echo } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.keyval(){ +bes.echo.keyval () +{ local c=': ' if [ ! "$BES_NOCOLOR" = 1 ]; then c="$Citem: ${Cval}" fi - bes.echo " $1 $c$2" 1 " " + local len="%-15s " +# printf "%s %s [UP]\n" $PROC_NAME "${line:${#PROC_NAME}}" + bes.echo "$(printf $len $1) $c$2 " 1 " " } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.state(){ +bes.echo.state () +{ local len=8 printf "%0.s " $(seq 1 $(($BES_TERM_WIDTH-${len}))) if [ "$1" = 0 ]; then @@ -65,7 +76,8 @@ bes.echo.state(){ fi } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.rs(){ +bes.echo.rs () +{ local rs=${1:-0} if [ "$rs" -eq 0 ]; then echo -e " ${Cdone} done ${Coff}" @@ -74,11 +86,13 @@ bes.echo.rs(){ fi } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.echo.error(){ +bes.echo.error () +{ echo -e "\n${Cerr} error : ${Coff}\n\t$1 ${Coff}\n" } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.sepline(){ +bes.echo.sepline () +{ local char=${1:-'_'} local width=${2:-$BES_TERM_WIDTH} echo -ne "${Csep} " @@ -86,7 +100,8 @@ bes.sepline(){ echo -e "${Coff}" } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.title(){ +bes.echo.app () +{ local msg=${1:-''} local version=${2:-''} local author=${3:-'a-Sansara'} @@ -94,14 +109,15 @@ bes.title(){ msg="$msg ${Cval}v$version" fi local len="$1${version}license : GNU GPL v3 author:$author" - bes.sepline + bes.echo.sepline echo -ne "\n $Ctitle $msg $Coff" printf "%0.s " $(seq 1 $(($BES_TERM_WIDTH-${#len}-15))) echo -e " ${Cmeta}license : ${Coff}GNU GPL v3 ${Cmeta}author : ${Cval}$author" - bes.sepline + bes.echo.sepline } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bes.color.map(){ +bes.echo.colormap () +{ for fgbg in 38 48 ; do for color in {0..256} ; do echo -en "\e[${fgbg};5;${color}m ${color}\t\e[0m" @@ -112,3 +128,5 @@ bes.color.map(){ echo done } +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bes.echo.boot diff --git a/src/ini.sh b/src/ini.sh new file mode 100644 index 0000000..01f3f59 --- /dev/null +++ b/src/ini.sh @@ -0,0 +1,290 @@ +#!/bin/bash + +# bes alter '__' to '_' +# +# > https://github.com/rudimeier/bash_ini_parser +# +# Copyright (c) 2009 Kevin Porter / Advanced Web Construction Ltd +# (http://coding.tinternet.info, http://webutils.co.uk) +# Copyright (c) 2010-2014 Ruediger Meier +# (https://github.com/rudimeier/) +# +# License: BSD-3-Clause, see LICENSE file +# +# Simple INI file parser. +# +# See README for usage. +# +# +function read_ini() +{ + # Be strict with the prefix, since it's going to be run through eval + function check_prefix() + { + if ! [[ "${VARNAME_PREFIX}" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] ;then + echo "read_ini: invalid prefix '${VARNAME_PREFIX}'" >&2 + return 1 + fi + } + + function check_ini_file() + { + if [ ! -r "$INI_FILE" ] ;then + echo "read_ini: '${INI_FILE}' doesn't exist or not" \ + "readable" >&2 + return 1 + fi + } + + # enable some optional shell behavior (shopt) + function pollute_bash() + { + if ! shopt -q extglob ;then + SWITCH_SHOPT="${SWITCH_SHOPT} extglob" + fi + if ! shopt -q nocasematch ;then + SWITCH_SHOPT="${SWITCH_SHOPT} nocasematch" + fi + shopt -q -s ${SWITCH_SHOPT} + } + + # unset all local functions and restore shopt settings before returning + # from read_ini() + function cleanup_bash() + { + shopt -q -u ${SWITCH_SHOPT} + unset -f check_prefix check_ini_file pollute_bash cleanup_bash + } + + local INI_FILE="" + local INI_SECTION="" + + # {{{ START Deal with command line args + + # Set defaults + local BOOLEANS=1 + local VARNAME_PREFIX=INI + local CLEAN_ENV=0 + + # {{{ START Options + + # Available options: + # --boolean Whether to recognise special boolean values: ie for 'yes', 'true' + # and 'on' return 1; for 'no', 'false' and 'off' return 0. Quoted + # values will be left as strings + # Default: on + # + # --prefix=STRING String to begin all returned variables with (followed by '_'). + # Default: INI + # + # First non-option arg is filename, second is section name + + while [ $# -gt 0 ] + do + + case $1 in + + --clean | -c ) + CLEAN_ENV=1 + ;; + + --booleans | -b ) + shift + BOOLEANS=$1 + ;; + + --prefix | -p ) + shift + VARNAME_PREFIX=$1 + ;; + + * ) + if [ -z "$INI_FILE" ] + then + INI_FILE=$1 + else + if [ -z "$INI_SECTION" ] + then + INI_SECTION=$1 + fi + fi + ;; + + esac + + shift + done + + if [ -z "$INI_FILE" ] && [ "${CLEAN_ENV}" = 0 ] ;then + echo -e "Usage: read_ini [-c] [-b 0| -b 1]] [-p PREFIX] FILE"\ + "[SECTION]\n or read_ini -c [-p PREFIX]" >&2 + cleanup_bash + return 1 + fi + + if ! check_prefix ;then + cleanup_bash + return 1 + fi + + local INI_ALL_VARNAME="${VARNAME_PREFIX}_ALL_VARS" + local INI_ALL_SECTION="${VARNAME_PREFIX}_ALL_SECTIONS" + local INI_NUMSECTIONS_VARNAME="${VARNAME_PREFIX}_NUMSECTIONS" + if [ "${CLEAN_ENV}" = 1 ] ;then + eval unset "\$${INI_ALL_VARNAME}" + fi + unset ${INI_ALL_VARNAME} + unset ${INI_ALL_SECTION} + unset ${INI_NUMSECTIONS_VARNAME} + + if [ -z "$INI_FILE" ] ;then + cleanup_bash + return 0 + fi + + if ! check_ini_file ;then + cleanup_bash + return 1 + fi + + # Sanitise BOOLEANS - interpret "0" as 0, anything else as 1 + if [ "$BOOLEANS" != "0" ] + then + BOOLEANS=1 + fi + + + # }}} END Options + + # }}} END Deal with command line args + + local LINE_NUM=0 + local SECTIONS_NUM=0 + local SECTION="" + + # IFS is used in "read" and we want to switch it within the loop + local IFS=$' \t\n' + local IFS_OLD="${IFS}" + + # we need some optional shell behavior (shopt) but want to restore + # current settings before returning + local SWITCH_SHOPT="" + pollute_bash + + while read -r line || [ -n "$line" ] + do +#echo line = "$line" + + ((LINE_NUM++)) + + # Skip blank lines and comments + if [ -z "$line" -o "${line:0:1}" = ";" -o "${line:0:1}" = "#" ] + then + continue + fi + + # Section marker? + if [[ "${line}" =~ ^\[[a-zA-Z0-9_]{1,}\]$ ]] + then + + # Set SECTION var to name of section (strip [ and ] from section marker) + SECTION="${line#[}" + SECTION="${SECTION%]}" + eval "${INI_ALL_SECTION}=\"\${${INI_ALL_SECTION}# } $SECTION\"" + ((SECTIONS_NUM++)) + + continue + fi + + # Are we getting only a specific section? And are we currently in it? + if [ ! -z "$INI_SECTION" ] + then + if [ "$SECTION" != "$INI_SECTION" ] + then + continue + fi + fi + + # Valid var/value line? (check for variable name and then '=') + if ! [[ "${line}" =~ ^[a-zA-Z0-9._]{1,}[[:space:]]*= ]] + then + echo "Error: Invalid line:" >&2 + echo " ${LINE_NUM}: $line" >&2 + cleanup_bash + return 1 + fi + + + # split line at "=" sign + IFS="=" + read -r VAR VAL <<< "${line}" + IFS="${IFS_OLD}" + + # delete spaces around the equal sign (using extglob) + VAR="${VAR%%+([[:space:]])}" + VAL="${VAL##+([[:space:]])}" + VAR=$(echo $VAR) + + + # Construct variable name: + # ${VARNAME_PREFIX}_$SECTION_$VAR + # Or if not in a section: + # ${VARNAME_PREFIX}_$VAR + # In both cases, full stops ('.') are replaced with underscores ('_') + if [ -z "$SECTION" ] + then + VARNAME=${VARNAME_PREFIX}_${VAR//./_} + else + VARNAME=${VARNAME_PREFIX}_${SECTION}_${VAR//./_} + fi + eval "${INI_ALL_VARNAME}=\"\${${INI_ALL_VARNAME}# } ${VARNAME}\"" + + if [[ "${VAL}" =~ ^\".*\"$ ]] + then + # remove existing double quotes + VAL="${VAL##\"}" + VAL="${VAL%%\"}" + elif [[ "${VAL}" =~ ^\'.*\'$ ]] + then + # remove existing single quotes + VAL="${VAL##\'}" + VAL="${VAL%%\'}" + elif [ "$BOOLEANS" = 1 ] + then + # Value is not enclosed in quotes + # Booleans processing is switched on, check for special boolean + # values and convert + + # here we compare case insensitive because + # "shopt nocasematch" + case "$VAL" in + yes | true | on ) + VAL=1 + ;; + no | false | off ) + VAL=0 + ;; + esac + fi + + + # enclose the value in single quotes and escape any + # single quotes and backslashes that may be in the value + VAL="${VAL//\\/\\\\}" + VAL="\$'${VAL//\'/\'}'" + + eval "$VARNAME=$VAL" + done <"${INI_FILE}" + + # return also the number of parsed sections + eval "$INI_NUMSECTIONS_VARNAME=$SECTIONS_NUM" + + cleanup_bash +} + + +# < https://github.com/rudimeier/bash_ini_parser + +bes.ini(){ + read_ini $* +} diff --git a/src/install.sh b/src/install.sh index 1920cea..a842032 100644 --- a/src/install.sh +++ b/src/install.sh @@ -1,5 +1,6 @@ #!/bin/bash +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bes.install(){ local path=${1:-/usr/local/bin} local done=1 diff --git a/src/main.sh b/src/main.sh index 7a4384b..7816fee 100755 --- a/src/main.sh +++ b/src/main.sh @@ -1,20 +1,24 @@ #!/bin/bash -BES_BUILD_VERSION=0.4 +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +BES_BUILD_VERSION=0.5 APP_DIR=$(pwd) APP_NAME=$(basename $(pwd)) APP_BIN=$APP_DIR/dist/$APP_NAME +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bes.main(){ if [ "$1" = "version" ] || [ "$1" = "-v" ]; then echo $BES_BUILD_VERSION else - bes.title 'bes-build' $BES_BUILD_VERSION + bes.echo.app 'bes-build' $BES_BUILD_VERSION echo if [ "$1" = "install" ] || [ "$1" = "-i" ]; then bes.install "$2" elif [ "$1" = "help" ] || [ "$1" = "-h" ]; then bes.usage + elif [ "$1" = "update" ] || [ "$1" = "-u" ]; then + bes.update elif [ -z "$1" ] || [ "$1" = "backup" ] || [ "$1" = "-b" ]; then bes.build "$1" fi diff --git a/src/update.sh b/src/update.sh new file mode 100644 index 0000000..221413c --- /dev/null +++ b/src/update.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +BES_LIB="echo" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bes.inlist () +{ + local rs=1 + if [[ "$2" =~ (^|[[:space:]])"$1"($|[[:space:]]) ]] ; then + rs=0 + fi + return $rs +} +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bes.update () +{ + bes.ini $APP_DIR/bes.ini -p bes -b 1 + + bes.echo.title "Reading Project" $APP_NAME + bes.echo.keyval path $APP_DIR + local keys="vendor name version license author" + local value="" + for key in $keys; do + value="bes_project_$key" + if [ ! -z "${!value}" ]; then + bes.echo.keyval $key "${!value}" + fi + done + + bes.ini "$APP_DIR/bes.ini" require -p bes -b 1 + local prefix="bes_require" + local key="" + if [ ! -z "${bes_ALL_VARS}" ]; then + bes.echo.title "Checking Dependencies" + for name in ${bes_ALL_VARS}; do + key=${name:${#prefix}+1} + bes.echo.keyval ${key//_/.} ${!name} + echo + local project=${key#*_} + local vendor=${key%_*} + local version=${!name} + + if [ "$vendor" = "bes" ]; then + if bes.inlist "$project" "$BES_LIB"; then + if [ ! -d "$APP_DIR/vendor/$vendor" ]; then + bes.echo.action "creating vendor directory ${Cusa}$vendor" + mkdir -p "$APP_DIR/vendor/$vendor" + else + bes.echo.action "checking vendor directory ${Cusa}$vendor" + fi + bes.echo.state $? + cd "$APP_DIR/vendor/$vendor" + bes.echo.action "updating repository $Cusa$vendor.$project ${Coff}:$Cusa $version" + if [ ! -d "$project" ]; then + git clone -q "https://git.pluie.org/meta-tech/$vendor-$project" "$project" 2>&1 >/dev/null + #~ bes.echo.state $? + cd $project + else + cd $project + git fetch --all -q 2>&1 >/dev/null + #~ bes.echo.state $? + fi + #~ bes.echo.action "checkout to version $Cusa$version" + local branch=$(git branch --no-color | grep \* | cut -d ' ' -f2-) + # branch=${branch:5: -3} + if [ "$branch" != "$version" ]; then + git checkout -q -b $version 2>&1 >/dev/null + fi + bes.echo.state $? + for entry in "$APP_DIR/vendor/$vendor/$project/src"/*.sh; do + bes.echo " ${Cspe}- ${Cok}set for autoloading ${Coff}src/$(basename $entry)" + # tail -n +2 "$entry" >> "$APP_BIN" + done + bes.echo.state $? + fi + fi + bes.echo.rs + + local req=${!name} + local path=${req#*:} + local repo=${req%:*} + local repoName=bes_repo_$repo + local repoVar=${!repoName} + local src=${!repoName}$path + #~ echo "$name : ${!name}" + #~ echo "\$req : ${req}" + #~ echo "\$repo : ${repo}" + #~ echo "\$path : ${path}" + #~ echo "\$repoName : ${repoName}" + #~ echo "\$repoVar : ${repoVar}" + #~ echo "\$src : ${src}" + done + fi +} diff --git a/src/usage.sh b/src/usage.sh index a4f50cc..7a6d64a 100644 --- a/src/usage.sh +++ b/src/usage.sh @@ -1,5 +1,6 @@ #!/bin/bash +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bes.usage(){ echo -e " ${Cusa}Usage :${Coff}\n ${Ccom}\tBuild current project (overwrite existing build)