telodendria/tools/bin/td

393 lines
11 KiB
Bash

#!/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.
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)\"}"
: "${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}"
: "${LDFLAGS:=-lm -pthread}"
: "${PROG:=telodendria}"
. "$(pwd)/tools/lib/common.sh"
if [ -n "$TLS_IMPL" ]; then
case "$TLS_IMPL" in
"LIBRESSL")
TLS_LIBS="-ltls -lcrypto -lssl"
;;
"OPENSSL")
TLS_LIBS="-lcrypto -lssl"
;;
*)
echo "Unrecognized TLS implementation: ${TLS_IMPL}"
echo "Consult src/include/Tls.h for supported implementations."
echo "Note that the TLS_ prefix is omitted in TLS_IMPL."
exit 1
;;
esac
DEFINES="${DEFINES} -DTLS_IMPL=TLS_${TLS_IMPL}"
LDFLAGS="${LDFLAGS} ${TLS_LIBS}"
fi
CFLAGS="${CFLAGS} ${DEFINES} ${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
}
# 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} ${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 -Iinclude -c -o "../$obj" "$src"; then
exit 1
fi
do_rebuild=1
fi
done
cd ..
if [ $do_rebuild -eq 1 ] || [ ! -f "build/lib${PROG}.a" ]; then
echo "AR lib${PROG}.a"
if ! $AR rcs "build/lib${PROG}.a" $objs; then
exit 1
fi
fi
if [ $do_rebuild -eq 1 ] || [ ! -f "build/lib${PROG}.so" ]; then
echo "LD lib${PROG}.so"
if ! $CC -shared -o "build/lib${PROG}.so" $objs ${LDFLAGS}; then
exit 1
fi
fi
if [ $do_rebuild -eq 1 ] || [ ! -f "build/$PROG" ]; then
echo "LD $PROG"
if ! $CC -o "build/$PROG" "build/$MAIN.o" -Lbuild -ltelodendria ${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 -Isrc/include -o "$out" "$src" -Lbuild -ltelodendria ${LDFLAGS} ${STATIC}; then
exit 1
fi
fi
done
if ! intcheck; then
exit 1
fi
recipe_docs
}
recipe_run() {
if [ -f "build/$PROG" ]; then
"build/$PROG" -d data
if [ -f "data/Memory.txt" ]; then
echo "WARNING: Memory.txt exists in the data directory; this means"
echo "Telodendria is leaking memory. 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