#!/usr/bin/env sh # # td: "Telodendria Developer" # # A helper script that operates sort of like Make, except it is # a lot easier to read and write than a Makefile, and it works on # any POSIX system. # # I chose to use a custom build script instead of using Make because # it is much more portable and flexible. This script doesn't only # handle building the code, it also handles formatting it, as well # as generating patch files. # # Set variables # # This syntax may look odd, but as far as I can tell, it is POSIX, # and it allows the values to be overridden by the environment, # such that these serve more as sane defaults than hard requirements. # : "${TELODENDRIA_VERSION:=0.2.0}" : "${CVS_TAG:=Telodendria-$(echo $TELODENDRIA_VERSION | sed 's/\./_/g')}" : "${DEFINES:=-D_DEFAULT_SOURCE -DTELODENDRIA_VERSION=\"$TELODENDRIA_VERSION\"}" : "${CC:=cc}" : "${CFLAGS:=-Wall -Wextra -pedantic -ansi -O3}" : "${STATIC:=-static -Wl,-static}" : "${LDFLAGS:=-lm -pthread -flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections}" : "${PROG:=telodendria}" . "$(pwd)/tools/lib/common.sh" CFLAGS="${CFLAGS} ${DEFINES} ${INCLUDES}" LDFLAGS="${LDFLAGS} ${STATIC}" if [ "$DEBUG" = "1" ]; then CFLAGS="$CFLAGS -O0 -g -pg" LDFLAGS="-lm -pthread -v" PROG="$PROG-debug" fi # Check the modificiation time of a file. This is used to do # incremental builds; we only want to rebuild files that have # have changed. mod_time() { if [ -n "$1" ] && [ -f "$1" ]; then case "$(uname)" in Linux|CYGWIN_NT*) stat -c %Y "$1" ;; *BSD|DragonFly) stat -f %m "$1" ;; *) # Platform unknown, force rebuilding the whole # project every time. echo "0" ;; esac else echo "0" fi } # Substitute shell variables in a stream with their actual value # in this shell. setsubst() { SED="/tmp/sed-$RANDOM.txt" ( set | while IFS='=' read -r var val; do val=$(echo "$val" | cut -d "'" -f 2- | rev | cut -d "'" -f 2- | rev) echo "s|\\\${$var}|$val|g" done echo "s|\\\${[a-zA-Z_]*}||g" echo "s|'''|'|g" ) > "$SED" sed -f "$SED" $@ rm "$SED" } # Build the source code, and generate the 'build/telodendria' # binary. recipe_build() { cd src mkdir -p ../build echo "CC = ${CC}" echo "CFLAGS = ${CFLAGS}" echo "LDFLAGS = ${LDFLAGS}" echo do_rebuild=0 objs="" for src in $(find . -name '*.c' | cut -d '/' -f 2-); do obj=$(echo "../build/$src" | sed -e 's/\.c$/\.o/') objs="$objs $obj" if [ $(mod_time "$src") -ge $(mod_time "$obj") ]; then echo "CC $(basename $obj)" obj_dir=$(dirname "$obj") mkdir -p "$obj_dir" if ! $CC $CFLAGS -Iinclude -c -o "$obj" "$src"; then exit 1 fi do_rebuild=1 fi done if [ $do_rebuild -eq 1 ] || [ ! -f "../build/$PROG" ]; then echo "LD $PROG" $CC $LDFLAGS -o "../build/$PROG" $objs else echo "Up to date." fi } recipe_run() { if [ -f "build/$PROG" ]; then "build/$PROG" -f contrib/development.conf else echo "build/$PROG does not exist; build it first." fi } # Remove all build files, which can be regenerated by re-running the # build recipe. recipe_clean() { rm -r build } # Format the source code by updating the copyright headers and # then running indent(1) on each source code file. recipe_format() { find src -name '*.c' -or -name '*.h' | while IFS= read -r src; do if [ -t 1 ]; then printf "FMT %s%*c\r" $(basename "$src") "16" " " fi # Update the headers srcHeader=$(grep -n -m 1 '^ \*/' "$src" | cut -d ':' -f 1) head "-n$srcHeader" "$src" | diff -u -p - LICENSE.txt | patch "$src" | grep -v "^Hmm" # Format the source code if indent "$src"; then rm $(basename "$src").BAK fi done if [ -t 1 ]; then printf "%*c\n" "50" " " fi } # Deploy the Telodendria website by copying the required files to # a web root defined by TELODENDRIA_PUB. recipe_site() { if [ -z "$TELODENDRIA_PUB" ]; then echo "No public root directory specified." echo "Set TELODENDRIA_PUB." exit 1 fi # Set some variables that may be replaced in the files. DATE=$(date) USER_DOCS=$(man-table user) DEV_DOCS=$(man-table dev) cd site/ find . -type f | grep -v CVS | while IFS= read -r file; do dest="$TELODENDRIA_PUB/$file" dir=$(dirname "$dest") echo "$dest" mkdir -p "$dir" setsubst "$file" > "$dest" done cd - > /dev/null find man/ -name '*.[1-9]' | while IFS= read -r man; do dir=$(dirname "$man") html=$(basename "$man") mkdir -p "$TELODENDRIA_PUB/$dir/" mandoc -Thtml \ -O style=/style.css,man=/man/man%S/%N.%S.html "$man" \ > "$TELODENDRIA_PUB/$dir/$html.html" echo "$TELODENDRIA_PUB/$dir/$html.html" done } # Generate a release tarball, checksum and sign it, and push it to # the web root. recipe_release() { if [ -z "$TELODENDRIA_PUB" ]; then echo "No public root directory specified." echo "Set TELODENDRIA_PUB." exit 1 fi # Tag the release at this point in time. cvs tag "$CVS_TAG" mkdir -p "$TELODENDRIA_PUB/pub/v$TELODENDRIA_VERSION" cd "$TELODENDRIA_PUB/pub/v$TELODENDRIA_VERSION" # Generate the release tarball. cvs export "-r$CVS_TAG" "Telodendria" mv "Telodendria" "Telodendria-v$TELODENDRIA_VERSION" tar -czf "Telodendria-v$TELODENDRIA_VERSION.tar.gz" \ "Telodendria-v$TELODENDRIA_VERSION" rm -r "Telodendria-v$TELODENDRIA_VERSION" # Checksum the release tarball. sha256 "Telodendria-v$TELODENDRIA_VERSION.tar.gz" \ > "Telodendria-v$TELODENDRIA_VERSION.tar.gz.sha256" # Sign the release tarball. if [ ! -z "$TELODENDRIA_SIGNIFY_SECRET" ]; then signify -S -s "$TELODENDRIA_SIGNIFY_SECRET" \ -m "Telodendria-v$TELODENDRIA_VERSION.tar.gz" \ -x "Telodendria-v$TELODENDRIA_VERSION.tar.gz.sig" else echo "Warning: TELODENDRIA_SIGNIFY_SECRET not net." echo "The built tarball will not be signed." fi } # Generate a formatted patch file. The Telodendria project isn't # really picky about how patches look, but this is how we like them # best. Makes them easy to read. recipe_patch() { # If the user has not set their MXID, try to deduce one from # their system. if [ -z "$MXID" ]; then MXID="@${USER}:$(hostname)" fi # If the user has not set their DISPLAY_NAME, try to deduce it # from their system. if [ -z "$DISPLAY_NAME" ]; then DISPLAY_NAME=$(getent passwd "$USER" | cut -d ':' -f 5 | cut -d ',' -f 1) fi # If the user has not set their EDITOR, use a safe default. # (vi should be available on any POSIX system) if [ -z "$EDITOR" ]; then EDITOR=vi fi NORMALIZED_MXID=$(echo "$MXID" | sed -e 's/@//g' -e 's/:/-/g') PATCH_FILE="${NORMALIZED_MXID}_$(date +%s).patch" # Generate the actual patch file # Here we write some nice headers, and then do a cvs diff. ( printf 'From: "%s" <%s>\n' "$DISPLAY_NAME" "$MXID" echo "Date: $(date)" echo "Subject: " echo echo "[ ] I have read the Telodendria Project developer certificate" echo " of origin, and certify that I have permission to submit" echo " this patch under the conditions specified in it." echo cvs -q diff -uNp $PATCHSET | grep -v '^\? ' ) > "$PATCH_FILE" "$EDITOR" "$PATCH_FILE" echo "$PATCH_FILE" } recipe_diff() { tmp_patch="/tmp/telodendria-$(date +%s).patch" cvs -q diff -uNp $PATCHSET > "$tmp_patch" if [ -z "$PAGER" ]; then PAGER="less -F" fi $PAGER "$tmp_patch" rm "$tmp_patch" } # Execute the user-specified recipes. for recipe in $@; do recipe_$recipe done # If no recipe was provided, run a build. if [ -z "$1" ]; then recipe_build fi