Skip to content

Instantly share code, notes, and snippets.

@pkhuong
Created September 28, 2025 23:13
Show Gist options
  • Select an option

  • Save pkhuong/3873f28a9f726128379e4cd668b980fe to your computer and use it in GitHub Desktop.

Select an option

Save pkhuong/3873f28a9f726128379e4cd668b980fe to your computer and use it in GitHub Desktop.
Git-driven parallel + ccache build script
#!/usr/bin/env bash
set -e
set -o pipefail
PARALLEL="parallel -0 --color-failed ${*:--j32 --verbose} "
EXTENSIONS="*.c *.cc"
# Everything goes in $OBJDIR/lib/, except for files listed in
# `.binfiles`: those go to $OBJDIR/bin/ and link as $BINDIR.
OBJDIR=build/obj
BINDIR=bin
# Default build rules by extension
declare -A BUILD
BUILD[c]="ccache gcc -O2 -std=gnu99 -W"
BUILD[cc]="ccache g++ -O3 -std=gnu++20 -W"
BUILD[3ps.c]="ccache gcc -Os"
BUILD[3ps.cc]="ccache g++ -Os"
LINK_CMD="g++ -O3 -W"
# Add per-file overrides for third-party source files (populate git patterns in `.3ps`).
declare -A BUILD_OVERRIDE
while IFS= read -r -d $'\0' FILE;
do
EXT="${FILE##*.}"
BUILD_OVERRIDE[$FILE]=${BUILD["3ps.${EXT}"]:?no build command for 3ps.$EXT}
done < <(
# shellcheck disable=SC2086 # we do want word splitting (globbing should be a non-issue)
git ls-files --cached --ignored --exclude-per-directory=.3ps -z $EXTENSIONS
)
printf "git clean -fx '%s' \0" "$OBJDIR" | $PARALLEL
printf "mkdir -p '%s/lib' '%s/bin' \0" "$OBJDIR" "$OBJDIR" | $PARALLEL
DEFAULT_OUTDIR="$OBJDIR/lib"
declare -A OUTDIRS
# Except for binary program files
while IFS= read -r -d $'\0' FILE;
do
OUTDIRS[$FILE]="$OBJDIR/bin"
done < <(
# shellcheck disable=SC2086 # we do want word splitting (globbing should be a non-issue)
git ls-files --cached --ignored --exclude-per-directory=.binfiles -z $EXTENSIONS
)
# shellcheck disable=SC2086 # we do want word splitting (globbing should be a non-issue)
git ls-files --cached -z $EXTENSIONS | \
while IFS= read -r -d $'\0' FILE;
do
OUT="${FILE//[^a-zA-Z0-9.]/%}.o"
EXT="${FILE##*.}"
DST="${OUTDIRS[$FILE]:-$DEFAULT_OUTDIR}"
CMD=${BUILD_OVERRIDE["$FILE"]:-${BUILD["$EXT"]:?"no build command for .$EXT file $FILE"}}
printf "%s %s -c -o %s \0" "$CMD" "$FILE" "$DST/$OUT"
done | $PARALLEL
printf "git clean -fx '%s' \0" "$BINDIR" | $PARALLEL
printf "mkdir -p '%s' \0" "$BINDIR" | $PARALLEL
# shellcheck disable=SC2086 # we do want word splitting (globbing should be a non-issue)
git ls-files --cached --ignored --exclude-per-directory=.binfiles -z $EXTENSIONS |
while IFS= read -r -d $'\0' FILE;
do
OUT="${FILE//[^a-zA-Z0-9.]/%}.o"
DST="${OUTDIRS[$FILE]:-$DEFAULT_OUTDIR}"
BASE=$(basename "${FILE%%.*}")
printf "%s $OBJDIR/lib/*.o %s -o %s \0" "$LINK_CMD" "$DST/$OUT" "$BINDIR/$BASE"
done | $PARALLEL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment