diff --git a/build/usr/local/bin/autodoc b/build/usr/local/bin/autodoc deleted file mode 100755 index 03b9c00..0000000 --- a/build/usr/local/bin/autodoc +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# -# hard globals -# - -g_bin="$(readlink -f "$(which "$0")")" -g_lib=${g_bin/"bin"/"lib"} -declare -a g_build_systems -declare -A g_build_systems_glob - -# -# load base program -# - -source "${g_lib}/globals" -source "${g_lib}/logging" -source "${g_lib}/handle_inotify" - -for plugin in "${g_lib}/plugins/"*".sh"; do - source "${plugin}" -done - -# -# MAIN -# - -echo "Booting '${g_bin}' in '${g_watchroot}'." -# setup inotify: -# -mrq monitor, recursive, quiet -# -e events -# --format %e eventlist csv, %w workdir, %f filename -inotifywait -mrq \ - -e create -e delete -e moved_to -e close_write \ - --format '%e %w%f' \ - "${g_watchroot}" | \ -\ -while read NOTIFICATION; do - do_handle ${NOTIFICATION} -done diff --git a/build/usr/local/lib/autodoc/globals b/build/usr/local/lib/autodoc/globals deleted file mode 100644 index bbe9c83..0000000 --- a/build/usr/local/lib/autodoc/globals +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -# $WATCHROOT (default: ".") -g_watchroot="$(readlink -f "${1:-.}")" diff --git a/build/usr/local/lib/autodoc/handle_inotify b/build/usr/local/lib/autodoc/handle_inotify deleted file mode 100644 index 63e7f94..0000000 --- a/build/usr/local/lib/autodoc/handle_inotify +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -# process an inotify event -do_handle() { # $FLAGS $OBJECT - # extract params - local flags="$1" - shift 1 - local dir="$(dirname "$*")" - local object="$(basename "$*")" - - if [[ "${flags}" =~ "ISDIR" ]]; then - # object refers to directory - local dir="${dir}/${object}" - local object="." - fi - - # start using toolchain - logline_append "'${object}': '${flags}' in '${dir}'." - do_compile "${dir}" "${object}" - logline_flush -} - -# compile an OBJECT using build instructions found in DIRectory -do_compile() { # $DIR $OBJECT $DONE - # extract params - local dir="$1" - local object="$2" - local done="${3:-0}" - - # build systems - for build_system in ${g_build_systems[@]}; do - do_build_system "${dir}" "${object}" "${build_system}" \ - && local done="1" - done - - # never leave $g_watchroot - if [ "${dir}" != "${g_watchroot}" ]; then - # search parent dir for more build instructions - do_compile "$(dirname "${dir}")" "${object}" "${done}" - - elif [ "${done}" == "0" ]; then - # hit $g_watchroot - logline_append "Not a source file." - fi -} - -# use given BUILD_SYSTEM to process an OBJECT from a DIRectory -do_build_system() { # $DIR $OBJECT $BUILD_SYSTEM - # extract params - local dir="$1" - local object="$2" - local build_system="$3" - - # not done yet - local result=1 - - # get glob pattern for plugin - for glob in ${g_build_systems_glob[${build_system}]}; do - # match glob in directory - for file in "${dir}"/${glob}; do - # check file readability - if [ -r "${file}" ]; then - # actually call into build system - logline_append "Found '${file}':" - do_${build_system} "${dir}" "${object}" "$(basename "${file}")" \ - && local result=0 - fi - done - done - - return ${result} -} diff --git a/build/usr/local/lib/autodoc/plugins/make.sh b/build/usr/local/lib/autodoc/plugins/make.sh deleted file mode 100644 index 6ee1ef8..0000000 --- a/build/usr/local/lib/autodoc/plugins/make.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# plugin name -g_build_systems+=(make) - -# build instruction file globs for this plugin -g_build_systems_glob[make]="Makefile *.mk" - -# compile using bare make command -do_make() { # $DIR $OBJECT $MAKEFILE - # extract params - local dir="$1" - local object="$2" - local makefile="$3" - - # check Makefile 'source pattern' - local srcpat="$(grep -E "^#%SRCPAT%" "${dir}/${makefile}" | tail -n 1 | sed -r "s/^#%SRCPAT%\s+//")" - - if [ -z "${srcpat}" ]; then - logline_append "Empty source pattern, check '#%SRCPAT%' annotation!" - return 1 - - elif [[ "${object}" =~ ${srcpat} ]]; then - # check for autodoc target - local target="$(grep -E "^autodoc:" "${dir}/${makefile}" | sed -r "s/:.*$//")" - - if [ -z "${target}" ]; then - logline_append "Running 'make'!" - else - logline_append "Making '${target}'!" - fi - - # actually run "make" and save (truncated) output - local makelog="$(make --no-print-directory -C "${dir}" -f "${makefile}" -j ${target})" - logline_append "$(echo "${makelog}" | head -n 10 | sed 's/$/;/g' | tr '\n' ' ' | sed 's/ *$//')" - - logline_append "Done." - - else - logline_append "SRCPAT '${srcpat}' mismatch." - return 1 - fi - - return 0 -} diff --git a/docker-compose.yml b/docker-compose.yml index e51149f..cad3529 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,11 +9,11 @@ services: image: ldericher/autodoc build: - context: ./build + context: ./src command: "bash" volumes: - - "${PWD}/build/usr/local/bin/autodoc:/usr/local/bin/autodoc:ro" - - "${PWD}/build/usr/local/lib/autodoc:/usr/local/lib/autodoc:ro" + - "${PWD}/src/usr/local/bin/autodoc:/usr/local/bin/autodoc:ro" + - "${PWD}/src/usr/local/lib/autodoc:/usr/local/lib/autodoc:ro" - "${PWD}/examples:/docs" diff --git a/build/Dockerfile b/src/Dockerfile similarity index 86% rename from build/Dockerfile rename to src/Dockerfile index c014a7a..c74c5d8 100644 --- a/build/Dockerfile +++ b/src/Dockerfile @@ -6,4 +6,4 @@ RUN apt-get update && apt-get -y install \ COPY usr /usr -CMD ["autodoc"] +CMD ["autodoc", "-bw"] diff --git a/src/usr/local/bin/autodoc b/src/usr/local/bin/autodoc new file mode 100755 index 0000000..3be9df9 --- /dev/null +++ b/src/usr/local/bin/autodoc @@ -0,0 +1,45 @@ +#!/bin/bash + +# +# hard globals +# + +g_bin="$(readlink -f "$(which "$0")")" +g_lib=${g_bin/"bin"/"lib"} + +# +# load base program +# + +source "${g_lib}/globals" +source "${g_lib}/logging" +source "${g_lib}/main" + +for plugin in "${g_lib}/plugins/"*".sh"; do + source "${plugin}" +done + +# +# MAIN +# + +# show debug info +if [ ${g_verbose} -eq 1 ]; then + logline_append "Variables:" + logline_append "build:${g_build}" + logline_append "watch:${g_watch}" + logline_append "root:${g_root}" + logline_flush +fi + +if [ ${g_build} -eq 1 ]; then + echo "Building everything in '${g_root}'." + do_build_all +fi + +if [ ${g_watch} -eq 1 ]; then + echo "Watching '${g_root}'." + do_build_watch +fi + +echo "Done." diff --git a/src/usr/local/lib/autodoc/globals b/src/usr/local/lib/autodoc/globals new file mode 100644 index 0000000..595aba1 --- /dev/null +++ b/src/usr/local/lib/autodoc/globals @@ -0,0 +1,67 @@ +#!/bin/bash + +# array of available build systems +declare -a g_build_systems + +# map of file globs for build systems +declare -A g_build_systems_glob + +# map of annotation patterns for build systems +declare -A g_build_systems_annotations + +# simple help page +do_show_help() { + echo "Usage: ${0} [-h?bwv] [-r ROOT] [ROOT]" + echo + echo "Options:" + echo " -h, -? Show this help and exit" + echo " -b Build ROOT directory on startup" + echo " -w Keep watching ROOT directory for changes" + echo " -v Verbose output" + echo " -r ROOT Set ROOT directory" + echo + echo "ROOT directory can be set via '-r' argument or positionally." + echo "If ROOT directory is undefined, it defaults to the current working directory." + exit 0 +} + +# reset in case getopts has been used previously in the shell +OPTIND=1 + +# initialize variables +g_build=0 +g_watch=0 +g_root="" + +while getopts "h?bwvr:" opt; do + case "$opt" in + h|\?) + do_show_help + ;; + b) + g_build=1 + ;; + w) + g_watch=1 + ;; + v) + g_verbose=1 + ;; + r) + g_root="${OPTARG}" + ;; + esac +done + +# default to help +[ ${g_build} -eq 0 ] && [ ${g_watch} -eq 0 ] && [ ${g_verbose} -eq 0 ] && do_show_help + +# shift off getopts parsed options +shift $((OPTIND-1)) +[ "${1:-}" = "--" ] && shift + +# if g_root undefined by getopt, +[ -z "${g_root:-}" ] && g_root="$1" + +# get actual $ROOT directory (default: ".") +g_root="$(readlink -f "${g_root:-.}")" \ No newline at end of file diff --git a/build/usr/local/lib/autodoc/logging b/src/usr/local/lib/autodoc/logging similarity index 100% rename from build/usr/local/lib/autodoc/logging rename to src/usr/local/lib/autodoc/logging diff --git a/src/usr/local/lib/autodoc/main b/src/usr/local/lib/autodoc/main new file mode 100644 index 0000000..d9b874b --- /dev/null +++ b/src/usr/local/lib/autodoc/main @@ -0,0 +1,146 @@ +#!/bin/bash + +# process an inotify event +do_handle_inotify() { # $FLAGS $OBJECT + # extract params + local flags="$1" + shift 1 + local dir="$(dirname "$*")" + local object="$(basename "$*")" + + if [[ "${flags}" =~ "ISDIR" ]]; then + # object refers to directory + local dir="${dir}/${object}" + local object="." + fi + + # start using toolchain + logline_append "'${object}': '${flags}' in '${dir}'." + do_compile "${dir}" "${object}" + logline_flush +} + +# compile an OBJECT using build instructions found in DIRectory +do_compile() { # $DIR $OBJECT $DONE + # extract params + local dir="$1" + local object="$2" + local done="${3:-0}" + + # build systems + for build_system in ${g_build_systems[@]}; do + do_build_system "${dir}" "${build_system}" "${object}" \ + && local done="1" + done + + # never leave $g_root + if [ "${dir}" != "${g_root}" ]; then + # search parent dir for more build instructions + do_compile "$(dirname "${dir}")" "${object}" "${done}" + + elif [ "${done}" == "0" ]; then + # hit $g_root + logline_append "Not a source file." + fi +} + +# check if defined source pattern matches OBJECT +do_check_srcpat() { # $DIR $BUILD_DESC $ANNOTATION $OBJECT + # extract params + local dir="$1" + local build_desc="$2" + local annotation="$3" + local object="$4" + + # check 'source pattern' + local srcpat="$(grep -E "^${annotation}" "${dir}/${build_desc}" | tail -n 1 | sed -r "s/^${annotation}\s+//")" + + if [ -z "${srcpat}" ]; then + # empty srcpat => fail + logline_append "Empty source pattern, check for '${annotation}' annotation!" + return 1 + + elif [ -z "${object}" ]; then + # empty object = "no specific object" => success + return 0 + + elif [[ "${object}" =~ ${srcpat} ]]; then + # nonempty object matches srcpat => success + return 0 + + else + # nonempty object does not match srcpat => fail + logline_append "SRCPAT '${srcpat}' mismatch." + return 1 + + fi +} + +# use given BUILD_SYSTEM to process an OBJECT from a DIRectory +do_build_system() { # $DIR $BUILD_SYSTEM $OBJECT + # extract params + local dir="$1" + local build_system="$2" + local object="$3" + + # not done yet + local result=1 + + # get glob patterns for plugin + for glob in ${g_build_systems_glob[${build_system}]}; do + # match each glob in directory + for file in "${dir}"/${glob}; do + # check file readability + if [ -r "${file}" ]; then + # actually call into build system + logline_append "Found '${file}':" + + local file="$(basename "${file}")" + do_${build_system} "${dir}" "${file}" "${object}" \ + && local result=0 + fi + done + done + + return ${result} +} + +# force build using all systems +do_build_all() { # + # build systems + for build_system in ${g_build_systems[@]}; do + echo "Build system '${build_system}'." + for glob in ${g_build_systems_glob[${build_system}]}; do + + # match each glob recursively + find "${g_root}" -iname "${glob}" | \ + while read file; do + if [ -r "${file}" ]; then + # force call into build system + logline_append "Found '${file}':" + + local dir="$(dirname "${file}")" + local file="$(basename "${file}")" + do_${build_system}_all "${dir}" "$(basename "${file}")" + + logline_flush + fi + done + done + done +} + +do_build_watch() { # + # setup inotify: + # -mrq monitor, recursive, quiet + # -e events + # --format %e eventlist csv, %w workdir, %f filename + inotifywait -mrq \ + -e create -e delete -e moved_to -e close_write \ + --format '%e %w%f' \ + "${g_root}" | \ + \ + while read NOTIFICATION; do + do_handle_inotify ${NOTIFICATION} + done +} diff --git a/src/usr/local/lib/autodoc/plugins/make.sh b/src/usr/local/lib/autodoc/plugins/make.sh new file mode 100644 index 0000000..d836faa --- /dev/null +++ b/src/usr/local/lib/autodoc/plugins/make.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# plugin name +g_build_systems+=(make) + +# build instruction file globs for this plugin +g_build_systems_glob[make]="Makefile *.mk" + +# srcpat annotation prefix for this plugin +g_build_systems_annotations[make]='#%SRCPAT%' + +# try to compile file OBJECT +do_make() { # $DIR $OBJECT $MAKEFILE + # extract params + local dir="$1" + local makefile="$2" + local object="$3" + + # only run if "object" is source file + if do_check_srcpat "${dir}" "${makefile}" "${g_build_systems_annotations[make]}" "${object}"; then + do_run_make "${dir}" "${makefile}" + fi +} + +# try running make for MAKEFILE inside DIRectory +do_make_all() { # $DIR $MAKEFILE + # extract params + local dir="$1" + local makefile="$2" + + # only run if "makefile" is relevant for autodoc + if do_check_srcpat "${dir}" "${makefile}" "${g_build_systems_annotations[make]}" ""; then + do_run_make "${dir}" "${makefile}" + fi +} + +# actually run GNU Make with MAKEFILE inside DIRectory +do_run_make() { # $DIR $MAKEFILE + # extract params + local dir="$1" + local makefile="$2" + + # check for autodoc target + local target="$(grep -E "^autodoc:" "${dir}/${makefile}" | sed -r "s/:.*$//")" + + if [ -z "${target}" ]; then + logline_append "Running 'make'!" + else + logline_append "Making '${target}'!" + fi + + # actually run "make" and save (truncated) output + local makelog="$(make --no-print-directory -C "${dir}" -f "${makefile}" -j ${target})" + logline_append "$(echo "${makelog}" | head -n 10 | sed 's/$/;/g' | tr '\n' ' ' | sed 's/ *$//')" + + logline_append "Done." +} \ No newline at end of file