# --- settings --- name := "clog" std := "c23" cc := "gcc" # --- directories --- src_dir := "src" inc_dir := "include" lib_dir := "lib" test_dir := "tests" build_dir := ".build" # --- platform --- so_ext := if os() == "macos" { "dylib" } else { "so" } so_flags := if os() == "macos" { "-dynamiclib" } else { "-shared" } # --- cflags --- warn := "-Wall -Wextra -Wfloat-equal -Wundef -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wconversion -Wunreachable-code -pedantic" base := "-std=" + std + " " + warn + " -I" + inc_dir san := "-fsanitize=address,undefined" # Static: put .a files in lib/static/ and append -L lib/static -lfoo # Dynamic: put .so files in lib/dynamic/ and append -L lib/dynamic -lbar lnk := "-L" + lib_dir + "/static -L" + lib_dir + "/dynamic -lm" # --- build --- # Build both static and dynamic libraries (default) build: static dynamic [group('build')] static: #!/usr/bin/env bash set -euo pipefail mkdir -p {{build_dir}} objs=() for f in $(find {{src_dir}} -name '*.c'); do obj="{{build_dir}}/$(basename "$f" .c).o" echo {{cc}} {{base}} -O2 -DNDEBUG -fPIC -c "$f" -o "$obj" {{cc}} {{base}} -O2 -DNDEBUG -fPIC -c "$f" -o "$obj" objs+=("$obj") done ar rcs {{build_dir}}/lib{{name}}.a "${objs[@]}" echo "Built: {{build_dir}}/lib{{name}}.a" [group('build')] dynamic: #!/usr/bin/env bash set -euo pipefail mkdir -p {{build_dir}} objs=() for f in $(find {{src_dir}} -name '*.c'); do obj="{{build_dir}}/$(basename "$f" .c).o" {{cc}} {{base}} -O2 -DNDEBUG -fPIC -c "$f" -o "$obj" objs+=("$obj") done {{cc}} {{so_flags}} -o {{build_dir}}/lib{{name}}.{{so_ext}} "${objs[@]}" {{lnk}} echo "Built: {{build_dir}}/lib{{name}}.{{so_ext}}" # --- checks --- [group('check')] test: #!/usr/bin/env bash set -euo pipefail mkdir -p {{build_dir}}/tests srcs=$(find {{src_dir}} -name '*.c' | tr '\n' ' ') failed=0 for f in $(find {{test_dir}} -name 'test_*.c'); do tname=$(basename "$f" .c) printf ' %-30s' "$tname" {{cc}} {{base}} -I{{test_dir}} -g -O1 {{san}} "$f" $srcs {{lnk}} \ -o {{build_dir}}/tests/"$tname" if {{build_dir}}/tests/"$tname"; then printf 'ok\n' else printf 'FAILED\n' failed=$((failed + 1)) fi done [ $failed -eq 0 ] || { echo "$failed test(s) failed"; exit 1; } # Run tests under Valgrind (Linux only) [group('check')] valgrind: #!/usr/bin/env bash set -euo pipefail mkdir -p {{build_dir}}/tests srcs=$(find {{src_dir}} -name '*.c' | tr '\n' ' ') for f in $(find {{test_dir}} -name 'test_*.c'); do tname=$(basename "$f" .c) echo "==> valgrind: $tname" {{cc}} {{base}} -I{{test_dir}} -g -O1 "$f" $srcs {{lnk}} \ -o {{build_dir}}/tests/"$tname"-vg valgrind --leak-check=full --show-leak-kinds=all \ --track-origins=yes --error-exitcode=1 \ {{build_dir}}/tests/"$tname"-vg done [group('check')] cppcheck: cppcheck --enable=all --std={{std}} --inconclusive --error-exitcode=1 \ -I {{inc_dir}} {{src_dir}}/ # --- formatting --- [group('fmt')] fmt: find {{src_dir}} {{inc_dir}} -name '*.[ch]' | xargs clang-format -i [group('fmt')] fmt-check: find {{src_dir}} {{inc_dir}} -name '*.[ch]' | xargs clang-format --dry-run --Werror # --- miscellaneous --- # Generate compile_commands.json for clangd (requires bear) compile-commands: bear -- just build # Generate HTML documentation in .build/docs/html/ docs: #!/usr/bin/env bash set -euo pipefail mkdir -p {{build_dir}}/docs doxygen - <<-'DOXYEOF' PROJECT_NAME = {{name}} INPUT = {{src_dir}} {{inc_dir}} OUTPUT_DIRECTORY = {{build_dir}}/docs GENERATE_HTML = YES GENERATE_LATEX = NO RECURSIVE = YES EXTRACT_ALL = YES QUIET = YES DOXYEOF echo "docs: {{build_dir}}/docs/html/index.html" # Pack include/ and built libs into a tarball for use in a binary project. # Usage: just pack [output.tar.gz [dirname]] # just pack -> mylib.tar.gz, extracts into cwd # just pack mylib.tar.gz mylib -> extracts into mylib/ subdirectory pack output="" prefix="": #!/usr/bin/env bash set -euo pipefail just static just dynamic out="{{output}}" [[ -z "$out" ]] && out="{{name}}.tar.gz" stage=$(mktemp -d) [[ -d "$stage" ]] || { echo "error: mktemp failed" >&2; exit 1; } trap 'rm -rf "$stage"' EXIT pfx="{{prefix}}" root="$stage${pfx:+/$pfx}" mkdir -p "$root/include" "$root/lib/static" "$root/lib/dynamic" cp -r {{inc_dir}}/. "$root/include/" [[ -f "{{build_dir}}/lib{{name}}.a" ]] && cp "{{build_dir}}/lib{{name}}.a" "$root/lib/static/" [[ -f "{{build_dir}}/lib{{name}}.so" ]] && cp "{{build_dir}}/lib{{name}}.so" "$root/lib/dynamic/" [[ -f "{{build_dir}}/lib{{name}}.dylib" ]] && cp "{{build_dir}}/lib{{name}}.dylib" "$root/lib/dynamic/" tar -czf "$out" -C "$stage" "${pfx:-.}" echo "packed: $out" clean: rm -rf {{build_dir}}