diff --git a/README.md b/README.md index 888aa97..14cc6e3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The `autodoc` image [available on Docker Hub](https://hub.docker.com/r/ldericher 01. Install [Docker CE](https://docs.docker.com/install/) -01. Clone or download the `autodoc` repository, open a terminal inside the [example_docs](https://github.com/ldericher/autodoc/tree/master/example_docs) directory +01. Clone or download the `autodoc` repository, open a terminal inside the [examples](https://github.com/ldericher/autodoc/tree/master/examples) directory 01. Deploy an `autodoc` container: @@ -93,12 +93,12 @@ You may combine build instruction systems to your liking. However, Makefiles must contain a SRCPAT annotation comment as follows, where `` is a source pattern as above. ```Makefile -#@SRCPAT +#%SRCPAT% ``` If there are multiple SRCPAT annotations, the lowermost one will be used. -You *may* add a PHONY target "autodoc" which will be built *instead* of the default target. +You *may* add a PHONY target "autodoc" which will be built *instead* of the default target. This is demonstrated in [examples/automatic directory listing/a directory in space/Makefile](https://github.com/ldericher/autodoc/blob/develop/examples/automatic%20directory%20listing/a%20directory%20in%20space/Makefile). ```Makefile .PHONY: autodoc diff --git a/build/Dockerfile b/build/Dockerfile index 0c0b8db..c014a7a 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -4,7 +4,6 @@ RUN apt-get update && apt-get -y install \ inotify-tools \ && rm -rf /var/lib/apt/lists/* -COPY autodoc.sh /usr/local/bin/autodoc -RUN chmod +x /usr/local/bin/autodoc +COPY usr /usr CMD ["autodoc"] diff --git a/build/autodoc.sh b/build/autodoc.sh deleted file mode 100755 index 312e036..0000000 --- a/build/autodoc.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash - -# $1:WATCHROOT (default: ".") -g_watchroot="$(readlink -f "${1:-.}")" - -# compile using bare make command -do_make() { # $1:DIR $2:MAKEFILE $3:OBJECT - # extract params - local dir="$1" - local makefile="$2" - local object="$3" - - # check Makefile 'source pattern' - local srcpat="$(grep -E "^#@SRCPAT" "${dir}/${makefile}" | tail -n 1 | sed -r "s/^#@SRCPAT\s+//")" - - if [ -z "${srcpat}" ]; then - echo -n "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 - echo "Running 'make'. " - else - echo "Making '${target}'. " - fi - - make --no-print-directory -C "${dir}" -j ${target} - - else - echo -n "SRCPAT '${srcpat}' mismatch. " - return 1 - fi - - return 0 -} - -# compile a directory -do_compile() { # $1:DIR $2:OBJECT $3:DONE - # extract params - local dir="$1" - local object="$2" - local done="${3:-0}" - - # build systems - - if [ -r "${dir}/Makefile" ]; then - # Makefile found - echo -n "Found '${dir}/Makefile': " - do_make "${dir}" "Makefile" "${object}" \ - && local done="1" - fi - - # 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 - echo "No build instructions found!" - fi -} - -# process an inotify event -do_handle() { # $1:FLAGS $2: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 - echo -n "'${object}': '${flags}' in '${dir}'. " - do_compile "${dir}" "${object}" -} - -# -# MAIN -# - -echo "Booting ${0} 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 FILE; do - do_handle ${FILE} -done diff --git a/build/usr/local/bin/autodoc b/build/usr/local/bin/autodoc new file mode 100755 index 0000000..6c92403 --- /dev/null +++ b/build/usr/local/bin/autodoc @@ -0,0 +1,37 @@ +#!/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}/plugins/"*".sh" +source "${g_lib}/handle_inotify" + +# +# 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 new file mode 100644 index 0000000..bbe9c83 --- /dev/null +++ b/build/usr/local/lib/autodoc/globals @@ -0,0 +1,4 @@ +#!/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 new file mode 100644 index 0000000..63e7f94 --- /dev/null +++ b/build/usr/local/lib/autodoc/handle_inotify @@ -0,0 +1,72 @@ +#!/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/logging b/build/usr/local/lib/autodoc/logging new file mode 100644 index 0000000..802d576 --- /dev/null +++ b/build/usr/local/lib/autodoc/logging @@ -0,0 +1,12 @@ +#!/bin/bash + +g_logline="" + +logline_append() { # $STRING + g_logline="${g_logline}${1} " +} + +logline_flush() { + echo "${g_logline}" + g_logline="" +} diff --git a/build/usr/local/lib/autodoc/plugins/make.sh b/build/usr/local/lib/autodoc/plugins/make.sh new file mode 100644 index 0000000..6ee1ef8 --- /dev/null +++ b/build/usr/local/lib/autodoc/plugins/make.sh @@ -0,0 +1,45 @@ +#!/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 d89ccb7..e51149f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,5 +14,6 @@ services: command: "bash" volumes: - - "${PWD}/build/autodoc.sh:/usr/local/bin/autodoc:ro" - - "${PWD}/example_docs:/docs" + - "${PWD}/build/usr/local/bin/autodoc:/usr/local/bin/autodoc:ro" + - "${PWD}/build/usr/local/lib/autodoc:/usr/local/lib/autodoc:ro" + - "${PWD}/examples:/docs" diff --git a/example_docs/somedir/a directory in space/Makefile b/example_docs/somedir/a directory in space/Makefile deleted file mode 100644 index e435afe..0000000 --- a/example_docs/somedir/a directory in space/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -#@SRCPAT .* - -.PHONY: all -all: - @echo "Hello World!" diff --git a/example_docs/somedir/.gitignore b/examples/automatic directory listing/.gitignore similarity index 100% rename from example_docs/somedir/.gitignore rename to examples/automatic directory listing/.gitignore diff --git a/example_docs/somedir/Makefile b/examples/automatic directory listing/Makefile similarity index 74% rename from example_docs/somedir/Makefile rename to examples/automatic directory listing/Makefile index 52668e7..a7b1d27 100644 --- a/example_docs/somedir/Makefile +++ b/examples/automatic directory listing/Makefile @@ -1,4 +1,4 @@ -#@SRCPAT (file|\.tex)$ +#%SRCPAT% (file|\.tex)$ .PHONY: all all: files.txt diff --git a/example_docs/somedir/a directory in space/Fishfile b/examples/automatic directory listing/a directory in space/Fishfile similarity index 100% rename from example_docs/somedir/a directory in space/Fishfile rename to examples/automatic directory listing/a directory in space/Fishfile diff --git a/examples/automatic directory listing/a directory in space/Makefile b/examples/automatic directory listing/a directory in space/Makefile new file mode 100644 index 0000000..c655049 --- /dev/null +++ b/examples/automatic directory listing/a directory in space/Makefile @@ -0,0 +1,9 @@ +#%SRCPAT% .* + +.PHONY: all +all: + @echo "Hello World!" + +.PHONY: autodoc +autodoc: + @echo "Hello autodoc!" diff --git a/example_docs/somedir/fake latex file.tex b/examples/automatic directory listing/fake latex file.tex similarity index 100% rename from example_docs/somedir/fake latex file.tex rename to examples/automatic directory listing/fake latex file.tex diff --git a/example_docs/simple md/.gitignore b/examples/simple md/.gitignore similarity index 100% rename from example_docs/simple md/.gitignore rename to examples/simple md/.gitignore diff --git a/example_docs/simple md/Makefile b/examples/simple md/Makefile similarity index 91% rename from example_docs/simple md/Makefile rename to examples/simple md/Makefile index 5042f01..6902e49 100644 --- a/example_docs/simple md/Makefile +++ b/examples/simple md/Makefile @@ -1,4 +1,4 @@ -#@SRCPAT \.md$ +#%SRCPAT% \.md$ .PHONY: all all: simple.pdf simple.html diff --git a/example_docs/simple md/simple.md b/examples/simple md/simple.md similarity index 100% rename from example_docs/simple md/simple.md rename to examples/simple md/simple.md diff --git a/example_docs/unrelated file.txt b/examples/unbuildable file.txt similarity index 100% rename from example_docs/unrelated file.txt rename to examples/unbuildable file.txt