#! /bin/sh set -euf ## SYNOPSIS # [debug=true] build compile SRCFILE DSTFILE # [debug=true] build deps SRCFILE... build() { case "$1" in compile) build_compile "$2" "$3";; deps) shift; build_deps "$@";; *) echo "build: $1: bad command" >&2; return 23;; esac } ## usage: init init() { directive_pattern='s:^ *\\([0-9]\\+\\) \1$:\2 \3:' input_parser="$(make_input_parser "$0")" debug_script input_parser '$0' build_directives="$(make_build_directives "$0")" debug_script build_directives '$0' build_x_directive_loader="$(make_build_x_directive_loader "$0")" debug_script build_x_directive_loader '$0' eval "$build_x_directive_loader" needs_compilation_sentinel="$(make_needs_compilation_sentinel $build_directives)" debug_script needs_compilation_sentinel 'build_directives' } # usage: debug_script VARNAME [DESCRIPTION] debug_script() { if test "${debug-false}" = true; then printf '====== %s%s\n%s\n' \ "$1" \ "${2+" ($2)"}" \ "$(eval echo \"\$$1\" | nl -b a)" >&2 fi } ## usage: #@include \([0-9A-Za-z]\+\) -> build_include \1 \2 build_include() { cat< build_info \1 build_info() { cat< "$2" chmod +x "$2" } ## usage: needs_compilation SHELLSCRIPT # Returns true if SRCFILE contains compilation directives. needs_compilation() { echo "$1" | grep -q "$needs_compilation_sentinel" } ## usage: make_sedscript_maker_shellscript SRCFILE # Print a shellscript that creates a sedscript that resolves all the build # directives in SRCFILE. make_sedscript_maker_shellscript() { sedscript_generator="$(echo "$1" | nl -b a -s ' ' | sed "$input_parser")" debug_script sedscript_generator 'sed input_parser srcfile' sedscript="$(eval "$sedscript_generator")" debug_script sedscript 'eval sedscript_generator' echo "$sedscript" } ## usage: build_deps SRCFILE... # Print all the dependencies of SRCFILE... (in alphabetic order) to stdout. build_deps() { while test $# -gt 0; do deps="$( for f; do for d in $(sed -n 's:^'"$build_include_directive"'$:\1:p' "$f"); do build_resolve $d done done )" set -- $deps if test $# -gt 0; then echo "$deps" fi done | sort | uniq } ## usage: build_resolve LIBNAME build_resolve() { echo "$BUILD_PATH" | tr : \\n | xargs -I: printf '%s/%s\n' : "$1" | xargs -I: ls -d : 2>/dev/null | head -n 1 | grep . || { echo "build resolve: $1: library not found" >&2 return 23 } } ## usage: make_input_parser FILENAME make_input_parser() { echo "$(sed -n ' s/^## usage: \(.*\) -> \([^ ]\+\) \(.*\)$/s:^ *\\([0-9]\\+\\) \1$:\2 \3:/p $a\ t;s:^ *\\([0-9]\\+\\) .*:echo \\1p: ' "$1")" } ## usage: make_build_directives FILENAME make_build_directives() { echo "$(sed -n ' s/^## usage: \(.*\) -> \([^ ]\+\) \(.*\)$/\2_directive/p ' "$0")" } ## usage: make_build_x_directive_loader FILENAME make_build_x_directive_loader() { sed -n ' s/^## usage: \(.*\) -> \([^ ]\+\) \(.*\)$/\2_directive='"'"'\1'"'"'/p ' "$1" } ## usage: make_needs_compilation_sentinel BUILD_DIRECTIVES... make_needs_compilation_sentinel() { echo "^\\($( for directive; do eval echo \"\$$directive\" done | tr \\n \| | sed 's/|/\\|/' )\\)$" } ## main init build "$@"