diff options
Diffstat (limited to 'ship/build')
-rwxr-xr-x | ship/build | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/ship/build b/ship/build new file mode 100755 index 00000000..0e8e1013 --- /dev/null +++ b/ship/build @@ -0,0 +1,210 @@ +#! /bin/sh +set -euf + +## SYNOPSIS +# [debug=true] build compile SRCFILE DSTFILE +# [debug=true] build deps SRCFILE... +build() { + + ## Load macro definitions. + defmacro_pattern='## usage: \(.*\) -> \([^ ]\+\) \(.*\)' + script='s/^'"$defmacro_pattern"'$/\2_macro='"'"'\1'"'"'/p' \ + setf defmacros '$(sed -n "$script" "$%s")' 0 + eval "$defmacros" + + ## Dispatch. + case "$1" in + compile) build_compile "$2" "$3";; + deps) shift; build_deps "$@";; + *) echo "build: $1: bad command" >&2; return 23;; + esac +} + +### +### macros +### + +## usage: #@include \([0-9A-Za-z_]\+\) -> build_include \1 \2 +build_include() { + if buildcache_has "#@include:$2"; then + printf '%da\\\n##include %s: already done\n' $1 $2 + else + buildcache_add "#@include:$2" + cat<<EOF +$1a\\ +# begin $2 +$1r$(build_resolve $2) +$1a\\ +# end $2 +EOF + fi +} + +## usage: #@strict -> build_strict_mode \1 +build_strict_mode() { cat<<EOF +$1a\\ +set -euf\\ +(set -o posix 2>/dev/null) && set -o posix || : +EOF +} + +## usage: #@info -> build_info \1 +build_info() { + gitinfo=$(git describe --always --dirty --abbrev=0 2>/dev/null || :) +cat<<EOF +$1a\\ +# this file was generated by //ship/build\\ +# build date: $(date -u --rfc-3339=s)\\ +# git describe: ${gitinfo:-not under version control} +EOF +} + +## usage: #@mainifyme\( \([A-Za-z_][A-Za-z0-9_]*\)\)\? -> build_mainifyme \1 \3 +build_mainifyme() { + mainifyme_name="${2:-main}" + cat<<EOF + $1a\\ +$mainifyme_name(){ + \$a\\ +}\\ +$mainifyme_name "\$@" +EOF +} + +### +### main subroutines +### + +## usage: build_compile SRCFILE DSTFILE +build_compile() { + + script='s/^'"$defmacro_pattern"'$/\2_macro/p' \ + setf macro_names '$(sed -n "$script" "$%s")' 0 + + setf unexpanded_macros_pattern \ + '$(make_unexpanded_macros_pattern $%s)' macro_names + + script=' + s/^'"$defmacro_pattern"'$/s:^ *\\([0-9]\\+\\) \1$:\2 \3:/p + $a\ +t;s:^ *\\([0-9]\\+\\) .*:echo \\1p: + ' \ + setf input_parser '$(sed -n "$script" "$%s")' 0 + + SRCFILE="$1" setf src '$(cat "$%s")' SRCFILE + + buildcache_initialize "$2" + + while echo "$src" | grep -q "$unexpanded_macros_pattern"; do + setf sedgen '$(echo "$%s" | nl -b a -s \ | sed "$%s")' src input_parser + setf sedscript '$(eval "$%s")' sedgen + setf src '$(echo "$%s" | sed -n "$%s")' src sedscript + done + + buildcache_finalize + + echo "$src" > "$2" + chmod +x "$2" +} + +## usage: build_deps SRCFILE... +# Print all the dependencies of SRCFILE... to stdout. (alphabetic order) +build_deps() { + while test $# -gt 0; do + deps="$( + for f; do + for d in $(sed -n 's:^'"$build_include_macro"'$:\1:p' "$f"); do + build_resolve $d + done + done + )" + set -- $deps + if test $# -gt 0; then + echo "$deps" + fi + done | sort | uniq +} + +### +### misc utilities +### + +## 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_unexpanded_macros_pattern BUILD_DIRECTIVES... +make_unexpanded_macros_pattern() { + echo "^\\($( + for macro; do + eval echo \"\$$macro\" + done | + tr \\n \| | + sed 's/|/\\|/' + )\\)$" +} + +## usage: setf NAME FMT [ARG...] +setf() { + value_script="$(shift; printf "$@")" + + eval "$1=$value_script" + + if is_debug_mode; then + eval 'echo "[35m$1=\"$value_script\"[m"' + eval 'echo "'"\$$1"'"' | nl -b a + fi >&2 +} + +## usage: is_debug_mode +is_debug_mode() { + test "${debug-false}" = true +} + +### +### buildcache utilities +### + +## usage: buildcache_initialize DESTFILE +buildcache_initialize() { + buildcache="$1.buildcache" + cat /dev/null > "$buildcache" +} + +## usage: buildcache_finalize +buildcache_finalize() { + if is_debug_mode; then + rm "$buildcache" + fi +} + +## usage: buildcache_has BRE +# Check if buildcache contains a line matching BRE. +buildcache_has() { + grep -q "^$1\$" "$buildcache" +} + +## usage: buildcache_add LINE +# Add LINE to buildcache. +buildcache_add() { + echo "$1" >> "$buildcache" +} + + + +### +### main invocation +### + +if echo "$0" | grep -q '^\(.*/\)\?build$'; then + build "$@" +fi |