summaryrefslogtreecommitdiffstats
path: root/ship/build
diff options
context:
space:
mode:
Diffstat (limited to 'ship/build')
-rwxr-xr-xship/build210
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 "$1=\"$value_script\""'
+ 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