#!/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. addprefix() { prefix="$1" shift for thing in "$@"; do echo "${prefix}${thing}" done unset prefix unset thing } if [ -z "$TELODENDRIA_ENV" ]; then . tools/env.sh fi # # 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.3.0}" : "${CVS_TAG:=Telodendria-$(echo $TELODENDRIA_VERSION | sed 's/\./_/g')}" : "${DEFINES:=-D_DEFAULT_SOURCE -DTELODENDRIA_VERSION=\"${TELODENDRIA_VERSION}-$(uname)\"}" : "${INCLUDES:=src/include Cytoplasm/src/include}" : "${CC:=cc}" : "${AR:=ar}" : "${CFLAGS:=-Wall -Wextra -pedantic -std=c89 -O3 -pipe}" : "${STATIC:=-static -Wl,-static}" : "${LD_EXTRA:=-flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections}" : "${PROG:=telodendria}" . "$(pwd)/tools/lib/common.sh" CFLAGS="${CFLAGS} ${DEFINES} $(addprefix -I$(pwd)/ ${INCLUDES})" MAIN="Main" if [ "$DEBUG" = "1" ]; then CFLAGS="$CFLAGS -O0 -g" PROG="$PROG-debug" else LDFLAGS="${LDFLAGS} ${LD_EXTRA}" 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* | Haiku) stat -c %Y "$1" ;; *BSD | DragonFly | Minix) 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" } recipe_docs() { mkdir -p build/man/man3 for header in $(find src -name '*.h'); do basename=$(basename "$header") man=$(echo "build/man/man3/$basename" | sed -e 's/\.h$/\.3/') if [ $(mod_time "$header") -ge $(mod_time "$man") ]; then echo "DOC $basename" if ! hdoc -D Os=Telodendria -i "$header" -o "$man"; then rm "$man" exit 1 fi fi done if which makewhatis 2>&1 > /dev/null; then makewhatis "$(pwd)/man" fi } recipe_cytoplasm() { cd Cytoplasm export TLS_IMPL if ! sh make.sh; then exit 1 fi CYTOPLASM_FLAGS="Cytoplasm/out/lib/cytoplasm.o -LCytoplasm/out/lib -lcytoplasm $(sh make.sh libs)" cd - > /dev/null } # Build the source code, and generate the 'build/telodendria' # binary. recipe_build() { recipe_cytoplasm cd src mkdir -p ../build echo "CC = ${CC}" echo "CFLAGS = ${CFLAGS}" echo "LDFLAGS = ${CYTOPLASM_FLAGS} ${LDFLAGS} ${STATIC}" 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/') if [ $(basename "$obj" .o) != "$MAIN" ]; then objs="$objs $obj" fi if [ $(mod_time "$src") -ge $(mod_time "../$obj") ]; then echo "CC $(basename $obj)" obj_dir=$(dirname "../$obj") mkdir -p "$obj_dir" if ! $CC $CFLAGS -fPIC -c -o "../$obj" "$src"; then exit 1 fi do_rebuild=1 fi done cd .. if [ $do_rebuild -eq 1 ] || [ ! -f "build/$PROG" ]; then echo "LD $PROG" if ! $CC -o "build/$PROG" $objs "build/$MAIN.o" ${CYTOPLASM_FLAGS} ${LDFLAGS} ${STATIC}; then exit 1 fi fi for src in $(find tools/src -name '*.c'); do out=$(basename "$src" .c) out="build/tools/$out" if [ $(mod_time "$src") -ge $(mod_time "$out") ] || [ $do_rebuild -eq 1 ]; then echo "CC $(basename $out)" mkdir -p "$(dirname $out)" if ! $CC $CFLAGS -o "$out" "$src" ${CYTOPLASM_FLAGS} ${LDFLAGS} ${STATIC}; then exit 1 fi fi done recipe_docs } recipe_run() { if [ -f "build/$PROG" ]; then "build/$PROG" -d data if [ -f "data/telodendria-leaked.txt" ]; then echo "WARNING: Telodendria is leaking memory." echo "Please fix memory leaks." fi 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 } # Update copyright comments in sources and header files. recipe_license() { find . -name '*.[ch]' | while IFS= read -r src; do if [ -t 1 ]; then printf "LICENSE %s%*c\r" $(basename "$src") "16" " " fi srcHeader=$(grep -n -m 1 '^ \*/' "$src" | cut -d ':' -f 1) head "-n$srcHeader" "$src" | diff -u -p - LICENSE.txt | patch "$src" | grep -v "^Hmm" done if [ -t 1 ]; then printf "%*c\n" "50" " " fi } # Format source code files by running indent(1) on them. recipe_format() { find . -name '*.c' | while IFS= read -r src; do if [ -t 1 ]; then printf "FMT %s%*c\r" $(basename "$src") "16" " " fi 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 recipe_docs # 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/ build/man/ -name '*.[1-9]' | while IFS= read -r man; do section=$(basename "$man" | rev | cut -d '.' -f 1 | rev) html=$(basename "$man") mkdir -p "$TELODENDRIA_PUB/man/man$section/" mandoc -Thtml \ -O style=/style.css,man=/man/man%S/%N.%S.html "$man" \ >"$TELODENDRIA_PUB/man/man$section/$html.html" echo "$TELODENDRIA_PUB/man/man$section/$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 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