From 28f519d9650004787be480f15e990f835d324d7f Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 28 Sep 2023 17:39:34 -0400 Subject: bench: New script to clone a git repo without file contents --- bench/clone-tree.sh | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100755 bench/clone-tree.sh (limited to 'bench') diff --git a/bench/clone-tree.sh b/bench/clone-tree.sh new file mode 100755 index 0000000..744b5f4 --- /dev/null +++ b/bench/clone-tree.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Creates a directory tree that matches a git repo, but with empty files. E.g. +# +# $ ./bench/clone-tree.sh "https://.../linux.git" v6.5 ./linux ./linux.git +# +# will create or update a shallow clone at ./linux.git, then create a directory +# tree at ./linux with the same directory tree as the tag v6.5, except all files +# will be empty. + +set -eu + +if (($# != 4)); then + printf 'Usage: %s https://url/of/repo.git path/to/checkout path/to/repo.git\n' "$0" >&2 + exit 1 +fi + +URL="$1" +TAG="$2" +DIR="$3" +REPO="$4" + +BENCH=$(dirname -- "${BASH_SOURCE[0]}") +BIN=$(realpath -- "$BENCH/../bin") +BFS="$BIN/bfs" +XTOUCH="$BIN/tests/xtouch" + +if [ "${NPROC-}" ]; then + # Use fewer cores in recursive calls + export NPROC=$(((NPROC + 1) / 2)) +else + export NPROC=$(nproc) +fi + +JOBS=$((NPROC < 8 ? NPROC : 8)) + +do-git() { + git -C "$REPO" "$@" +} + +if ! [ -e "$REPO" ]; then + mkdir -p -- "$REPO" + do-git init -q --bare +fi + +has-ref() { + do-git rev-list --quiet -1 --missing=allow-promisor "$1" &>/dev/null +} + +sparse-fetch() { + do-git -c fetch.negotiationAlgorithm=noop fetch -q --filter=blob:none --depth=1 --no-tags --no-write-fetch-head --no-auto-gc "$@" +} + +if ! has-ref "$TAG"; then + printf 'Fetching %s ...\n' "$TAG" >&2 + do-git config remote.origin.url "$URL" + if ((${#TAG} >= 40)); then + sparse-fetch origin "$TAG" + else + sparse-fetch origin tag "$TAG" + fi +fi + +# Delete a tree in parallel +clean() { + local d=5 + "$BFS" -f "$1" -mindepth $d -maxdepth $d -type d -print0 \ + | xargs -0r -n1 -P$JOBS -- "$BFS" -j1 -mindepth 1 -delete -f + "$BFS" -f "$1" -delete +} + +if [ -e "$DIR" ]; then + printf 'Cleaning old directory tree %s ...\n' "$DIR" >&2 + TMP=$(mktemp -dp "$(dirname -- "$DIR")") + mv -- "$DIR" "$TMP" + clean "$TMP" & +fi + +# List gitlinks (submodule references) in the tree +ls-gitlinks() { + do-git ls-tree -zr "$TAG" \ + | sed -zn 's/.* commit //p' +} + +# Get the submodule ID for a path +submodule-for-path() { + do-git config --blob "$TAG:.gitmodules" \ + --name-only \ + --fixed-value \ + --get-regexp 'submodule\..**\.path' "$1" \ + | sed -En 's/submodule\.(.*)\.path/\1/p' +} + +# Get the URL for a submodule +submodule-url() { + # - https://chrome-internal.googlesource.com/ + # - not publicly accessible + # - https://chromium.googlesource.com/external/github.com/WebKit/webkit.git + # - is accessible, but the commit (59e9de61b7b3) isn't + # - https://android.googlesource.com/ + # - is accessible, but you need an account + + do-git config --blob "$TAG:.gitmodules" \ + --get "submodule.$1.url" \ + | sed -E \ + -e '\|^https://chrome-internal.googlesource.com/|Q1' \ + -e '\|^https://chromium.googlesource.com/external/github.com/WebKit/webkit.git|Q1' \ + -e '\|^https://android.googlesource.com/|Q1' +} + +# Recursively checkout submodules +while read -rd '' SUBREF SUBDIR; do + SUBNAME=$(submodule-for-path "$SUBDIR") + SUBURL=$(submodule-url "$SUBNAME") || continue + + if (($(jobs -pr | wc -w) >= JOBS)); then + wait -n + fi + "$0" "$SUBURL" "$SUBREF" "$DIR/$SUBDIR" "$REPO/modules/$SUBNAME" & +done < <(ls-gitlinks) + +# Touch files in parallel +xtouch() ( + cd "$DIR" + if ((JOBS > 1)); then + xargs -0r -n4096 -P$JOBS -- "$XTOUCH" -p -- + else + xargs -0r -- "$XTOUCH" -p -- + fi +) + +# Check out files +printf 'Checking out %s ...\n' "$DIR" >&2 +mkdir -p -- "$DIR" +do-git ls-tree -zr "$TAG"\ + | sed -zn 's/.* blob .*\t//p' \ + | xtouch + +# Wait for cleaning/submodules +wait -- cgit v1.2.3 From fd2a86bb93f42a2c64b202f959dd5c8cbb724c69 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 29 Sep 2023 14:13:06 -0400 Subject: bench: Add benchmarking script --- bench/.gitignore | 3 + bench/bench.sh | 491 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 494 insertions(+) create mode 100644 bench/.gitignore create mode 100644 bench/bench.sh (limited to 'bench') diff --git a/bench/.gitignore b/bench/.gitignore new file mode 100644 index 0000000..170d850 --- /dev/null +++ b/bench/.gitignore @@ -0,0 +1,3 @@ +/corpus/ +/results/ +/worktree/ diff --git a/bench/bench.sh b/bench/bench.sh new file mode 100644 index 0000000..09564ac --- /dev/null +++ b/bench/bench.sh @@ -0,0 +1,491 @@ +#!/hint/bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +declare -gA URLS=( + [chromium]="https://chromium.googlesource.com/chromium/src.git" + [linux]="https://github.com/torvalds/linux.git" + [rust]="https://github.com/rust-lang/rust.git" +) + +declare -gA TAGS=( + [chromium]=119.0.6036.2 + [linux]=v6.5 + [rust]=1.72.1 +) + +COMPLETE_DEFAULT=(linux rust chromium) +EARLY_QUIT_DEFAULT=(chromium) +PRINT_DEFAULT=(linux) +STRATEGIES_DEFAULT=(linux) + +usage() { + printf 'Usage: tailfin run %s [--default]\n' "${BASH_SOURCE[0]}" + printf ' [--complete] [--early-quit] [--print] [--strategies]\n' + printf ' [--build=...] [--bfs] [--find] [--fd]\n' + printf ' [--no-clean] [--help]\n\n' + + printf ' --default\n' + printf ' Run the default set of benchmarks\n\n' + + printf ' --complete[=CORPUS]\n' + printf ' Complete traversal benchmark. \n' + printf ' Default corpus is --complete="%s"\n\n' "${COMPLETE_DEFAULT[*]}" + + printf ' --early-quit[=CORPUS]\n' + printf ' Early quitting benchmark. \n' + printf ' Default corpus is --early-quit=chromium\n\n' "${EARLY_QUIT_DEFAULT[*]}" + + printf ' --print[=CORPUS]\n' + printf ' Path printing benchmark. \n' + printf ' Default corpus is --print=linux\n\n' "${PRINT_DEFAULT[*]}" + + printf ' --strategies[=CORPUS]\n' + printf ' Search strategy benchmark.\n' + printf ' Default corpus is --strategies=linux\n\n' "${STRATEGIES_DEFAULT[*]}" + + printf ' --build=COMMIT\n' + printf ' Build this bfs commit and benchmark it. Specify multiple times to\n' + printf ' compare, e.g. --build=3.0.1 --build=3.0.2\n\n' + + printf ' --bfs[=COMMAND]\n' + printf ' Benchmark an existing build of bfs\n\n' + + printf ' --find[=COMMAND]\n' + printf ' Compare against find\n\n' + + printf ' --fd[=COMMAND]\n' + printf ' Compare against fd\n\n' + + printf ' --no-clean\n' + printf ' Use any existing corpora as-is\n\n' + + printf ' --help\n' + printf ' This message\n\n' +} + +# Hack to export an array +export_array() { + local str=$(declare -p "$1" | sed 's/ -a / -ga /') + unset "$1" + export "$1=$str" +} + +# Hack to import an array +import_array() { + local cmd="${!1}" + unset "$1" + eval "$cmd" +} + +# Set up the benchmarks +setup() { + ROOT=$(realpath -- "$(dirname -- "${BASH_SOURCE[0]}")/..") + if ! [ "$PWD" -ef "$ROOT" ]; then + printf 'error: Please run this script from %s\n\n' "$ROOT" >&2 + usage >&2 + exit $EX_USAGE + fi + + nproc=$(nproc) + + # Options + + CLEAN=1 + + BUILD=() + BFS=() + FIND=() + FD=() + + COMPLETE=() + EARLY_QUIT=() + PRINT=() + STRATEGIES=() + + for arg; do + case "$arg" in + # Flags + --no-clean) + CLEAN=0 + ;; + # bfs commits/tags to benchmark + --build=*) + BUILD+=("${arg#*=}") + BFS+=("bfs-${arg#*=}") + ;; + # Utilities to benchmark against + --bfs) + BFS+=(bfs) + ;; + --bfs=*) + BFS+=("${arg#*=}") + ;; + --find) + FIND+=(find) + ;; + --find=*) + FIND+=("${arg#*=}") + ;; + --fd) + FD+=(fd) + ;; + --fd=*) + FD+=("${arg#*=}") + ;; + # Benchmark groups + --complete) + COMPLETE=("${COMPLETE_DEFAULT[@]}") + ;; + --complete=*) + read -ra COMPLETE <<<"${arg#*=}" + ;; + --early-quit) + EARLY_QUIT=("${EARLY_QUIT_DEFAULT[@]}") + ;; + --early-quit=*) + read -ra EARLY_QUIT <<<"${arg#*=}" + ;; + --print) + PRINT=("${PRINT_DEFAULT[@]}") + ;; + --print=*) + read -ra PRINT <<<"${arg#*=}" + ;; + --strategies) + STRATEGIES=("${STRATEGIES_DEFAULT[@]}") + ;; + --strategies=*) + read -ra STRATEGIES <<<"${arg#*=}" + ;; + --default) + COMPLETE=("${COMPLETE_DEFAULT[@]}") + EARLY_QUIT=("${EARLY_QUIT_DEFAULT[@]}") + PRINT=("${PRINT_DEFAULT[@]}") + STRATEGIES=("${STRATEGIES_DEFAULT[@]}") + ;; + --help) + usage + exit + ;; + *) + printf 'error: Unknown option %q\n\n' "$arg" >&2 + usage >&2 + exit $EX_USAGE + ;; + esac + done + + if ((UID == 0)); then + max-freq + fi + + echo "Building bfs ..." + as-user make -s -j"$nproc" release all + + as-user mkdir -p bench/corpus + + declare -A cloned=() + for corpus in "${COMPLETE[@]}" "${EARLY_QUIT[@]}" "${PRINT[@]}" "${STRATEGIES[@]}"; do + if ((cloned["$corpus"])); then + continue + fi + cloned["$corpus"]=1 + + dir="bench/corpus/$corpus" + if ((CLEAN)) || ! [ -e "$dir" ]; then + as-user ./bench/clone-tree.sh "${URLS[$corpus]}" "${TAGS[$corpus]}" "$dir"{,.git} + fi + done + + if ((${#BUILD[@]} > 0)); then + echo "Creating bfs worktree ..." + + worktree="bench/worktree" + as-user git worktree add -qd "$worktree" + at-exit as-user git worktree remove "$worktree" + + bin="$(realpath -- "$SETUP_DIR")/bin" + as-user mkdir "$bin" + + for commit in "${BUILD[@]}"; do + ( + echo "Building bfs $commit ..." + cd "$worktree" + as-user git checkout -qd "$commit" -- + as-user make -s -j"$nproc" release + as-user cp ./bin/bfs "$bin/bfs-$commit" + as-user make -s clean + ) + done + + # $SETUP_DIR contains `:` so it won't work in $PATH + # Work around this with a symlink + tmp=$(as-user mktemp) + as-user ln -sf "$bin" "$tmp" + at-exit rm "$tmp" + export PATH="$tmp:$PATH" + fi + + export_array BFS + export_array FIND + export_array FD + + export_array COMPLETE + export_array EARLY_QUIT + export_array PRINT + export_array STRATEGIES + + if ((UID == 0)); then + turbo-off + fi + + sync +} + +# Runs hyperfine and saves the output +do-hyperfine() { + local tmp_md="$BENCH_DIR/.bench.md" + local md="$BENCH_DIR/bench.md" + local tmp_json="$BENCH_DIR/.bench.json" + local json="$BENCH_DIR/bench.json" + + if (($# == 0)); then + printf 'Nothing to do\n\n' | tee -a "$md" + return 1 + fi + + hyperfine -w2 -M20 --export-markdown="$tmp_md" --export-json="$tmp_json" "$@" &>/dev/tty + cat "$tmp_md" >>"$md" + cat "$tmp_json" >>"$json" + rm "$tmp_md" "$tmp_json" + + printf '\n' | tee -a "$md" +} + +# Print the header for a benchmark group +group() { + printf '## %s\n\n' "$1" | tee -a "$BENCH_DIR/bench.md" +} + +# Print the header for a benchmark subgroup +subgroup() { + printf '### %s\n\n' "$1" | tee -a "$BENCH_DIR/bench.md" +} + +# Print the header for a benchmark sub-subgroup +subsubgroup() { + printf '#### %s\n\n' "$1" | tee -a "$BENCH_DIR/bench.md" +} + +# Benchmark the complete traversal of a directory tree +# (without printing anything) +bench-complete-corpus() { + total=$(./bin/bfs "$2" -printf '.' | wc -c) + + subgroup "$1 ($total files)" + + cmds=() + for bfs in "${BFS[@]}"; do + cmds+=("$bfs $2 -false") + done + + for find in "${FIND[@]}"; do + cmds+=("$find $2 -false") + done + + for fd in "${FD[@]}"; do + cmds+=("$fd -u '^$' $2") + done + + do-hyperfine "${cmds[@]}" +} + +# All complete traversal benchmarks +bench-complete() { + if (($#)); then + group "Complete traversal" + + for corpus; do + bench-complete-corpus "$corpus ${TAGS[$corpus]}" "bench/corpus/$corpus" + done + fi +} + +# Benchmark quiting as soon as a file is seen +bench-early-quit-corpus() { + dir="$2" + max_depth=$(./bin/bfs "$dir" -printf '%d\n' | sort -rn | head -n1) + + subgroup "$1 (depth $max_depth)" + + # Save the list of unique filenames, along with their depth + UNIQ="$BENCH_DIR/uniq" + ./bin/bfs "$dir" -printf '%d %f\n' | sort -k2 | uniq -uf1 >"$UNIQ" + + for ((i = 2; i <= max_depth; i *= 2)); do + subsubgroup "Depth $i" + + # Sample random uniquely-named files at depth $i + export FILES="$BENCH_DIR/uniq-$i" + sed -n "s/^$i //p" "$UNIQ" | shuf -n20 >"$FILES" + if ! [ -s "$FILES" ]; then + continue + fi + + cmds=() + for bfs in "${BFS[@]}"; do + cmds+=("$bfs $dir -name \$(shuf -n1 \$FILES) -print -quit") + done + + for find in "${FIND[@]}"; do + cmds+=("$find $dir -name \$(shuf -n1 \$FILES) -print -quit") + done + + for fd in "${FD[@]}"; do + cmds+=("$fd -usg1 \$(shuf -n1 \$FILES) $dir") + done + + do-hyperfine "${cmds[@]}" + done +} + +# All early-quitting benchmarks +bench-early-quit() { + if (($#)); then + group "Early termination" + + for corpus; do + bench-early-quit-corpus "$corpus ${TAGS[$corpus]}" "bench/corpus/$corpus" + done + fi +} + +# Benchmark printing paths without colors +bench-print-nocolor() { + subsubgroup "$1" + + cmds=() + for bfs in "${BFS[@]}"; do + cmds+=("$bfs $2") + done + + for find in "${FIND[@]}"; do + cmds+=("$find $2") + done + + for fd in "${FD[@]}"; do + cmds+=("$fd -u --search-path $2") + done + + do-hyperfine "${cmds[@]}" +} + +# Benchmark printing paths with colors +bench-print-color() { + subsubgroup "$1" + + cmds=() + for bfs in "${BFS[@]}"; do + cmds+=("$bfs $2 -color") + done + + for fd in "${FD[@]}"; do + cmds+=("$fd -u --search-path $2 --color=always") + done + + do-hyperfine "${cmds[@]}" +} + +# All printing benchmarks +bench-print() { + if (($#)); then + group "Printing paths" + + subgroup "Without colors" + for corpus; do + bench-print-nocolor "$corpus ${TAGS[$corpus]}" "bench/corpus/$corpus" + done + + subgroup "With colors" + for corpus; do + bench-print-color "$corpus ${TAGS[$corpus]}" "bench/corpus/$corpus" + done + fi +} + +# Benchmark search strategies +bench-strategies-corpus() { + subgroup "$1" + + for bfs in "${BFS[@]}"; do + subsubgroup "$bfs" + cmds=("$bfs -S "{bfs,dfs,ids,eds}" $2") + do-hyperfine "${cmds[@]}" + done +} + +# All search strategy benchmarks +bench-strategies() { + if (($#)); then + group "Search strategies" + + for corpus; do + bench-strategies-corpus "$corpus ${TAGS[$corpus]}" "bench/corpus/$corpus" + done + fi +} + +# Print benchmarked versions +bench-versions() { + subgroup "Versions" + + local md="$BENCH_DIR/bench.md" + + printf '```console\n' >>"$md" + + { + for bfs in "${BFS[@]}"; do + printf '$ %s --version | head -n1\n' "$bfs" + "$bfs" --version | head -n1 + done + + for find in "${FIND[@]}"; do + printf '$ %s --version | head -n1\n' "$find" + "$find" --version | head -n1 + done + + for fd in "${FD[@]}"; do + printf '$ %s --version\n' "$fd" + "$fd" --version + done + } | tee -a "$md" + + printf '```' >>"$md" +} + +# Print benchmark details +bench-details() { + group "Details" + + bench-versions +} + +# Run all the benchmarks +bench() { + import_array BFS + import_array FIND + import_array FD + + import_array COMPLETE + import_array EARLY_QUIT + import_array PRINT + import_array STRATEGIES + + bench-complete "${COMPLETE[@]}" + bench-early-quit "${EARLY_QUIT[@]}" + bench-print "${PRINT[@]}" + bench-strategies "${STRATEGIES[@]}" + bench-details +} -- cgit v1.2.3 From 692098fdb922c464949fad7c5b9e36b531ea6f68 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 2 Oct 2023 10:49:41 -0400 Subject: bench: Add a README --- bench/README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 bench/README.md (limited to 'bench') diff --git a/bench/README.md b/bench/README.md new file mode 100644 index 0000000..56157a0 --- /dev/null +++ b/bench/README.md @@ -0,0 +1,51 @@ +This directory contains a suite of benchmarks used to evaluate `bfs` and detect performance regressions. +To run them, you'll need the [tailfin] benchmark harness. +You can read the full usage information with + +[tailfin]: https://github.com/tavianator/tailfin + +```console +$ tailfin -n run bench/bench.sh --help +Usage: tailfin run bench/bench.sh [--default] + [--complete] [--early-quit] [--print] [--strategies] + [--build=...] [--bfs] [--find] [--fd] + [--no-clean] [--help] +... +``` + +The benchmarks use various git repositories to have a realistic and reproducible directory structure as a corpus. +Currently, those are the [Linux], [Rust], and [Chromium] repos. +The scripts will automatically clone those repos using [partial clone] filters to avoid downloading the actual file contents, saving bandwidth and space. + +[Linux]: https://github.com/torvalds/linux.git +[Rust]: https://github.com/rust-lang/rust.git +[Chromium]: https://chromium.googlesource.com/chromium/src.git +[partial clone]: https://git-scm.com/docs/partial-clone + +You can try out a quick benchmark by running + +```console +$ tailfin run bench/bench.sh --build=main --complete=linux +``` + +This will build the `main` branch, and measure the complete traversal of the Linux repo. +Results will be both printed to the console and saved in a Markdown file, which you can find by running + +```console +$ tailfin latest +results/2023/09/29/15:32:49 +$ cat results/2023/09/29/15:32:49/runs/1/bench.md +## Complete traversal +... +``` + +To measure performance improvements/regressions of a change, compare the `main` branch to the topic branch on the full benchmark suite: + +```console +$ tailfin run bench/bench.sh --build=main --build=branch --default +``` + +This will take a few minutes. +Results from the full benchmark suite can be seen in performance-related pull requests, for example [#126]. + +[#126]: https://github.com/tavianator/bfs/pull/126 -- cgit v1.2.3 From 20cf56fd225d341ab1ae1c6924e73b7e28583b1c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 10 Oct 2023 11:14:18 -0400 Subject: bench: Allow format specifiers in (sub)group headers --- bench/bench.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index 09564ac..e0943d4 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -266,17 +266,17 @@ do-hyperfine() { # Print the header for a benchmark group group() { - printf '## %s\n\n' "$1" | tee -a "$BENCH_DIR/bench.md" + printf "## $1\\n\\n" "${@:2}" | tee -a "$BENCH_DIR/bench.md" } # Print the header for a benchmark subgroup subgroup() { - printf '### %s\n\n' "$1" | tee -a "$BENCH_DIR/bench.md" + printf "### $1\\n\\n" "${@:2}" | tee -a "$BENCH_DIR/bench.md" } # Print the header for a benchmark sub-subgroup subsubgroup() { - printf '#### %s\n\n' "$1" | tee -a "$BENCH_DIR/bench.md" + printf "#### $1\\n\\n" "${@:2}" | tee -a "$BENCH_DIR/bench.md" } # Benchmark the complete traversal of a directory tree @@ -284,7 +284,7 @@ subsubgroup() { bench-complete-corpus() { total=$(./bin/bfs "$2" -printf '.' | wc -c) - subgroup "$1 ($total files)" + subgroup "%s (%'d files)" "$1" "$total" cmds=() for bfs in "${BFS[@]}"; do @@ -318,14 +318,14 @@ bench-early-quit-corpus() { dir="$2" max_depth=$(./bin/bfs "$dir" -printf '%d\n' | sort -rn | head -n1) - subgroup "$1 (depth $max_depth)" + subgroup '%s (depth %d)' "$1" "$max_depth" # Save the list of unique filenames, along with their depth UNIQ="$BENCH_DIR/uniq" ./bin/bfs "$dir" -printf '%d %f\n' | sort -k2 | uniq -uf1 >"$UNIQ" for ((i = 2; i <= max_depth; i *= 2)); do - subsubgroup "Depth $i" + subsubgroup 'Depth %d' "$i" # Sample random uniquely-named files at depth $i export FILES="$BENCH_DIR/uniq-$i" @@ -364,7 +364,7 @@ bench-early-quit() { # Benchmark printing paths without colors bench-print-nocolor() { - subsubgroup "$1" + subsubgroup '%s' "$1" cmds=() for bfs in "${BFS[@]}"; do @@ -384,7 +384,7 @@ bench-print-nocolor() { # Benchmark printing paths with colors bench-print-color() { - subsubgroup "$1" + subsubgroup '%s' "$1" cmds=() for bfs in "${BFS[@]}"; do @@ -417,10 +417,10 @@ bench-print() { # Benchmark search strategies bench-strategies-corpus() { - subgroup "$1" + subgroup '%s' "$1" for bfs in "${BFS[@]}"; do - subsubgroup "$bfs" + subsubgroup '%s' "$bfs" cmds=("$bfs -S "{bfs,dfs,ids,eds}" $2") do-hyperfine "${cmds[@]}" done -- cgit v1.2.3 From cad3a75f16a4f970e96830359cb62f3802ec0b6c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 10 Oct 2023 11:21:38 -0400 Subject: bench: Don't print paths in the --strategies benchmark --- bench/bench.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index e0943d4..1294fd1 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -421,7 +421,7 @@ bench-strategies-corpus() { for bfs in "${BFS[@]}"; do subsubgroup '%s' "$bfs" - cmds=("$bfs -S "{bfs,dfs,ids,eds}" $2") + cmds=("$bfs -S "{bfs,dfs,ids,eds}" $2 -false") do-hyperfine "${cmds[@]}" done } -- cgit v1.2.3 From 8719b66e6f0c5aac71e5245490269d4a644f6fe1 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 10 Oct 2023 11:34:26 -0400 Subject: bench: Group by strategy in comparison mode --- bench/bench.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index 1294fd1..8fe5ab9 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -419,11 +419,20 @@ bench-print() { bench-strategies-corpus() { subgroup '%s' "$1" - for bfs in "${BFS[@]}"; do - subsubgroup '%s' "$bfs" - cmds=("$bfs -S "{bfs,dfs,ids,eds}" $2 -false") + if ((${#BFS[@]} == 1)); then + cmds=("$BFS -S "{bfs,dfs,ids,eds}" $2 -false") do-hyperfine "${cmds[@]}" - done + else + for S in bfs dfs ids eds; do + subsubgroup '`-S %s`' "$S" + + cmds=() + for bfs in "${BFS[@]}"; do + cmds+=("$bfs -S $S $2 -false") + done + do-hyperfine "${cmds[@]}" + done + fi } # All search strategy benchmarks -- cgit v1.2.3 From 04786f44089a3d650330255770ee9dc5d22e4c02 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 10 Oct 2023 11:37:40 -0400 Subject: bench: New --jobs benchmark --- bench/bench.sh | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 4 deletions(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index 8fe5ab9..7042319 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -19,6 +19,7 @@ COMPLETE_DEFAULT=(linux rust chromium) EARLY_QUIT_DEFAULT=(chromium) PRINT_DEFAULT=(linux) STRATEGIES_DEFAULT=(linux) +JOBS_DEFAULT=(rust) usage() { printf 'Usage: tailfin run %s [--default]\n' "${BASH_SOURCE[0]}" @@ -35,15 +36,19 @@ usage() { printf ' --early-quit[=CORPUS]\n' printf ' Early quitting benchmark. \n' - printf ' Default corpus is --early-quit=chromium\n\n' "${EARLY_QUIT_DEFAULT[*]}" + printf ' Default corpus is --early-quit=%s\n\n' "${EARLY_QUIT_DEFAULT[*]}" printf ' --print[=CORPUS]\n' printf ' Path printing benchmark. \n' - printf ' Default corpus is --print=linux\n\n' "${PRINT_DEFAULT[*]}" + printf ' Default corpus is --print=%s\n\n' "${PRINT_DEFAULT[*]}" printf ' --strategies[=CORPUS]\n' printf ' Search strategy benchmark.\n' - printf ' Default corpus is --strategies=linux\n\n' "${STRATEGIES_DEFAULT[*]}" + printf ' Default corpus is --strategies=%s\n\n' "${STRATEGIES_DEFAULT[*]}" + + printf ' --jobs[=CORPUS]\n' + printf ' Parallelism benchmark.\n' + printf ' Default corpus is --jobs=%s\n\n' "${JOBS_DEFAULT[*]}" printf ' --build=COMMIT\n' printf ' Build this bfs commit and benchmark it. Specify multiple times to\n' @@ -103,6 +108,7 @@ setup() { EARLY_QUIT=() PRINT=() STRATEGIES=() + JOBS=() for arg; do case "$arg" in @@ -159,11 +165,18 @@ setup() { --strategies=*) read -ra STRATEGIES <<<"${arg#*=}" ;; + --jobs) + JOBS=("${JOBS_DEFAULT[@]}") + ;; + --jobs=*) + read -ra JOBS <<<"${arg#*=}" + ;; --default) COMPLETE=("${COMPLETE_DEFAULT[@]}") EARLY_QUIT=("${EARLY_QUIT_DEFAULT[@]}") PRINT=("${PRINT_DEFAULT[@]}") STRATEGIES=("${STRATEGIES_DEFAULT[@]}") + JOBS=("${JOBS_DEFAULT[@]}") ;; --help) usage @@ -187,7 +200,7 @@ setup() { as-user mkdir -p bench/corpus declare -A cloned=() - for corpus in "${COMPLETE[@]}" "${EARLY_QUIT[@]}" "${PRINT[@]}" "${STRATEGIES[@]}"; do + for corpus in "${COMPLETE[@]}" "${EARLY_QUIT[@]}" "${PRINT[@]}" "${STRATEGIES[@]}" "${JOBS[@]}"; do if ((cloned["$corpus"])); then continue fi @@ -236,6 +249,7 @@ setup() { export_array EARLY_QUIT export_array PRINT export_array STRATEGIES + export_array JOBS if ((UID == 0)); then turbo-off @@ -446,6 +460,50 @@ bench-strategies() { fi } +# Benchmark parallelism +bench-jobs-corpus() { + subgroup '%s' "$1" + + if ((${#BFS[@]} + ${#FD[@]} == 1)); then + cmds=() + for bfs in "${BFS[@]}"; do + cmds+=("$bfs -j"{1,2,3,4,6,8,12,16}" $2 -false") + done + + for fd in "${FD[@]}"; do + cmds+=("$fd -j"{1,2,3,4,6,8,12,16}" -u '^$' $2") + done + + do-hyperfine "${cmds[@]}" + else + for j in 1 2 3 4 6 8 12 16; do + subsubgroup '`-j%d`' $j + + cmds=() + for bfs in "${BFS[@]}"; do + cmds+=("$bfs -j$j $2 -false") + done + + for fd in "${FD[@]}"; do + cmds+=("$fd -j$j -u '^$' $2") + done + + do-hyperfine "${cmds[@]}" + done + fi +} + +# All parallelism benchmarks +bench-jobs() { + if (($#)); then + group "Parallelism" + + for corpus; do + bench-jobs-corpus "$corpus ${TAGS[$corpus]}" "bench/corpus/$corpus" + done + fi +} + # Print benchmarked versions bench-versions() { subgroup "Versions" @@ -491,10 +549,12 @@ bench() { import_array EARLY_QUIT import_array PRINT import_array STRATEGIES + import_array JOBS bench-complete "${COMPLETE[@]}" bench-early-quit "${EARLY_QUIT[@]}" bench-print "${PRINT[@]}" bench-strategies "${STRATEGIES[@]}" + bench-jobs "${JOBS[@]}" bench-details } -- cgit v1.2.3 From 778659e5fc758515428db40c353f45b201264802 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 10 Oct 2023 11:51:34 -0400 Subject: bench: Default to --strategies=rust --- bench/bench.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index 7042319..be5a15b 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -18,7 +18,7 @@ declare -gA TAGS=( COMPLETE_DEFAULT=(linux rust chromium) EARLY_QUIT_DEFAULT=(chromium) PRINT_DEFAULT=(linux) -STRATEGIES_DEFAULT=(linux) +STRATEGIES_DEFAULT=(rust) JOBS_DEFAULT=(rust) usage() { -- cgit v1.2.3 From 11feab9496ef610fa01f088155549b4d2d5e5c08 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 12 Oct 2023 18:21:55 -0400 Subject: bench: Check for bfs -j support before using it --- bench/bench.sh | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index be5a15b..ab56b79 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -467,7 +467,11 @@ bench-jobs-corpus() { if ((${#BFS[@]} + ${#FD[@]} == 1)); then cmds=() for bfs in "${BFS[@]}"; do - cmds+=("$bfs -j"{1,2,3,4,6,8,12,16}" $2 -false") + if "$bfs" -j1 -quit &>/dev/null; then + cmds+=("$bfs -j"{1,2,3,4,6,8,12,16}" $2 -false") + else + cmds+=("$bfs $2 -false") + fi done for fd in "${FD[@]}"; do @@ -481,14 +485,20 @@ bench-jobs-corpus() { cmds=() for bfs in "${BFS[@]}"; do - cmds+=("$bfs -j$j $2 -false") + if "$bfs" -j1 -quit &>/dev/null; then + cmds+=("$bfs -j$j $2 -false") + elif ((j == 1)); then + cmds+=("$bfs $2 -false") + fi done for fd in "${FD[@]}"; do cmds+=("$fd -j$j -u '^$' $2") done - do-hyperfine "${cmds[@]}" + if ((${#cmds[@]})); then + do-hyperfine "${cmds[@]}" + fi done fi } -- cgit v1.2.3 From b475307de3b168cf9a63fa4e73a84a46900c8df6 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 12 Oct 2023 18:27:31 -0400 Subject: bench: Look for ./bfs if ./bin/bfs doesn't exist --- bench/bench.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index ab56b79..0dfd7c4 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -228,7 +228,11 @@ setup() { cd "$worktree" as-user git checkout -qd "$commit" -- as-user make -s -j"$nproc" release - as-user cp ./bin/bfs "$bin/bfs-$commit" + if [ -e ./bin/bfs ]; then + as-user cp ./bin/bfs "$bin/bfs-$commit" + else + as-user cp ./bfs "$bin/bfs-$commit" + fi as-user make -s clean ) done -- cgit v1.2.3 From 55904f21f8565986fbd04cf0cfba486f936c08a6 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Oct 2023 08:03:04 -0400 Subject: bench: Account for tailfin's at-exit -> defer rename --- bench/bench.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index 0dfd7c4..b1d52bb 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -217,7 +217,7 @@ setup() { worktree="bench/worktree" as-user git worktree add -qd "$worktree" - at-exit as-user git worktree remove "$worktree" + defer as-user git worktree remove "$worktree" bin="$(realpath -- "$SETUP_DIR")/bin" as-user mkdir "$bin" @@ -241,7 +241,7 @@ setup() { # Work around this with a symlink tmp=$(as-user mktemp) as-user ln -sf "$bin" "$tmp" - at-exit rm "$tmp" + defer rm "$tmp" export PATH="$tmp:$PATH" fi -- cgit v1.2.3 From 5fb1ed2d9803bfa5eefdc49ae232f9ea4afff018 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 2 Nov 2023 11:53:50 -0400 Subject: bench: New -exec benchmarks --- bench/bench.sh | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index b1d52bb..3436b19 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -20,10 +20,11 @@ EARLY_QUIT_DEFAULT=(chromium) PRINT_DEFAULT=(linux) STRATEGIES_DEFAULT=(rust) JOBS_DEFAULT=(rust) +EXEC_DEFAULT=(linux) usage() { - printf 'Usage: tailfin run %s [--default]\n' "${BASH_SOURCE[0]}" - printf ' [--complete] [--early-quit] [--print] [--strategies]\n' + printf 'Usage: tailfin run %s\n' "${BASH_SOURCE[0]}" + printf ' [--default] [-- [--...]]\n' printf ' [--build=...] [--bfs] [--find] [--fd]\n' printf ' [--no-clean] [--help]\n\n' @@ -50,6 +51,10 @@ usage() { printf ' Parallelism benchmark.\n' printf ' Default corpus is --jobs=%s\n\n' "${JOBS_DEFAULT[*]}" + printf ' --exec[=CORPUS]\n' + printf ' Process spawning benchmark.\n' + printf ' Default corpus is --exec=%s\n\n' "${EXEC_DEFAULT[*]}" + printf ' --build=COMMIT\n' printf ' Build this bfs commit and benchmark it. Specify multiple times to\n' printf ' compare, e.g. --build=3.0.1 --build=3.0.2\n\n' @@ -109,6 +114,7 @@ setup() { PRINT=() STRATEGIES=() JOBS=() + EXEC=() for arg; do case "$arg" in @@ -171,12 +177,19 @@ setup() { --jobs=*) read -ra JOBS <<<"${arg#*=}" ;; + --exec) + EXEC=("${EXEC_DEFAULT[@]}") + ;; + --exec=*) + read -ra EXEC <<<"${arg#*=}" + ;; --default) COMPLETE=("${COMPLETE_DEFAULT[@]}") EARLY_QUIT=("${EARLY_QUIT_DEFAULT[@]}") PRINT=("${PRINT_DEFAULT[@]}") STRATEGIES=("${STRATEGIES_DEFAULT[@]}") JOBS=("${JOBS_DEFAULT[@]}") + EXEC=("${EXEC_DEFAULT[@]}") ;; --help) usage @@ -200,7 +213,7 @@ setup() { as-user mkdir -p bench/corpus declare -A cloned=() - for corpus in "${COMPLETE[@]}" "${EARLY_QUIT[@]}" "${PRINT[@]}" "${STRATEGIES[@]}" "${JOBS[@]}"; do + for corpus in "${COMPLETE[@]}" "${EARLY_QUIT[@]}" "${PRINT[@]}" "${STRATEGIES[@]}" "${JOBS[@]}" "${EXEC[@]}"; do if ((cloned["$corpus"])); then continue fi @@ -254,6 +267,7 @@ setup() { export_array PRINT export_array STRATEGIES export_array JOBS + export_array EXEC if ((UID == 0)); then turbo-off @@ -518,6 +532,76 @@ bench-jobs() { fi } +# One file/process +bench-exec-single() { + subsubgroup "One file per process" + + cmds=() + for cmd in "${BFS[@]}" "${FIND[@]}"; do + cmds+=("$cmd $1 -maxdepth 2 -exec true -- {} \;") + done + + for fd in "${FD[@]}"; do + cmds+=("$fd -u --search-path $1 --max-depth=2 -x true --") + # Without -j1, fd runs multiple processes in parallel, which is unfair + cmds+=("$fd -j1 -u --search-path $1 --max-depth=2 -x true --") + done + + do-hyperfine "${cmds[@]}" +} + +# Many files/process +bench-exec-multi() { + subsubgroup "Many files per process" + + cmds=() + for cmd in "${BFS[@]}" "${FIND[@]}"; do + cmds+=("$cmd $1 -exec true -- {} +") + done + + for fd in "${FD[@]}"; do + cmds+=("$fd -u --search-path $1 -X true --") + done + + do-hyperfine "${cmds[@]}" +} + +# Many files, same dir +bench-exec-chdir() { + if ((${#BFS[@]} + ${#FIND[@]} == 0)); then + return + fi + + subsubgroup "Spawn in parent directory" + + cmds=() + for cmd in "${BFS[@]}" "${FIND[@]}"; do + cmds+=("$cmd $1 -maxdepth 3 -execdir true -- {} +") + done + + do-hyperfine "${cmds[@]}" +} + +# Benchmark process spawning +bench-exec-corpus() { + subgroup '%s' "$1" + + bench-exec-single "$2" + bench-exec-multi "$2" + bench-exec-chdir "$2" +} + +# All process spawning benchmarks +bench-exec() { + if (($#)); then + group "Process spawning" + + for corpus; do + bench-exec-corpus "$corpus ${TAGS[$corpus]}" "bench/corpus/$corpus" + done + fi +} + # Print benchmarked versions bench-versions() { subgroup "Versions" @@ -564,11 +648,13 @@ bench() { import_array PRINT import_array STRATEGIES import_array JOBS + import_array EXEC bench-complete "${COMPLETE[@]}" bench-early-quit "${EARLY_QUIT[@]}" bench-print "${PRINT[@]}" bench-strategies "${STRATEGIES[@]}" bench-jobs "${JOBS[@]}" + bench-exec "${EXEC[@]}" bench-details } -- cgit v1.2.3 From 27924356725df884a5351db3e930171af2ea0488 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 12 Feb 2024 13:20:50 -0500 Subject: bench: Add a stat() benchmark --- bench/bench.sh | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index 3436b19..e4b5511 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -17,6 +17,7 @@ declare -gA TAGS=( COMPLETE_DEFAULT=(linux rust chromium) EARLY_QUIT_DEFAULT=(chromium) +STAT_DEFAULT=(rust) PRINT_DEFAULT=(linux) STRATEGIES_DEFAULT=(rust) JOBS_DEFAULT=(rust) @@ -32,15 +33,19 @@ usage() { printf ' Run the default set of benchmarks\n\n' printf ' --complete[=CORPUS]\n' - printf ' Complete traversal benchmark. \n' + printf ' Complete traversal benchmark.\n' printf ' Default corpus is --complete="%s"\n\n' "${COMPLETE_DEFAULT[*]}" printf ' --early-quit[=CORPUS]\n' - printf ' Early quitting benchmark. \n' + printf ' Early quitting benchmark.\n' printf ' Default corpus is --early-quit=%s\n\n' "${EARLY_QUIT_DEFAULT[*]}" + printf ' --stat[=CORPUS]\n' + printf ' Traversal with stat().\n' + printf ' Default corpus is --stat=%s\n\n' "${STAT_DEFAULT[*]}" + printf ' --print[=CORPUS]\n' - printf ' Path printing benchmark. \n' + printf ' Path printing benchmark.\n' printf ' Default corpus is --print=%s\n\n' "${PRINT_DEFAULT[*]}" printf ' --strategies[=CORPUS]\n' @@ -111,6 +116,7 @@ setup() { COMPLETE=() EARLY_QUIT=() + STAT=() PRINT=() STRATEGIES=() JOBS=() @@ -159,6 +165,12 @@ setup() { --early-quit=*) read -ra EARLY_QUIT <<<"${arg#*=}" ;; + --stat) + STAT=("${STAT_DEFAULT[@]}") + ;; + --stat=*) + read -ra STAT <<<"${arg#*=}" + ;; --print) PRINT=("${PRINT_DEFAULT[@]}") ;; @@ -186,6 +198,7 @@ setup() { --default) COMPLETE=("${COMPLETE_DEFAULT[@]}") EARLY_QUIT=("${EARLY_QUIT_DEFAULT[@]}") + STAT=("${STAT_DEFAULT[@]}") PRINT=("${PRINT_DEFAULT[@]}") STRATEGIES=("${STRATEGIES_DEFAULT[@]}") JOBS=("${JOBS_DEFAULT[@]}") @@ -213,7 +226,7 @@ setup() { as-user mkdir -p bench/corpus declare -A cloned=() - for corpus in "${COMPLETE[@]}" "${EARLY_QUIT[@]}" "${PRINT[@]}" "${STRATEGIES[@]}" "${JOBS[@]}" "${EXEC[@]}"; do + for corpus in "${COMPLETE[@]}" "${EARLY_QUIT[@]}" "${STAT[@]}" "${PRINT[@]}" "${STRATEGIES[@]}" "${JOBS[@]}" "${EXEC[@]}"; do if ((cloned["$corpus"])); then continue fi @@ -264,6 +277,7 @@ setup() { export_array COMPLETE export_array EARLY_QUIT + export_array STAT export_array PRINT export_array STRATEGIES export_array JOBS @@ -394,6 +408,39 @@ bench-early-quit() { fi } +# Benchmark traversal with stat() +bench-stat-corpus() { + total=$(./bin/bfs "$2" -printf '.' | wc -c) + + subgroup "%s (%'d files)" "$1" "$total" + + cmds=() + for bfs in "${BFS[@]}"; do + cmds+=("$bfs $2 -size 1024G") + done + + for find in "${FIND[@]}"; do + cmds+=("$find $2 -size 1024G") + done + + for fd in "${FD[@]}"; do + cmds+=("$fd -u --search-path $2 --size 1024Gi") + done + + do-hyperfine "${cmds[@]}" +} + +# stat() benchmarks +bench-stat() { + if (($#)); then + group "Traversal with stat()" + + for corpus; do + bench-stat-corpus "$corpus ${TAGS[$corpus]}" "bench/corpus/$corpus" + done + fi +} + # Benchmark printing paths without colors bench-print-nocolor() { subsubgroup '%s' "$1" @@ -645,6 +692,7 @@ bench() { import_array COMPLETE import_array EARLY_QUIT + import_array STAT import_array PRINT import_array STRATEGIES import_array JOBS @@ -652,6 +700,7 @@ bench() { bench-complete "${COMPLETE[@]}" bench-early-quit "${EARLY_QUIT[@]}" + bench-stat "${STAT[@]}" bench-print "${PRINT[@]}" bench-strategies "${STRATEGIES[@]}" bench-jobs "${JOBS[@]}" -- cgit v1.2.3 From 83dd04452498408c1d46b7282ed08e9f5a1e76a8 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 25 Apr 2024 13:49:07 -0400 Subject: bench: Run make config when building --- bench/bench.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index e4b5511..1d379ca 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -221,7 +221,8 @@ setup() { fi echo "Building bfs ..." - as-user make -s -j"$nproc" release all + as-user make -s -j"$nproc" RELEASE=y + as-user make -s -j"$nproc" all as-user mkdir -p bench/corpus @@ -253,7 +254,13 @@ setup() { echo "Building bfs $commit ..." cd "$worktree" as-user git checkout -qd "$commit" -- - as-user make -s -j"$nproc" release + ls + if [ -e config ]; then + as-user make -s -j"$nproc" config RELEASE=1 + as-user make -s -j"$nproc" + else + as-user make -s -j"$nproc" release + fi if [ -e ./bin/bfs ]; then as-user cp ./bin/bfs "$bin/bfs-$commit" else -- cgit v1.2.3 From b8ed989642b9f0f6c1301bcff6f1498935cbd81c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 27 Apr 2024 14:59:10 -0400 Subject: bench: Get rid of stray ls --- bench/bench.sh | 1 - 1 file changed, 1 deletion(-) (limited to 'bench') diff --git a/bench/bench.sh b/bench/bench.sh index 1d379ca..1526fe5 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -254,7 +254,6 @@ setup() { echo "Building bfs $commit ..." cd "$worktree" as-user git checkout -qd "$commit" -- - ls if [ -e config ]; then as-user make -s -j"$nproc" config RELEASE=1 as-user make -s -j"$nproc" -- cgit v1.2.3 From 37caa3d71fd8bb4d0d9204e4a2f5cac234fa25fd Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 29 Apr 2024 15:30:39 -0400 Subject: build: Replace `make config` with a `./configure` script This lets us do more traditional out-of-tree builds like $ ../path/to/bfs/configure $ make The .mk files are moved from ./config to ./build, mostly so that ./configure will auto-complete easily. --- .github/workflows/ci.yml | 8 +- .github/workflows/codecov.yml | 2 +- .github/workflows/codeql.yml | 2 +- Makefile | 143 +++++++++++++++--------------- README.md | 4 +- bench/bench.sh | 6 +- build/cc.sh | 16 ++++ build/config.mk | 62 +++++++++++++ build/define-if.sh | 20 +++++ build/deps.mk | 18 ++++ build/empty.c | 6 ++ build/exports.mk | 20 +++++ build/flags.mk | 136 ++++++++++++++++++++++++++++ build/has/acl-get-entry.c | 8 ++ build/has/acl-get-file.c | 8 ++ build/has/acl-get-tag-type.c | 10 +++ build/has/acl-is-trivial-np.c | 12 +++ build/has/acl-trivial.c | 8 ++ build/has/aligned-alloc.c | 8 ++ build/has/confstr.c | 9 ++ build/has/extattr-get-file.c | 10 +++ build/has/extattr-get-link.c | 10 +++ build/has/extattr-list-file.c | 10 +++ build/has/extattr-list-link.c | 10 +++ build/has/fdclosedir.c | 8 ++ build/has/getdents.c | 10 +++ build/has/getdents64-syscall.c | 12 +++ build/has/getdents64.c | 10 +++ build/has/getprogname-gnu.c | 9 ++ build/has/getprogname.c | 9 ++ build/has/max-align-t.c | 8 ++ build/has/pipe2.c | 10 +++ build/has/posix-spawn-addfchdir-np.c | 11 +++ build/has/posix-spawn-addfchdir.c | 11 +++ build/has/st-acmtim.c | 12 +++ build/has/st-acmtimespec.c | 12 +++ build/has/st-birthtim.c | 9 ++ build/has/st-birthtimespec.c | 9 ++ build/has/st-flags.c | 9 ++ build/has/statx-syscall.c | 13 +++ build/has/statx.c | 11 +++ build/has/strerror-l.c | 11 +++ build/has/strerror-r-gnu.c | 11 +++ build/has/strerror-r-posix.c | 11 +++ build/has/tm-gmtoff.c | 9 ++ build/has/uselocale.c | 9 ++ build/header.mk | 69 +++++++++++++++ build/pkgconf.sh | 100 +++++++++++++++++++++ build/pkgs.mk | 36 ++++++++ build/prelude.mk | 133 ++++++++++++++++++++++++++++ build/use/libacl.c | 9 ++ build/use/libcap.c | 9 ++ build/use/libselinux.c | 9 ++ build/use/liburing.c | 9 ++ build/use/oniguruma.c | 9 ++ config/cc.sh | 16 ---- config/config.mk | 63 ------------- config/define-if.sh | 20 ----- config/deps.mk | 18 ---- config/empty.c | 6 -- config/exports.mk | 20 ----- config/flags.mk | 136 ---------------------------- config/has/acl-get-entry.c | 8 -- config/has/acl-get-file.c | 8 -- config/has/acl-get-tag-type.c | 10 --- config/has/acl-is-trivial-np.c | 12 --- config/has/acl-trivial.c | 8 -- config/has/aligned-alloc.c | 8 -- config/has/confstr.c | 9 -- config/has/extattr-get-file.c | 10 --- config/has/extattr-get-link.c | 10 --- config/has/extattr-list-file.c | 10 --- config/has/extattr-list-link.c | 10 --- config/has/fdclosedir.c | 8 -- config/has/getdents.c | 10 --- config/has/getdents64-syscall.c | 12 --- config/has/getdents64.c | 10 --- config/has/getprogname-gnu.c | 9 -- config/has/getprogname.c | 9 -- config/has/max-align-t.c | 8 -- config/has/pipe2.c | 10 --- config/has/posix-spawn-addfchdir-np.c | 11 --- config/has/posix-spawn-addfchdir.c | 11 --- config/has/st-acmtim.c | 12 --- config/has/st-acmtimespec.c | 12 --- config/has/st-birthtim.c | 9 -- config/has/st-birthtimespec.c | 9 -- config/has/st-flags.c | 9 -- config/has/statx-syscall.c | 13 --- config/has/statx.c | 11 --- config/has/strerror-l.c | 11 --- config/has/strerror-r-gnu.c | 11 --- config/has/strerror-r-posix.c | 11 --- config/has/tm-gmtoff.c | 9 -- config/has/uselocale.c | 9 -- config/header.mk | 69 --------------- config/pkgconf.sh | 100 --------------------- config/pkgs.mk | 36 -------- config/prelude.mk | 162 ---------------------------------- config/use/libacl.c | 9 -- config/use/libcap.c | 9 -- config/use/libselinux.c | 9 -- config/use/liburing.c | 9 -- config/use/oniguruma.c | 9 -- configure | 98 ++++++++++++++++++++ docs/BUILDING.md | 47 +++++----- tests/util.sh | 9 +- tests/xspawn.c | 15 +--- 108 files changed, 1199 insertions(+), 1141 deletions(-) create mode 100755 build/cc.sh create mode 100644 build/config.mk create mode 100755 build/define-if.sh create mode 100644 build/deps.mk create mode 100644 build/empty.c create mode 100644 build/exports.mk create mode 100644 build/flags.mk create mode 100644 build/has/acl-get-entry.c create mode 100644 build/has/acl-get-file.c create mode 100644 build/has/acl-get-tag-type.c create mode 100644 build/has/acl-is-trivial-np.c create mode 100644 build/has/acl-trivial.c create mode 100644 build/has/aligned-alloc.c create mode 100644 build/has/confstr.c create mode 100644 build/has/extattr-get-file.c create mode 100644 build/has/extattr-get-link.c create mode 100644 build/has/extattr-list-file.c create mode 100644 build/has/extattr-list-link.c create mode 100644 build/has/fdclosedir.c create mode 100644 build/has/getdents.c create mode 100644 build/has/getdents64-syscall.c create mode 100644 build/has/getdents64.c create mode 100644 build/has/getprogname-gnu.c create mode 100644 build/has/getprogname.c create mode 100644 build/has/max-align-t.c create mode 100644 build/has/pipe2.c create mode 100644 build/has/posix-spawn-addfchdir-np.c create mode 100644 build/has/posix-spawn-addfchdir.c create mode 100644 build/has/st-acmtim.c create mode 100644 build/has/st-acmtimespec.c create mode 100644 build/has/st-birthtim.c create mode 100644 build/has/st-birthtimespec.c create mode 100644 build/has/st-flags.c create mode 100644 build/has/statx-syscall.c create mode 100644 build/has/statx.c create mode 100644 build/has/strerror-l.c create mode 100644 build/has/strerror-r-gnu.c create mode 100644 build/has/strerror-r-posix.c create mode 100644 build/has/tm-gmtoff.c create mode 100644 build/has/uselocale.c create mode 100644 build/header.mk create mode 100755 build/pkgconf.sh create mode 100644 build/pkgs.mk create mode 100644 build/prelude.mk create mode 100644 build/use/libacl.c create mode 100644 build/use/libcap.c create mode 100644 build/use/libselinux.c create mode 100644 build/use/liburing.c create mode 100644 build/use/oniguruma.c delete mode 100755 config/cc.sh delete mode 100644 config/config.mk delete mode 100755 config/define-if.sh delete mode 100644 config/deps.mk delete mode 100644 config/empty.c delete mode 100644 config/exports.mk delete mode 100644 config/flags.mk delete mode 100644 config/has/acl-get-entry.c delete mode 100644 config/has/acl-get-file.c delete mode 100644 config/has/acl-get-tag-type.c delete mode 100644 config/has/acl-is-trivial-np.c delete mode 100644 config/has/acl-trivial.c delete mode 100644 config/has/aligned-alloc.c delete mode 100644 config/has/confstr.c delete mode 100644 config/has/extattr-get-file.c delete mode 100644 config/has/extattr-get-link.c delete mode 100644 config/has/extattr-list-file.c delete mode 100644 config/has/extattr-list-link.c delete mode 100644 config/has/fdclosedir.c delete mode 100644 config/has/getdents.c delete mode 100644 config/has/getdents64-syscall.c delete mode 100644 config/has/getdents64.c delete mode 100644 config/has/getprogname-gnu.c delete mode 100644 config/has/getprogname.c delete mode 100644 config/has/max-align-t.c delete mode 100644 config/has/pipe2.c delete mode 100644 config/has/posix-spawn-addfchdir-np.c delete mode 100644 config/has/posix-spawn-addfchdir.c delete mode 100644 config/has/st-acmtim.c delete mode 100644 config/has/st-acmtimespec.c delete mode 100644 config/has/st-birthtim.c delete mode 100644 config/has/st-birthtimespec.c delete mode 100644 config/has/st-flags.c delete mode 100644 config/has/statx-syscall.c delete mode 100644 config/has/statx.c delete mode 100644 config/has/strerror-l.c delete mode 100644 config/has/strerror-r-gnu.c delete mode 100644 config/has/strerror-r-posix.c delete mode 100644 config/has/tm-gmtoff.c delete mode 100644 config/has/uselocale.c delete mode 100644 config/header.mk delete mode 100755 config/pkgconf.sh delete mode 100644 config/pkgs.mk delete mode 100644 config/prelude.mk delete mode 100644 config/use/libacl.c delete mode 100644 config/use/libcap.c delete mode 100644 config/use/libselinux.c delete mode 100644 config/use/liburing.c delete mode 100644 config/use/oniguruma.c create mode 100755 configure (limited to 'bench') diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3890bb0..57f59f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: gmake \ oniguruma jobs=$(sysctl -n hw.ncpu) - gmake -j$jobs config + MAKE=gmake ./configure gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" netbsd: @@ -133,7 +133,7 @@ jobs: pkgconf \ tcl-expect jobs=$(sysctl -n hw.ncpu) - make -j$jobs config + ./configure make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" dragonflybsd: @@ -165,7 +165,7 @@ jobs: run: | chown -R action:action . jobs=$(sysctl -n hw.ncpu) - sudo -u action make config + sudo -u action ./configure sudo -u action make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" omnios: @@ -198,5 +198,5 @@ jobs: PATH="/usr/xpg4/bin:$PATH" chown -R action:staff . jobs=$(getconf NPROCESSORS_ONLN) - sudo -u action gmake config + sudo -u action MAKE=gmake ./configure sudo -u action gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 2245f40..bb68927 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -25,7 +25,7 @@ jobs: - name: Generate coverage run: | - make config GCOV=y + ./configure GCOV=y make -j$(nproc) check TEST_FLAGS="--sudo" gcov -abcfpu obj/*/*.o diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c21fda5..a0b8fe3 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,7 +41,7 @@ jobs: - name: Configure run: | - make -j$(nproc) config + ./configure - name: Initialize CodeQL uses: github/codeql-action/init@v3 diff --git a/Makefile b/Makefile index 981c322..2fb35fd 100644 --- a/Makefile +++ b/Makefile @@ -1,56 +1,50 @@ # Copyright © Tavian Barnes # SPDX-License-Identifier: 0BSD -# This Makefile implements the configuration and build steps for bfs. It is -# portable to both GNU make and most BSD make implementations. To build bfs, -# run +# To build bfs, run # -# $ make config +# $ ./configure # $ make # Utilities and GNU/BSD portability -include config/prelude.mk +include build/prelude.mk # The default build target default: bfs .PHONY: default # Include the generated build config, if it exists --include ${CONFIG} +-include gen/config.mk -## Configuration phase (`make config`) - -# The configuration goal itself -config:: - @+${MAKE} -sf config/config.mk +## Configuration phase (`./configure`) # bfs used to have flag-like targets (`make release`, `make asan ubsan`, etc.). # Direct users to the new configuration system. asan lsan msan tsan ubsan gcov lint release:: - @printf 'error: `%s %s` is no longer supported. ' "${MAKE}" $@ >&2 - @printf 'Use `%s config %s=y` instead.\n' "${MAKE}" $$(echo $@ | tr 'a-z' 'A-Z') >&2 + @printf 'error: `%s %s` is no longer supported. Use `./configure %s=y` instead.\n' \ + "${MAKE}" $@ $$(echo $@ | tr 'a-z' 'A-Z') >&2 @false -# Print an error if `make` is run before `make config` -${CONFIG}:: +# Print an error if `make` is run before `./configure` +gen/config.mk:: @if ! [ -e $@ ]; then \ - printf 'error: You must run `%s config` before `%s`.\n' "${MAKE}" "${MAKE}" >&2; \ + printf 'error: You must run `./configure` before `%s`.\n' "${MAKE}" >&2; \ false; \ fi ## Build phase (`make`) # The main binary -bfs: ${BIN}/bfs +bfs: bin/bfs .PHONY: bfs # All binaries BINS := \ - ${BIN}/bfs \ - ${BIN}/tests/mksock \ - ${BIN}/tests/units \ - ${BIN}/tests/xspawnee \ - ${BIN}/tests/xtouch + bin/bfs \ + bin/tests/mksock \ + bin/tests/units \ + bin/tests/xspawnee \ + bin/tests/xtouch all: ${BINS} .PHONY: all @@ -60,52 +54,50 @@ ALL_CFLAGS = ${CPPFLAGS} ${CFLAGS} ${DEPFLAGS} ALL_LDFLAGS = ${CFLAGS} ${LDFLAGS} # The main binary -${BIN}/bfs: ${LIBBFS} ${OBJ}/src/main.o +bin/bfs: ${LIBBFS} obj/src/main.o ${BINS}: @${MKDIR} ${@D} - +${MSG} "[ LD ] ${TGT}" ${CC} ${ALL_LDFLAGS} ${.ALLSRC} ${LDLIBS} -o $@ + +${MSG} "[ LD ] $@" ${CC} ${ALL_LDFLAGS} ${.ALLSRC} ${LDLIBS} -o $@ ${POSTLINK} # Get the .c file for a .o file -_CSRC = ${@:${OBJ}/%.o=%.c} -CSRC = ${_CSRC:gen/%=${GEN}/%} +CSRC = ${@:obj/%.o=%.c} -# Depend on ${CONFIG} to make sure `make config` runs first, and to rebuild when -# the configuration changes -${OBJS}: ${CONFIG} +# Rebuild when the configuration changes +${OBJS}: gen/config.mk @${MKDIR} ${@D} - ${MSG} "[ CC ] ${_CSRC}" ${CC} ${ALL_CFLAGS} -c ${CSRC} -o $@ + ${MSG} "[ CC ] ${CSRC}" ${CC} ${ALL_CFLAGS} -c ${CSRC} -o $@ # Save the version number to this file, but only update version.c if it changes -${GEN}/version.c.new:: +gen/version.c.new:: @${MKDIR} ${@D} @printf 'const char bfs_version[] = "' >$@ @if [ "$$VERSION" ]; then \ printf '%s' "$$VERSION"; \ - elif test -d .git && command -v git >/dev/null 2>&1; then \ - git describe --always --dirty; \ + elif test -e src/../.git && command -v git >/dev/null 2>&1; then \ + git -C src/.. describe --always --dirty; \ else \ echo "3.1.3"; \ fi | tr -d '\n' >>$@ @printf '";\n' >>$@ -${GEN}/version.c: ${GEN}/version.c.new +gen/version.c: gen/version.c.new @test -e $@ && cmp -s $@ ${.ALLSRC} && rm ${.ALLSRC} || mv ${.ALLSRC} $@ -${OBJ}/gen/version.o: ${GEN}/version.c +obj/gen/version.o: gen/version.c ## Test phase (`make check`) # Unit test binaries UTEST_BINS := \ - ${BIN}/tests/units \ - ${BIN}/tests/xspawnee + bin/tests/units \ + bin/tests/xspawnee # Integration test binaries ITEST_BINS := \ - ${BIN}/tests/mksock \ - ${BIN}/tests/xtouch + bin/tests/mksock \ + bin/tests/xtouch # Build (but don't run) test binaries tests: ${UTEST_BINS} ${ITEST_BINS} @@ -117,45 +109,45 @@ check: unit-tests integration-tests # Run the unit tests unit-tests: ${UTEST_BINS} - ${MSG} "[TEST] tests/units" ${BIN}/tests/units + ${MSG} "[TEST] tests/units" bin/tests/units .PHONY: unit-tests -${BIN}/tests/units: \ +bin/tests/units: \ ${UNIT_OBJS} \ ${LIBBFS} -${BIN}/tests/xspawnee: \ - ${OBJ}/tests/xspawnee.o +bin/tests/xspawnee: \ + obj/tests/xspawnee.o # The different flag combinations we check INTEGRATIONS := default dfs ids eds j1 j2 j3 s INTEGRATION_TESTS := ${INTEGRATIONS:%=check-%} # Check just `bfs` -check-default: ${BIN}/bfs ${ITEST_BINS} +check-default: bin/bfs ${ITEST_BINS} +${MSG} "[TEST] bfs" \ - ./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs" ${TEST_FLAGS} + ./tests/tests.sh --make="${MAKE}" --bfs="bin/bfs" ${TEST_FLAGS} # Check the different search strategies -check-dfs check-ids check-eds: ${BIN}/bfs ${ITEST_BINS} +check-dfs check-ids check-eds: bin/bfs ${ITEST_BINS} +${MSG} "[TEST] bfs -S ${@:check-%=%}" \ - ./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -S ${@:check-%=%}" ${TEST_FLAGS} + ./tests/tests.sh --make="${MAKE}" --bfs="bin/bfs -S ${@:check-%=%}" ${TEST_FLAGS} # Check various flags -check-j1 check-j2 check-j3 check-s: ${BIN}/bfs ${ITEST_BINS} +check-j1 check-j2 check-j3 check-s: bin/bfs ${ITEST_BINS} +${MSG} "[TEST] bfs -${@:check-%=%}" \ - ./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -${@:check-%=%}" ${TEST_FLAGS} + ./tests/tests.sh --make="${MAKE}" --bfs="bin/bfs -${@:check-%=%}" ${TEST_FLAGS} # Run the integration tests integration-tests: ${INTEGRATION_TESTS} .PHONY: integration-tests -${BIN}/tests/mksock: \ - ${OBJ}/tests/mksock.o \ +bin/tests/mksock: \ + obj/tests/mksock.o \ ${LIBBFS} -${BIN}/tests/xtouch: \ - ${OBJ}/tests/xtouch.o \ +bin/tests/xtouch: \ + obj/tests/xtouch.o \ ${LIBBFS} # `make distcheck` configurations @@ -183,8 +175,10 @@ DISTCHECK_CONFIG_m32 := EXTRA_CFLAGS="-m32" PKG_CONFIG_LIBDIR=/usr/lib32/pkgconf DISTCHECK_CONFIG_release := RELEASE=y ${DISTCHECKS}:: - +${MAKE} -rs BUILDDIR=${BUILDDIR}/$@ config ${DISTCHECK_CONFIG_${@:distcheck-%=%}} - +${MAKE} -s BUILDDIR=${BUILDDIR}/$@ check TEST_FLAGS="--sudo --verbose=skipped" + @${MKDIR} $@ + @+cd $@ \ + && ../configure ${DISTCHECK_CONFIG_${@:distcheck-%=%}} \ + && ${MAKE} -s check TEST_FLAGS="--sudo --verbose=skipped" ## Packaging (`make install`) @@ -193,44 +187,49 @@ DEST_MANDIR := ${DESTDIR}${MANDIR} install:: ${Q}${MKDIR} ${DEST_PREFIX}/bin - ${MSG} "[INSTALL] bin/bfs" \ - ${INSTALL} -m755 ${BIN}/bfs ${DEST_PREFIX}/bin/bfs + ${MSG} "[INST] bin/bfs" \ + ${INSTALL} -m755 bin/bfs ${DEST_PREFIX}/bin/bfs ${Q}${MKDIR} ${DEST_MANDIR}/man1 - ${MSG} "[INSTALL] man/man1/bfs.1" \ + ${MSG} "[INST] man/man1/bfs.1" \ ${INSTALL} -m644 docs/bfs.1 ${DEST_MANDIR}/man1/bfs.1 ${Q}${MKDIR} ${DEST_PREFIX}/share/bash-completion/completions - ${MSG} "[INSTALL] completions/bfs.bash" \ + ${MSG} "[INST] completions/bfs.bash" \ ${INSTALL} -m644 completions/bfs.bash ${DEST_PREFIX}/share/bash-completion/completions/bfs ${Q}${MKDIR} ${DEST_PREFIX}/share/zsh/site-functions - ${MSG} "[INSTALL] completions/bfs.zsh" \ + ${MSG} "[INST] completions/bfs.zsh" \ ${INSTALL} -m644 completions/bfs.zsh ${DEST_PREFIX}/share/zsh/site-functions/_bfs ${Q}${MKDIR} ${DEST_PREFIX}/share/fish/vendor_completions.d - ${MSG} "[INSTALL] completions/bfs.fish" \ + ${MSG} "[INST] completions/bfs.fish" \ ${INSTALL} -m644 completions/bfs.fish ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish uninstall:: - ${RM} ${DEST_PREFIX}/share/bash-completion/completions/bfs - ${RM} ${DEST_PREFIX}/share/zsh/site-functions/_bfs - ${RM} ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish - ${RM} ${DEST_MANDIR}/man1/bfs.1 - ${RM} ${DEST_PREFIX}/bin/bfs + ${MSG} "[ RM ] completions/bfs.bash" \ + ${RM} ${DEST_PREFIX}/share/bash-completion/completions/bfs + ${MSG} "[ RM ] completions/bfs.zsh" \ + ${RM} ${DEST_PREFIX}/share/zsh/site-functions/_bfs + ${MSG} "[ RM ] completions/bfs.fish" \ + ${RM} ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish + ${MSG} "[ RM ] man/man1/bfs.1" \ + ${RM} ${DEST_MANDIR}/man1/bfs.1 + ${MSG} "[ RM ] bin/bfs" \ + ${RM} ${DEST_PREFIX}/bin/bfs # Check that `make install` works and `make uninstall` removes everything check-install:: - +${MAKE} install DESTDIR=${BUILDDIR}/pkg - +${MAKE} uninstall DESTDIR=${BUILDDIR}/pkg - ${BIN}/bfs ${BUILDDIR}/pkg -not -type d -print -exit 1 - ${RM} -r ${BUILDDIR}/pkg + +${MAKE} install DESTDIR=pkg + +${MAKE} uninstall DESTDIR=pkg + bin/bfs pkg -not -type d -print -exit 1 + ${RM} -r pkg ## Cleanup (`make clean`) # Clean all build products clean:: ${MSG} "[ RM ] bin obj" \ - ${RM} -r ${BIN} ${OBJ} + ${RM} -r bin obj # Clean everything, including generated files distclean: clean ${MSG} "[ RM ] gen" \ - ${RM} -r ${GEN} ${DISTCHECKS} + ${RM} -r gen ${DISTCHECKS} .PHONY: distclean diff --git a/README.md b/README.md index b95c16b..922ebdc 100644 --- a/README.md +++ b/README.md @@ -333,7 +333,7 @@ Once you have the dependencies, you can build bfs. Download one of the [releases](https://github.com/tavianator/bfs/releases) or clone the [git repo](https://github.com/tavianator/bfs). Then run - $ make config + $ ./configure $ make This will build the `./bin/bfs` binary. @@ -343,7 +343,7 @@ Run the test suite to make sure it works correctly: If you're interested in speed, you may want to build the release version instead: - $ make config RELEASE=y + $ ./configure RELEASE=y $ make Finally, if you want to install it globally, run diff --git a/bench/bench.sh b/bench/bench.sh index 1526fe5..ba46599 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -221,7 +221,7 @@ setup() { fi echo "Building bfs ..." - as-user make -s -j"$nproc" RELEASE=y + as-user ./configure RELEASE=y as-user make -s -j"$nproc" all as-user mkdir -p bench/corpus @@ -254,8 +254,8 @@ setup() { echo "Building bfs $commit ..." cd "$worktree" as-user git checkout -qd "$commit" -- - if [ -e config ]; then - as-user make -s -j"$nproc" config RELEASE=1 + if [ -e configure ]; then + as-user ./configure RELEASE=1 as-user make -s -j"$nproc" else as-user make -s -j"$nproc" release diff --git a/build/cc.sh b/build/cc.sh new file mode 100755 index 0000000..45d51ca --- /dev/null +++ b/build/cc.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Run the compiler and check if it succeeded + +set -eu + +TMP=$(mktemp) +trap 'rm -f "$TMP"' EXIT + +( + set -x + $XCC $XCPPFLAGS $XCFLAGS $XLDFLAGS "$@" $XLDLIBS -o "$TMP" +) diff --git a/build/config.mk b/build/config.mk new file mode 100644 index 0000000..153838d --- /dev/null +++ b/build/config.mk @@ -0,0 +1,62 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that implements `./configure` + +include build/prelude.mk +include build/exports.mk + +# All configuration steps +config: gen/config.mk gen/config.h +.PHONY: config + +# Makefile fragments generated by `./configure` +MKS := \ + gen/vars.mk \ + gen/flags.mk \ + gen/deps.mk \ + gen/pkgs.mk + +# The main configuration file, which includes the others +gen/config.mk: ${MKS} + ${MSG} "[ GEN] $@" + @printf '# %s\n' "$@" >$@ + @printf 'include %s\n' ${.ALLSRC} >>$@ + ${VCAT} gen/config.mk +.PHONY: gen/config.mk + +# Saves the configurable variables +gen/vars.mk:: + @${MKDIR} ${@D} + ${MSG} "[ GEN] $@" + @printf '# %s\n' "$@" >$@ + @printf 'PREFIX := %s\n' "$$XPREFIX" >>$@ + @printf 'MANDIR := %s\n' "$$XMANDIR" >>$@ + @printf 'OS := %s\n' "$${OS:-$$(uname)}" >>$@ + @printf 'CC := %s\n' "$$XCC" >>$@ + @printf 'INSTALL := %s\n' "$$XINSTALL" >>$@ + @printf 'MKDIR := %s\n' "$$XMKDIR" >>$@ + @printf 'PKG_CONFIG := %s\n' "$$XPKG_CONFIG" >>$@ + @printf 'RM := %s\n' "$$XRM" >>$@ + ${VCAT} $@ + +# Sets the build flags. This depends on vars.mk and uses a recursive make so +# that the default flags can depend on variables like ${OS}. +gen/flags.mk: gen/vars.mk + @+${MAKE} -sf build/flags.mk $@ +.PHONY: gen/flags.mk + +# Check for dependency generation support +gen/deps.mk: gen/flags.mk + @+${MAKE} -sf build/deps.mk $@ +.PHONY: gen/deps.mk + +# Auto-detect dependencies and their build flags +gen/pkgs.mk: gen/flags.mk + @+${MAKE} -sf build/pkgs.mk $@ +.PHONY: gen/pkgs.mk + +# Compile-time feature detection +gen/config.h: gen/config.mk + @+${MAKE} -sf build/header.mk $@ +.PHONY: gen/config.h diff --git a/build/define-if.sh b/build/define-if.sh new file mode 100755 index 0000000..295ead8 --- /dev/null +++ b/build/define-if.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Output a C preprocessor definition based on whether a command succeeds + +set -eu + +SLUG="${1#build/}" +SLUG="${SLUG%.c}" +MACRO="BFS_$(printf '%s' "$SLUG" | tr '/a-z-' '_A-Z_')" +shift + +if "$@"; then + printf '#define %s true\n' "$MACRO" +else + printf '#define %s false\n' "$MACRO" + exit 1 +fi diff --git a/build/deps.mk b/build/deps.mk new file mode 100644 index 0000000..e8dd9fb --- /dev/null +++ b/build/deps.mk @@ -0,0 +1,18 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that generates gen/deps.mk + +include build/prelude.mk +include gen/vars.mk +include gen/flags.mk +include build/exports.mk + +gen/deps.mk:: + ${MSG} "[ GEN] $@" + printf '# %s\n' "$@" >$@ + if build/cc.sh -MD -MP -MF /dev/null build/empty.c; then \ + printf 'DEPFLAGS := -MD -MP\n'; \ + fi >>$@ 2>$@.log + ${VCAT} $@ + printf -- '-include %s\n' ${OBJS:.o=.d} >>$@ diff --git a/build/empty.c b/build/empty.c new file mode 100644 index 0000000..4fa9a5b --- /dev/null +++ b/build/empty.c @@ -0,0 +1,6 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +int main(void) { + return 0; +} diff --git a/build/exports.mk b/build/exports.mk new file mode 100644 index 0000000..ed19134 --- /dev/null +++ b/build/exports.mk @@ -0,0 +1,20 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile fragment that exports variables used by configuration scripts + +export XPREFIX=${PREFIX} +export XMANDIR=${MANDIR} + +export XCC=${CC} +export XINSTALL=${INSTALL} +export XMKDIR=${MKDIR} +export XPKG_CONFIG=${PKG_CONFIG} +export XRM=${RM} + +export XCPPFLAGS=${CPPFLAGS} +export XCFLAGS=${CFLAGS} +export XLDFLAGS=${LDFLAGS} +export XLDLIBS=${LDLIBS} + +export XNOLIBS=${NOLIBS} diff --git a/build/flags.mk b/build/flags.mk new file mode 100644 index 0000000..0a44e94 --- /dev/null +++ b/build/flags.mk @@ -0,0 +1,136 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that generates gen/flags.mk + +include build/prelude.mk +include gen/vars.mk + +# Configurable flags +CPPFLAGS ?= +CFLAGS ?= \ + -g \ + -Wall \ + -Wformat=2 \ + -Werror=implicit \ + -Wimplicit-fallthrough \ + -Wmissing-declarations \ + -Wshadow \ + -Wsign-compare \ + -Wstrict-prototypes +LDFLAGS ?= +LDLIBS ?= + +export XCPPFLAGS=${CPPFLAGS} +export XCFLAGS=${CFLAGS} +export XLDFLAGS=${LDFLAGS} +export XLDLIBS=${LDLIBS} + +# Immutable flags +export BFS_CPPFLAGS= \ + -Isrc \ + -Igen \ + -D__EXTENSIONS__ \ + -D_ATFILE_SOURCE \ + -D_BSD_SOURCE \ + -D_DARWIN_C_SOURCE \ + -D_DEFAULT_SOURCE \ + -D_GNU_SOURCE \ + -D_POSIX_PTHREAD_SEMANTICS \ + -D_FILE_OFFSET_BITS=64 \ + -D_TIME_BITS=64 +export BFS_CFLAGS= -std=c17 -pthread + +# Platform-specific system libraries +LDLIBS,DragonFly := -lposix1e +LDLIBS,Linux := -lrt +LDLIBS,NetBSD := -lutil +LDLIBS,SunOS := -lsec -lsocket -lnsl +export BFS_LDLIBS=${LDLIBS,${OS}} + +# Build profiles +_ASAN := ${TRUTHY,${ASAN}} +_LSAN := ${TRUTHY,${LSAN}} +_MSAN := ${TRUTHY,${MSAN}} +_TSAN := ${TRUTHY,${TSAN}} +_UBSAN := ${TRUTHY,${UBSAN}} +_GCOV := ${TRUTHY,${GCOV}} +_LINT := ${TRUTHY,${LINT}} +_RELEASE := ${TRUTHY,${RELEASE}} + +# https://github.com/google/sanitizers/issues/342 +TSAN_CPPFLAGS,y := -DBFS_USE_TARGET_CLONES=0 +export TSAN_CPPFLAGS=${TSAN_CPPFLAGS,${_TSAN}} + +ASAN_CFLAGS,y := -fsanitize=address +LSAN_CFLAGS,y := -fsanitize=leak +MSAN_CFLAGS,y := -fsanitize=memory -fsanitize-memory-track-origins +TSAN_CFLAGS,y := -fsanitize=thread +UBSAN_CFLAGS.y := -fsanitize=undefined + +export ASAN_CFLAGS=${ASAN_CFLAGS,${_ASAN}} +export LSAN_CFLAGS=${LSAN_CFLAGS,${_LSAN}} +export MSAN_CFLAGS=${MSAN_CFLAGS,${_MSAN}} +export TSAN_CFLAGS=${TSAN_CFLAGS,${_TSAN}} +export UBSAN_CFLAGS=${UBSAN_CFLAGS,${_UBSAN}} + +SAN_CFLAGS,y := -fno-sanitize-recover=all +INSANE := ${NOT,${_ASAN}${_LSAN}${_MSAN}${_TSAN}${_UBSAN}} +SAN := ${NOT,${INSANE}} +export SAN_CFLAGS=${SAN_CFLAGS,${SAN}} + +# MSAN and TSAN both need all code to be instrumented +YESLIBS := ${NOT,${_MSAN}${_TSAN}} +NOLIBS ?= ${NOT,${YESLIBS}} +export XNOLIBS=${NOLIBS} + +# gcov only intercepts fork()/exec() with -std=gnu* +GCOV_CFLAGS,y := -std=gnu17 --coverage +export GCOV_CFLAGS=${GCOV_CFLAGS,${_GCOV}} + +LINT_CPPFLAGS,y := -D_FORTIFY_SOURCE=3 -DBFS_LINT +LINT_CFLAGS,y := -Werror -O2 + +export LINT_CPPFLAGS=${LINT_CPPFLAGS,${_LINT}} +export LINT_CFLAGS=${LINT_CFLAGS,${_LINT}} + +RELEASE_CPPFLAGS,y := -DNDEBUG +RELEASE_CFLAGS,y := -O3 -flto=auto + +export RELEASE_CPPFLAGS=${RELEASE_CPPFLAGS,${_RELEASE}} +export RELEASE_CFLAGS=${RELEASE_CFLAGS,${_RELEASE}} + +# Set a variable +SETVAR = printf '%s := %s\n' >>$@ + +# Append to a variable, if non-empty +APPEND = append() { test -z "$$2" || printf '%s += %s\n' "$$1" "$$2" >>$@; }; append + +gen/flags.mk:: + ${MSG} "[ GEN] $@" + printf '# %s\n' "$@" >$@ + ${SETVAR} CPPFLAGS "$$BFS_CPPFLAGS" + ${APPEND} CPPFLAGS "$$TSAN_CPPFLAGS" + ${APPEND} CPPFLAGS "$$LINT_CPPFLAGS" + ${APPEND} CPPFLAGS "$$RELEASE_CPPFLAGS" + ${APPEND} CPPFLAGS "$$XCPPFLAGS" + ${APPEND} CPPFLAGS "$$EXTRA_CPPFLAGS" + ${SETVAR} CFLAGS "$$BFS_CFLAGS" + ${APPEND} CFLAGS "$$ASAN_CFLAGS" + ${APPEND} CFLAGS "$$LSAN_CFLAGS" + ${APPEND} CFLAGS "$$MSAN_CFLAGS" + ${APPEND} CFLAGS "$$TSAN_CFLAGS" + ${APPEND} CFLAGS "$$UBSAN_CFLAGS" + ${APPEND} CFLAGS "$$SAN_CFLAGS" + ${APPEND} CFLAGS "$$GCOV_CFLAGS" + ${APPEND} CFLAGS "$$LINT_CFLAGS" + ${APPEND} CFLAGS "$$RELEASE_CFLAGS" + ${APPEND} CFLAGS "$$XCFLAGS" + ${APPEND} CFLAGS "$$EXTRA_CFLAGS" + ${SETVAR} LDFLAGS "$$XLDFLAGS" + ${SETVAR} LDLIBS "$$XLDLIBS" + ${APPEND} LDLIBS "$$EXTRA_LDLIBS" + ${APPEND} LDLIBS "$$BFS_LDLIBS" + ${SETVAR} NOLIBS "$$XNOLIBS" + test "${OS}-${SAN}" != FreeBSD-y || printf 'POSTLINK = elfctl -e +noaslr $$@\n' >>$@ + ${VCAT} $@ diff --git a/build/has/acl-get-entry.c b/build/has/acl-get-entry.c new file mode 100644 index 0000000..3cce771 --- /dev/null +++ b/build/has/acl-get-entry.c @@ -0,0 +1,8 @@ +#include +#include + +int main(void) { + acl_t acl = acl_get_file(".", ACL_TYPE_DEFAULT); + acl_entry_t entry; + return acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); +} diff --git a/build/has/acl-get-file.c b/build/has/acl-get-file.c new file mode 100644 index 0000000..89fbf23 --- /dev/null +++ b/build/has/acl-get-file.c @@ -0,0 +1,8 @@ +#include +#include +#include + +int main(void) { + acl_t acl = acl_get_file(".", ACL_TYPE_DEFAULT); + return acl == (acl_t)NULL; +} diff --git a/build/has/acl-get-tag-type.c b/build/has/acl-get-tag-type.c new file mode 100644 index 0000000..2901956 --- /dev/null +++ b/build/has/acl-get-tag-type.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int main(void) { + acl_entry_t entry; + memset(&entry, 0, sizeof(entry)); + acl_tag_t tag; + return acl_get_tag_type(entry, &tag); +} diff --git a/build/has/acl-is-trivial-np.c b/build/has/acl-is-trivial-np.c new file mode 100644 index 0000000..9ca9fc7 --- /dev/null +++ b/build/has/acl-is-trivial-np.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + acl_t acl = acl_get_fd(3); + int trivial; + acl_is_trivial_np(acl, &trivial); + return 0; +} diff --git a/build/has/acl-trivial.c b/build/has/acl-trivial.c new file mode 100644 index 0000000..7efc838 --- /dev/null +++ b/build/has/acl-trivial.c @@ -0,0 +1,8 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + return acl_trivial("."); +} diff --git a/build/has/aligned-alloc.c b/build/has/aligned-alloc.c new file mode 100644 index 0000000..4460038 --- /dev/null +++ b/build/has/aligned-alloc.c @@ -0,0 +1,8 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + return !aligned_alloc(_Alignof(void *), sizeof(void *)); +} diff --git a/build/has/confstr.c b/build/has/confstr.c new file mode 100644 index 0000000..58280b4 --- /dev/null +++ b/build/has/confstr.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + confstr(_CS_PATH, NULL, 0); + return 0; +} diff --git a/build/has/extattr-get-file.c b/build/has/extattr-get-file.c new file mode 100644 index 0000000..ac9cf96 --- /dev/null +++ b/build/has/extattr-get-file.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + return extattr_get_file("file", EXTATTR_NAMESPACE_USER, "xattr", NULL, 0); +} diff --git a/build/has/extattr-get-link.c b/build/has/extattr-get-link.c new file mode 100644 index 0000000..c35be5b --- /dev/null +++ b/build/has/extattr-get-link.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + return extattr_get_link("link", EXTATTR_NAMESPACE_USER, "xattr", NULL, 0); +} diff --git a/build/has/extattr-list-file.c b/build/has/extattr-list-file.c new file mode 100644 index 0000000..e68a8bb --- /dev/null +++ b/build/has/extattr-list-file.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + return extattr_list_file("file", EXTATTR_NAMESPACE_USER, NULL, 0); +} diff --git a/build/has/extattr-list-link.c b/build/has/extattr-list-link.c new file mode 100644 index 0000000..49f0ec2 --- /dev/null +++ b/build/has/extattr-list-link.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + return extattr_list_link("link", EXTATTR_NAMESPACE_USER, NULL, 0); +} diff --git a/build/has/fdclosedir.c b/build/has/fdclosedir.c new file mode 100644 index 0000000..f4ad1f5 --- /dev/null +++ b/build/has/fdclosedir.c @@ -0,0 +1,8 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + return fdclosedir(opendir(".")); +} diff --git a/build/has/getdents.c b/build/has/getdents.c new file mode 100644 index 0000000..d0d4228 --- /dev/null +++ b/build/has/getdents.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct dirent de; + getdents(3, &de, 1024); + return 0; +} diff --git a/build/has/getdents64-syscall.c b/build/has/getdents64-syscall.c new file mode 100644 index 0000000..4838c14 --- /dev/null +++ b/build/has/getdents64-syscall.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + struct dirent64 de; + syscall(SYS_getdents64, 3, &de, 1024); + return 0; +} diff --git a/build/has/getdents64.c b/build/has/getdents64.c new file mode 100644 index 0000000..1abf36d --- /dev/null +++ b/build/has/getdents64.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct dirent64 de; + getdents64(3, &de, 1024); + return 0; +} diff --git a/build/has/getprogname-gnu.c b/build/has/getprogname-gnu.c new file mode 100644 index 0000000..6b97c5e --- /dev/null +++ b/build/has/getprogname-gnu.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + const char *str = program_invocation_short_name; + return str[0]; +} diff --git a/build/has/getprogname.c b/build/has/getprogname.c new file mode 100644 index 0000000..83dc8e8 --- /dev/null +++ b/build/has/getprogname.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + const char *str = getprogname(); + return str[0]; +} diff --git a/build/has/max-align-t.c b/build/has/max-align-t.c new file mode 100644 index 0000000..96165ce --- /dev/null +++ b/build/has/max-align-t.c @@ -0,0 +1,8 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + return _Alignof(max_align_t); +} diff --git a/build/has/pipe2.c b/build/has/pipe2.c new file mode 100644 index 0000000..4cb43b5 --- /dev/null +++ b/build/has/pipe2.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + int fds[2]; + return pipe2(fds, O_CLOEXEC); +} diff --git a/build/has/posix-spawn-addfchdir-np.c b/build/has/posix-spawn-addfchdir-np.c new file mode 100644 index 0000000..b870a53 --- /dev/null +++ b/build/has/posix-spawn-addfchdir-np.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_addfchdir_np(&actions, 3); + return 0; +} diff --git a/build/has/posix-spawn-addfchdir.c b/build/has/posix-spawn-addfchdir.c new file mode 100644 index 0000000..c52ff81 --- /dev/null +++ b/build/has/posix-spawn-addfchdir.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_addfchdir(&actions, 3); + return 0; +} diff --git a/build/has/st-acmtim.c b/build/has/st-acmtim.c new file mode 100644 index 0000000..d687ab0 --- /dev/null +++ b/build/has/st-acmtim.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + unsigned int a = sb.st_atim.tv_sec; + unsigned int c = sb.st_ctim.tv_sec; + unsigned int m = sb.st_mtim.tv_sec; + return a + c + m; +} diff --git a/build/has/st-acmtimespec.c b/build/has/st-acmtimespec.c new file mode 100644 index 0000000..f747bc0 --- /dev/null +++ b/build/has/st-acmtimespec.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + unsigned int a = sb.st_atimespec.tv_sec; + unsigned int c = sb.st_ctimespec.tv_sec; + unsigned int m = sb.st_mtimespec.tv_sec; + return a + c + m; +} diff --git a/build/has/st-birthtim.c b/build/has/st-birthtim.c new file mode 100644 index 0000000..4964571 --- /dev/null +++ b/build/has/st-birthtim.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + return sb.st_birthtim.tv_sec; +} diff --git a/build/has/st-birthtimespec.c b/build/has/st-birthtimespec.c new file mode 100644 index 0000000..91a613f --- /dev/null +++ b/build/has/st-birthtimespec.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + return sb.st_birthtimespec.tv_sec; +} diff --git a/build/has/st-flags.c b/build/has/st-flags.c new file mode 100644 index 0000000..b1d0c32 --- /dev/null +++ b/build/has/st-flags.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + return sb.st_flags; +} diff --git a/build/has/statx-syscall.c b/build/has/statx-syscall.c new file mode 100644 index 0000000..87ec869 --- /dev/null +++ b/build/has/statx-syscall.c @@ -0,0 +1,13 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include +#include + +int main(void) { + struct statx sb; + syscall(SYS_statx, AT_FDCWD, ".", 0, STATX_BASIC_STATS, &sb); + return 0; +} diff --git a/build/has/statx.c b/build/has/statx.c new file mode 100644 index 0000000..65f1674 --- /dev/null +++ b/build/has/statx.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + struct statx sb; + statx(AT_FDCWD, ".", 0, STATX_BASIC_STATS, &sb); + return 0; +} diff --git a/build/has/strerror-l.c b/build/has/strerror-l.c new file mode 100644 index 0000000..3dcc4d7 --- /dev/null +++ b/build/has/strerror-l.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + locale_t locale = duplocale(LC_GLOBAL_LOCALE); + return !strerror_l(ENOMEM, locale); +} diff --git a/build/has/strerror-r-gnu.c b/build/has/strerror-r-gnu.c new file mode 100644 index 0000000..26ca0ee --- /dev/null +++ b/build/has/strerror-r-gnu.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + char buf[256]; + // Check that strerror_r() returns a pointer + return *strerror_r(ENOMEM, buf, sizeof(buf)); +} diff --git a/build/has/strerror-r-posix.c b/build/has/strerror-r-posix.c new file mode 100644 index 0000000..41b2d30 --- /dev/null +++ b/build/has/strerror-r-posix.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + char buf[256]; + // Check that strerror_r() returns an integer + return 2 * strerror_r(ENOMEM, buf, sizeof(buf)); +} diff --git a/build/has/tm-gmtoff.c b/build/has/tm-gmtoff.c new file mode 100644 index 0000000..543df48 --- /dev/null +++ b/build/has/tm-gmtoff.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct tm tm = {0}; + return tm.tm_gmtoff; +} diff --git a/build/has/uselocale.c b/build/has/uselocale.c new file mode 100644 index 0000000..a712ff8 --- /dev/null +++ b/build/has/uselocale.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + locale_t locale = uselocale((locale_t)0); + return locale == LC_GLOBAL_LOCALE; +} diff --git a/build/header.mk b/build/header.mk new file mode 100644 index 0000000..3f77ca5 --- /dev/null +++ b/build/header.mk @@ -0,0 +1,69 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that generates gen/config.h + +include build/prelude.mk +include gen/config.mk +include build/exports.mk + +# All header fragments we generate +HEADERS := \ + gen/has/acl-get-entry.h \ + gen/has/acl-get-file.h \ + gen/has/acl-get-tag-type.h \ + gen/has/acl-is-trivial-np.h \ + gen/has/acl-trivial.h \ + gen/has/aligned-alloc.h \ + gen/has/confstr.h \ + gen/has/extattr-get-file.h \ + gen/has/extattr-get-link.h \ + gen/has/extattr-list-file.h \ + gen/has/extattr-list-link.h \ + gen/has/fdclosedir.h \ + gen/has/getdents.h \ + gen/has/getdents64.h \ + gen/has/getdents64-syscall.h \ + gen/has/getprogname.h \ + gen/has/getprogname-gnu.h \ + gen/has/max-align-t.h \ + gen/has/pipe2.h \ + gen/has/posix-spawn-addfchdir.h \ + gen/has/posix-spawn-addfchdir-np.h \ + gen/has/st-acmtim.h \ + gen/has/st-acmtimespec.h \ + gen/has/st-birthtim.h \ + gen/has/st-birthtimespec.h \ + gen/has/st-flags.h \ + gen/has/statx.h \ + gen/has/statx-syscall.h \ + gen/has/strerror-l.h \ + gen/has/strerror-r-gnu.h \ + gen/has/strerror-r-posix.h \ + gen/has/tm-gmtoff.h \ + gen/has/uselocale.h + +# Previously generated by pkgs.mk +PKG_HEADERS := ${ALL_PKGS:%=gen/use/%.h} + +gen/config.h: ${PKG_HEADERS} ${HEADERS} + ${MSG} "[ GEN] $@" + printf '// %s\n' "$@" >$@ + printf '#ifndef BFS_CONFIG_H\n' >>$@ + printf '#define BFS_CONFIG_H\n' >>$@ + cat ${.ALLSRC} >>$@ + printf '#endif // BFS_CONFIG_H\n' >>$@ + cat ${.ALLSRC:%=%.log} >$@.log + ${VCAT} $@ +.PHONY: gen/config.h + +# The short name of the config test +SLUG = ${@:gen/%.h=%} + +${HEADERS}:: + ${MKDIR} ${@D} + if build/define-if.sh ${SLUG} build/cc.sh build/${SLUG}.c >$@ 2>$@.log; then \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' ${SLUG}.c; \ + else \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' ${SLUG}.c; \ + fi diff --git a/build/pkgconf.sh b/build/pkgconf.sh new file mode 100755 index 0000000..96e4bf1 --- /dev/null +++ b/build/pkgconf.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# pkg-config wrapper with hardcoded fallbacks + +set -eu + +MODE= +case "${1:-}" in + --*) + MODE="$1" + shift +esac + +if [ $# -lt 1 ]; then + exit +fi + +case "$XNOLIBS" in + y|1) + exit 1 +esac + +if [ -z "$MODE" ]; then + # Check whether the libraries exist at all + for LIB; do + # Check ${USE_$LIB} + USE_LIB="USE_$(printf '%s' "$LIB" | tr 'a-z-' 'A-Z_')" + eval "USE=\"\${$USE_LIB:-}\"" + case "$USE" in + y|1) + continue + ;; + n|0) + exit 1 + ;; + esac + + CFLAGS=$("$0" --cflags "$LIB") || exit 1 + LDFLAGS=$("$0" --ldflags "$LIB") || exit 1 + LDLIBS=$("$0" --ldlibs "$LIB") || exit 1 + build/cc.sh $CFLAGS $LDFLAGS build/use/$LIB.c $LDLIBS || exit 1 + done +fi + +# Defer to pkg-config if possible +if command -v "${XPKG_CONFIG:-}" >/dev/null 2>&1; then + case "$MODE" in + --cflags) + "$XPKG_CONFIG" --cflags "$@" + ;; + --ldflags) + "$XPKG_CONFIG" --libs-only-L --libs-only-other "$@" + ;; + --ldlibs) + "$XPKG_CONFIG" --libs-only-l "$@" + ;; + esac + + exit +fi + +# pkg-config unavailable, emulate it ourselves +CFLAGS="" +LDFLAGS="" +LDLIBS="" + +for LIB; do + case "$LIB" in + libacl) + LDLIB=-lacl + ;; + libcap) + LDLIB=-lcap + ;; + libselinux) + LDLIB=-lselinux + ;; + liburing) + LDLIB=-luring + ;; + oniguruma) + LDLIB=-lonig + ;; + *) + printf 'error: Unknown package %s\n' "$LIB" >&2 + exit 1 + ;; + esac + + LDLIBS="$LDLIBS$LDLIB " +done + +case "$MODE" in + --ldlibs) + printf '%s\n' "$LDLIBS" + ;; +esac diff --git a/build/pkgs.mk b/build/pkgs.mk new file mode 100644 index 0000000..51ca664 --- /dev/null +++ b/build/pkgs.mk @@ -0,0 +1,36 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that generates gen/pkgs.mk + +include build/prelude.mk +include gen/vars.mk +include gen/flags.mk +include build/exports.mk + +HEADERS := ${ALL_PKGS:%=gen/use/%.h} + +gen/pkgs.mk: ${HEADERS} + ${MSG} "[ GEN] $@" + printf '# %s\n' "$@" >$@ + gen() { \ + printf 'PKGS := %s\n' "$$*"; \ + printf 'CFLAGS += %s\n' "$$(build/pkgconf.sh --cflags "$$@")"; \ + printf 'LDFLAGS += %s\n' "$$(build/pkgconf.sh --ldflags "$$@")"; \ + printf 'LDLIBS := %s $${LDLIBS}\n' "$$(build/pkgconf.sh --ldlibs "$$@")"; \ + }; \ + gen $$(grep -l ' true$$' ${.ALLSRC} | sed 's|.*/\(.*\)\.h|\1|') >>$@ + ${VCAT} $@ + +.PHONY: gen/pkgs.mk + +# Convert gen/use/foo.h to foo +PKG = ${@:gen/use/%.h=%} + +${HEADERS}:: + ${MKDIR} ${@D} + if build/define-if.sh use/${PKG} build/pkgconf.sh ${PKG} >$@ 2>$@.log; then \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' use/${PKG}.c; \ + else \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' use/${PKG}.c; \ + fi diff --git a/build/prelude.mk b/build/prelude.mk new file mode 100644 index 0000000..b235fa9 --- /dev/null +++ b/build/prelude.mk @@ -0,0 +1,133 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Common makefile utilities. Compatible with both GNU make and most BSD makes. + +# BSD make will chdir into ${.OBJDIR} by default, unless we tell it not to +.OBJDIR: . + +# We don't use any suffix rules +.SUFFIXES: + +# GNU make has $^ for the full list of targets, while BSD make has $> and the +# long-form ${.ALLSRC}. We could write $^ $> to get them both, but that would +# break if one of them implemented support for the other. So instead, bring +# BSD's ${.ALLSRC} to GNU. +.ALLSRC ?= $^ + +# Installation paths +DESTDIR ?= +PREFIX ?= /usr +MANDIR ?= ${PREFIX}/share/man + +# Configurable executables +CC ?= cc +INSTALL ?= install +MKDIR ?= mkdir -p +PKG_CONFIG ?= pkg-config +RM ?= rm -f + +# GNU and BSD make have incompatible syntax for conditionals, but we can do a +# lot with just nested variable expansion. We use "y" as the canonical +# truthy value, and "" (the empty string) as the canonical falsey value. +# +# To normalize a boolean, use ${TRUTHY,${VAR}}, which expands like this: +# +# VAR=y ${TRUTHY,${VAR}} => ${TRUTHY,y} => y +# VAR=1 ${TRUTHY,${VAR}} => ${TRUTHY,1} => y +# VAR=n ${TRUTHY,${VAR}} => ${TRUTHY,n} => [empty] +# VAR=other ${TRUTHY,${VAR}} => ${TRUTHY,other} => [empty] +# VAR= ${TRUTHY,${VAR}} => ${TRUTHY,} => [emtpy] +# +# Inspired by https://github.com/wahern/autoguess +TRUTHY,y := y +TRUTHY,1 := y + +# Boolean operators are also implemented with nested expansion +NOT, := y + +# Normalize ${V} to either "y" or "" +IS_V := ${TRUTHY,${V}} + +# Suppress output unless V=1 +Q, := @ +Q := ${Q,${IS_V}} + +# Show full commands with `make V=1`, otherwise short summaries +MSG = @msg() { \ + MSG="$$1"; \ + shift; \ + test "${IS_V}" || printf '%s\n' "$$MSG"; \ + test "$${1:-}" || return 0; \ + test "${IS_V}" && printf '%s\n' "$$*"; \ + "$$@"; \ + }; \ + msg + +# Maximum width of a short message, to align the ✔/✘ +MSG_WIDTH := 30 + +# cat a file if V=1 +VCAT,y := @cat +VCAT, := @: +VCAT := ${VCAT,${IS_V}} + +# All external dependencies +ALL_PKGS := \ + libacl \ + libcap \ + libselinux \ + liburing \ + oniguruma + +# List all object files here, as they're needed by both `./configure` and `make` + +# All object files except the entry point +LIBBFS := \ + obj/src/alloc.o \ + obj/src/bar.o \ + obj/src/bfstd.o \ + obj/src/bftw.o \ + obj/src/color.o \ + obj/src/ctx.o \ + obj/src/diag.o \ + obj/src/dir.o \ + obj/src/dstring.o \ + obj/src/eval.o \ + obj/src/exec.o \ + obj/src/expr.o \ + obj/src/fsade.o \ + obj/src/ioq.o \ + obj/src/mtab.o \ + obj/src/opt.o \ + obj/src/parse.o \ + obj/src/printf.o \ + obj/src/pwcache.o \ + obj/src/stat.o \ + obj/src/thread.o \ + obj/src/trie.o \ + obj/src/typo.o \ + obj/src/xregex.o \ + obj/src/xspawn.o \ + obj/src/xtime.o \ + obj/gen/version.o + +# Unit test objects +UNIT_OBJS := \ + obj/tests/alloc.o \ + obj/tests/bfstd.o \ + obj/tests/bit.o \ + obj/tests/ioq.o \ + obj/tests/main.o \ + obj/tests/trie.o \ + obj/tests/xspawn.o \ + obj/tests/xtime.o + +# All object files +OBJS := \ + obj/src/main.o \ + obj/tests/mksock.o \ + obj/tests/xspawnee.o \ + obj/tests/xtouch.o \ + ${LIBBFS} \ + ${UNIT_OBJS} diff --git a/build/use/libacl.c b/build/use/libacl.c new file mode 100644 index 0000000..de1fe50 --- /dev/null +++ b/build/use/libacl.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + acl_free(0); + return 0; +} diff --git a/build/use/libcap.c b/build/use/libcap.c new file mode 100644 index 0000000..58e832c --- /dev/null +++ b/build/use/libcap.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + cap_free(0); + return 0; +} diff --git a/build/use/libselinux.c b/build/use/libselinux.c new file mode 100644 index 0000000..bca409d --- /dev/null +++ b/build/use/libselinux.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + freecon(0); + return 0; +} diff --git a/build/use/liburing.c b/build/use/liburing.c new file mode 100644 index 0000000..bea499a --- /dev/null +++ b/build/use/liburing.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + io_uring_free_probe(0); + return 0; +} diff --git a/build/use/oniguruma.c b/build/use/oniguruma.c new file mode 100644 index 0000000..cb17596 --- /dev/null +++ b/build/use/oniguruma.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + onig_free(0); + return 0; +} diff --git a/config/cc.sh b/config/cc.sh deleted file mode 100755 index 45d51ca..0000000 --- a/config/cc.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Run the compiler and check if it succeeded - -set -eu - -TMP=$(mktemp) -trap 'rm -f "$TMP"' EXIT - -( - set -x - $XCC $XCPPFLAGS $XCFLAGS $XLDFLAGS "$@" $XLDLIBS -o "$TMP" -) diff --git a/config/config.mk b/config/config.mk deleted file mode 100644 index 280c6ac..0000000 --- a/config/config.mk +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile fragment that implements `make config` - -include config/prelude.mk -include config/exports.mk - -# All configuration steps -config: ${CONFIG} ${GEN}/config.h -.PHONY: config - -# Makefile fragments generated by `make config` -MKS := \ - ${GEN}/vars.mk \ - ${GEN}/flags.mk \ - ${GEN}/deps.mk \ - ${GEN}/pkgs.mk - -# The main configuration file, which includes the others -${CONFIG}: ${MKS} - ${MSG} "[ GEN] ${TGT}" - @printf '# %s\n' "${TGT}" >$@ - @printf 'include $${GEN}/%s\n' ${MKS:${GEN}/%=%} >>$@ - ${VCAT} ${CONFIG} -.PHONY: ${CONFIG} - -# Saves the configurable variables -${GEN}/vars.mk:: - @${MKDIR} ${@D} - ${MSG} "[ GEN] ${TGT}" - @printf '# %s\n' "${TGT}" >$@ - @printf 'PREFIX := %s\n' "$$XPREFIX" >>$@ - @printf 'MANDIR := %s\n' "$$XMANDIR" >>$@ - @printf 'OS := %s\n' "$${OS:-$$(uname)}" >>$@ - @printf 'CC := %s\n' "$$XCC" >>$@ - @printf 'INSTALL := %s\n' "$$XINSTALL" >>$@ - @printf 'MKDIR := %s\n' "$$XMKDIR" >>$@ - @printf 'PKG_CONFIG := %s\n' "$$XPKG_CONFIG" >>$@ - @printf 'RM := %s\n' "$$XRM" >>$@ - @printf 'PKGS :=\n' >>$@ - ${VCAT} $@ - -# Sets the build flags. This depends on vars.mk and uses a recursive make so -# that the default flags can depend on variables like ${OS}. -${GEN}/flags.mk: ${GEN}/vars.mk - @+${MAKE} -sf config/flags.mk $@ -.PHONY: ${GEN}/flags.mk - -# Check for dependency generation support -${GEN}/deps.mk: ${GEN}/flags.mk - @+${MAKE} -sf config/deps.mk $@ -.PHONY: ${GEN}/deps.mk - -# Auto-detect dependencies and their build flags -${GEN}/pkgs.mk: ${GEN}/flags.mk - @+${MAKE} -sf config/pkgs.mk $@ -.PHONY: ${GEN}/pkgs.mk - -# Compile-time feature detection -${GEN}/config.h: ${CONFIG} - @+${MAKE} -sf config/header.mk $@ -.PHONY: ${GEN}/config.h diff --git a/config/define-if.sh b/config/define-if.sh deleted file mode 100755 index 059c1ac..0000000 --- a/config/define-if.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Output a C preprocessor definition based on whether a command succeeds - -set -eu - -SLUG="${1#config/}" -SLUG="${SLUG%.c}" -MACRO="BFS_$(printf '%s' "$SLUG" | tr '/a-z-' '_A-Z_')" -shift - -if "$@"; then - printf '#define %s true\n' "$MACRO" -else - printf '#define %s false\n' "$MACRO" - exit 1 -fi diff --git a/config/deps.mk b/config/deps.mk deleted file mode 100644 index ac394a5..0000000 --- a/config/deps.mk +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile that generates gen/deps.mk - -include config/prelude.mk -include ${GEN}/vars.mk -include ${GEN}/flags.mk -include config/exports.mk - -${GEN}/deps.mk:: - ${MSG} "[ GEN] ${TGT}" - printf '# %s\n' "${TGT}" >$@ - if config/cc.sh -MD -MP -MF /dev/null config/empty.c; then \ - printf 'DEPFLAGS = -MD -MP\n'; \ - fi >>$@ 2>$@.log - ${VCAT} $@ - printf -- '-include %s\n' ${OBJS:.o=.d} >>$@ diff --git a/config/empty.c b/config/empty.c deleted file mode 100644 index 4fa9a5b..0000000 --- a/config/empty.c +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -int main(void) { - return 0; -} diff --git a/config/exports.mk b/config/exports.mk deleted file mode 100644 index ed19134..0000000 --- a/config/exports.mk +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile fragment that exports variables used by configuration scripts - -export XPREFIX=${PREFIX} -export XMANDIR=${MANDIR} - -export XCC=${CC} -export XINSTALL=${INSTALL} -export XMKDIR=${MKDIR} -export XPKG_CONFIG=${PKG_CONFIG} -export XRM=${RM} - -export XCPPFLAGS=${CPPFLAGS} -export XCFLAGS=${CFLAGS} -export XLDFLAGS=${LDFLAGS} -export XLDLIBS=${LDLIBS} - -export XNOLIBS=${NOLIBS} diff --git a/config/flags.mk b/config/flags.mk deleted file mode 100644 index 837c30f..0000000 --- a/config/flags.mk +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile that generates gen/flags.mk - -include config/prelude.mk -include ${GEN}/vars.mk - -# Configurable flags -CPPFLAGS ?= -CFLAGS ?= \ - -g \ - -Wall \ - -Wformat=2 \ - -Werror=implicit \ - -Wimplicit-fallthrough \ - -Wmissing-declarations \ - -Wshadow \ - -Wsign-compare \ - -Wstrict-prototypes -LDFLAGS ?= -LDLIBS ?= - -export XCPPFLAGS=${CPPFLAGS} -export XCFLAGS=${CFLAGS} -export XLDFLAGS=${LDFLAGS} -export XLDLIBS=${LDLIBS} - -# Immutable flags -export BFS_CPPFLAGS= \ - -Isrc \ - -I${GEN} \ - -D__EXTENSIONS__ \ - -D_ATFILE_SOURCE \ - -D_BSD_SOURCE \ - -D_DARWIN_C_SOURCE \ - -D_DEFAULT_SOURCE \ - -D_GNU_SOURCE \ - -D_POSIX_PTHREAD_SEMANTICS \ - -D_FILE_OFFSET_BITS=64 \ - -D_TIME_BITS=64 -export BFS_CFLAGS= -std=c17 -pthread - -# Platform-specific system libraries -LDLIBS,DragonFly := -lposix1e -LDLIBS,Linux := -lrt -LDLIBS,NetBSD := -lutil -LDLIBS,SunOS := -lsec -lsocket -lnsl -export BFS_LDLIBS=${LDLIBS,${OS}} - -# Build profiles -_ASAN := ${TRUTHY,${ASAN}} -_LSAN := ${TRUTHY,${LSAN}} -_MSAN := ${TRUTHY,${MSAN}} -_TSAN := ${TRUTHY,${TSAN}} -_UBSAN := ${TRUTHY,${UBSAN}} -_GCOV := ${TRUTHY,${GCOV}} -_LINT := ${TRUTHY,${LINT}} -_RELEASE := ${TRUTHY,${RELEASE}} - -# https://github.com/google/sanitizers/issues/342 -TSAN_CPPFLAGS,y := -DBFS_USE_TARGET_CLONES=0 -export TSAN_CPPFLAGS=${TSAN_CPPFLAGS,${_TSAN}} - -ASAN_CFLAGS,y := -fsanitize=address -LSAN_CFLAGS,y := -fsanitize=leak -MSAN_CFLAGS,y := -fsanitize=memory -fsanitize-memory-track-origins -TSAN_CFLAGS,y := -fsanitize=thread -UBSAN_CFLAGS.y := -fsanitize=undefined - -export ASAN_CFLAGS=${ASAN_CFLAGS,${_ASAN}} -export LSAN_CFLAGS=${LSAN_CFLAGS,${_LSAN}} -export MSAN_CFLAGS=${MSAN_CFLAGS,${_MSAN}} -export TSAN_CFLAGS=${TSAN_CFLAGS,${_TSAN}} -export UBSAN_CFLAGS=${UBSAN_CFLAGS,${_UBSAN}} - -SAN_CFLAGS,y := -fno-sanitize-recover=all -INSANE := ${NOT,${_ASAN}${_LSAN}${_MSAN}${_TSAN}${_UBSAN}} -SAN := ${NOT,${INSANE}} -export SAN_CFLAGS=${SAN_CFLAGS,${SAN}} - -# MSAN and TSAN both need all code to be instrumented -YESLIBS := ${NOT,${_MSAN}${_TSAN}} -NOLIBS ?= ${NOT,${YESLIBS}} -export XNOLIBS=${NOLIBS} - -# gcov only intercepts fork()/exec() with -std=gnu* -GCOV_CFLAGS,y := -std=gnu17 --coverage -export GCOV_CFLAGS=${GCOV_CFLAGS,${_GCOV}} - -LINT_CPPFLAGS,y := -D_FORTIFY_SOURCE=3 -DBFS_LINT -LINT_CFLAGS,y := -Werror -O2 - -export LINT_CPPFLAGS=${LINT_CPPFLAGS,${_LINT}} -export LINT_CFLAGS=${LINT_CFLAGS,${_LINT}} - -RELEASE_CPPFLAGS,y := -DNDEBUG -RELEASE_CFLAGS,y := -O3 -flto=auto - -export RELEASE_CPPFLAGS=${RELEASE_CPPFLAGS,${_RELEASE}} -export RELEASE_CFLAGS=${RELEASE_CFLAGS,${_RELEASE}} - -# Set a variable -SETVAR = printf '%s := %s\n' >>$@ - -# Append to a variable, if non-empty -APPEND = append() { test -z "$$2" || printf '%s += %s\n' "$$1" "$$2" >>$@; }; append - -${GEN}/flags.mk:: - ${MSG} "[ GEN] ${TGT}" - printf '# %s\n' "${TGT}" >$@ - ${SETVAR} CPPFLAGS "$$BFS_CPPFLAGS" - ${APPEND} CPPFLAGS "$$TSAN_CPPFLAGS" - ${APPEND} CPPFLAGS "$$LINT_CPPFLAGS" - ${APPEND} CPPFLAGS "$$RELEASE_CPPFLAGS" - ${APPEND} CPPFLAGS "$$XCPPFLAGS" - ${APPEND} CPPFLAGS "$$EXTRA_CPPFLAGS" - ${SETVAR} CFLAGS "$$BFS_CFLAGS" - ${APPEND} CFLAGS "$$ASAN_CFLAGS" - ${APPEND} CFLAGS "$$LSAN_CFLAGS" - ${APPEND} CFLAGS "$$MSAN_CFLAGS" - ${APPEND} CFLAGS "$$TSAN_CFLAGS" - ${APPEND} CFLAGS "$$UBSAN_CFLAGS" - ${APPEND} CFLAGS "$$SAN_CFLAGS" - ${APPEND} CFLAGS "$$GCOV_CFLAGS" - ${APPEND} CFLAGS "$$LINT_CFLAGS" - ${APPEND} CFLAGS "$$RELEASE_CFLAGS" - ${APPEND} CFLAGS "$$XCFLAGS" - ${APPEND} CFLAGS "$$EXTRA_CFLAGS" - ${SETVAR} LDFLAGS "$$XLDFLAGS" - ${SETVAR} LDLIBS "$$XLDLIBS" - ${APPEND} LDLIBS "$$EXTRA_LDLIBS" - ${APPEND} LDLIBS "$$BFS_LDLIBS" - ${SETVAR} NOLIBS "$$XNOLIBS" - test "${OS}-${SAN}" != FreeBSD-y || printf 'POSTLINK = elfctl -e +noaslr $$@\n' >>$@ - ${VCAT} $@ diff --git a/config/has/acl-get-entry.c b/config/has/acl-get-entry.c deleted file mode 100644 index 3cce771..0000000 --- a/config/has/acl-get-entry.c +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -int main(void) { - acl_t acl = acl_get_file(".", ACL_TYPE_DEFAULT); - acl_entry_t entry; - return acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); -} diff --git a/config/has/acl-get-file.c b/config/has/acl-get-file.c deleted file mode 100644 index 89fbf23..0000000 --- a/config/has/acl-get-file.c +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include -#include - -int main(void) { - acl_t acl = acl_get_file(".", ACL_TYPE_DEFAULT); - return acl == (acl_t)NULL; -} diff --git a/config/has/acl-get-tag-type.c b/config/has/acl-get-tag-type.c deleted file mode 100644 index 2901956..0000000 --- a/config/has/acl-get-tag-type.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include -#include - -int main(void) { - acl_entry_t entry; - memset(&entry, 0, sizeof(entry)); - acl_tag_t tag; - return acl_get_tag_type(entry, &tag); -} diff --git a/config/has/acl-is-trivial-np.c b/config/has/acl-is-trivial-np.c deleted file mode 100644 index 9ca9fc7..0000000 --- a/config/has/acl-is-trivial-np.c +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - acl_t acl = acl_get_fd(3); - int trivial; - acl_is_trivial_np(acl, &trivial); - return 0; -} diff --git a/config/has/acl-trivial.c b/config/has/acl-trivial.c deleted file mode 100644 index 7efc838..0000000 --- a/config/has/acl-trivial.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - return acl_trivial("."); -} diff --git a/config/has/aligned-alloc.c b/config/has/aligned-alloc.c deleted file mode 100644 index 4460038..0000000 --- a/config/has/aligned-alloc.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - return !aligned_alloc(_Alignof(void *), sizeof(void *)); -} diff --git a/config/has/confstr.c b/config/has/confstr.c deleted file mode 100644 index 58280b4..0000000 --- a/config/has/confstr.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - confstr(_CS_PATH, NULL, 0); - return 0; -} diff --git a/config/has/extattr-get-file.c b/config/has/extattr-get-file.c deleted file mode 100644 index ac9cf96..0000000 --- a/config/has/extattr-get-file.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - return extattr_get_file("file", EXTATTR_NAMESPACE_USER, "xattr", NULL, 0); -} diff --git a/config/has/extattr-get-link.c b/config/has/extattr-get-link.c deleted file mode 100644 index c35be5b..0000000 --- a/config/has/extattr-get-link.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - return extattr_get_link("link", EXTATTR_NAMESPACE_USER, "xattr", NULL, 0); -} diff --git a/config/has/extattr-list-file.c b/config/has/extattr-list-file.c deleted file mode 100644 index e68a8bb..0000000 --- a/config/has/extattr-list-file.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - return extattr_list_file("file", EXTATTR_NAMESPACE_USER, NULL, 0); -} diff --git a/config/has/extattr-list-link.c b/config/has/extattr-list-link.c deleted file mode 100644 index 49f0ec2..0000000 --- a/config/has/extattr-list-link.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - return extattr_list_link("link", EXTATTR_NAMESPACE_USER, NULL, 0); -} diff --git a/config/has/fdclosedir.c b/config/has/fdclosedir.c deleted file mode 100644 index f4ad1f5..0000000 --- a/config/has/fdclosedir.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - return fdclosedir(opendir(".")); -} diff --git a/config/has/getdents.c b/config/has/getdents.c deleted file mode 100644 index d0d4228..0000000 --- a/config/has/getdents.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct dirent de; - getdents(3, &de, 1024); - return 0; -} diff --git a/config/has/getdents64-syscall.c b/config/has/getdents64-syscall.c deleted file mode 100644 index 4838c14..0000000 --- a/config/has/getdents64-syscall.c +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - struct dirent64 de; - syscall(SYS_getdents64, 3, &de, 1024); - return 0; -} diff --git a/config/has/getdents64.c b/config/has/getdents64.c deleted file mode 100644 index 1abf36d..0000000 --- a/config/has/getdents64.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct dirent64 de; - getdents64(3, &de, 1024); - return 0; -} diff --git a/config/has/getprogname-gnu.c b/config/has/getprogname-gnu.c deleted file mode 100644 index 6b97c5e..0000000 --- a/config/has/getprogname-gnu.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - const char *str = program_invocation_short_name; - return str[0]; -} diff --git a/config/has/getprogname.c b/config/has/getprogname.c deleted file mode 100644 index 83dc8e8..0000000 --- a/config/has/getprogname.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - const char *str = getprogname(); - return str[0]; -} diff --git a/config/has/max-align-t.c b/config/has/max-align-t.c deleted file mode 100644 index 96165ce..0000000 --- a/config/has/max-align-t.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - return _Alignof(max_align_t); -} diff --git a/config/has/pipe2.c b/config/has/pipe2.c deleted file mode 100644 index 4cb43b5..0000000 --- a/config/has/pipe2.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - int fds[2]; - return pipe2(fds, O_CLOEXEC); -} diff --git a/config/has/posix-spawn-addfchdir-np.c b/config/has/posix-spawn-addfchdir-np.c deleted file mode 100644 index b870a53..0000000 --- a/config/has/posix-spawn-addfchdir-np.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - posix_spawn_file_actions_t actions; - posix_spawn_file_actions_init(&actions); - posix_spawn_file_actions_addfchdir_np(&actions, 3); - return 0; -} diff --git a/config/has/posix-spawn-addfchdir.c b/config/has/posix-spawn-addfchdir.c deleted file mode 100644 index c52ff81..0000000 --- a/config/has/posix-spawn-addfchdir.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - posix_spawn_file_actions_t actions; - posix_spawn_file_actions_init(&actions); - posix_spawn_file_actions_addfchdir(&actions, 3); - return 0; -} diff --git a/config/has/st-acmtim.c b/config/has/st-acmtim.c deleted file mode 100644 index d687ab0..0000000 --- a/config/has/st-acmtim.c +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - unsigned int a = sb.st_atim.tv_sec; - unsigned int c = sb.st_ctim.tv_sec; - unsigned int m = sb.st_mtim.tv_sec; - return a + c + m; -} diff --git a/config/has/st-acmtimespec.c b/config/has/st-acmtimespec.c deleted file mode 100644 index f747bc0..0000000 --- a/config/has/st-acmtimespec.c +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - unsigned int a = sb.st_atimespec.tv_sec; - unsigned int c = sb.st_ctimespec.tv_sec; - unsigned int m = sb.st_mtimespec.tv_sec; - return a + c + m; -} diff --git a/config/has/st-birthtim.c b/config/has/st-birthtim.c deleted file mode 100644 index 4964571..0000000 --- a/config/has/st-birthtim.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - return sb.st_birthtim.tv_sec; -} diff --git a/config/has/st-birthtimespec.c b/config/has/st-birthtimespec.c deleted file mode 100644 index 91a613f..0000000 --- a/config/has/st-birthtimespec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - return sb.st_birthtimespec.tv_sec; -} diff --git a/config/has/st-flags.c b/config/has/st-flags.c deleted file mode 100644 index b1d0c32..0000000 --- a/config/has/st-flags.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - return sb.st_flags; -} diff --git a/config/has/statx-syscall.c b/config/has/statx-syscall.c deleted file mode 100644 index 87ec869..0000000 --- a/config/has/statx-syscall.c +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include -#include - -int main(void) { - struct statx sb; - syscall(SYS_statx, AT_FDCWD, ".", 0, STATX_BASIC_STATS, &sb); - return 0; -} diff --git a/config/has/statx.c b/config/has/statx.c deleted file mode 100644 index 65f1674..0000000 --- a/config/has/statx.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - struct statx sb; - statx(AT_FDCWD, ".", 0, STATX_BASIC_STATS, &sb); - return 0; -} diff --git a/config/has/strerror-l.c b/config/has/strerror-l.c deleted file mode 100644 index 3dcc4d7..0000000 --- a/config/has/strerror-l.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - locale_t locale = duplocale(LC_GLOBAL_LOCALE); - return !strerror_l(ENOMEM, locale); -} diff --git a/config/has/strerror-r-gnu.c b/config/has/strerror-r-gnu.c deleted file mode 100644 index 26ca0ee..0000000 --- a/config/has/strerror-r-gnu.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - char buf[256]; - // Check that strerror_r() returns a pointer - return *strerror_r(ENOMEM, buf, sizeof(buf)); -} diff --git a/config/has/strerror-r-posix.c b/config/has/strerror-r-posix.c deleted file mode 100644 index 41b2d30..0000000 --- a/config/has/strerror-r-posix.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - char buf[256]; - // Check that strerror_r() returns an integer - return 2 * strerror_r(ENOMEM, buf, sizeof(buf)); -} diff --git a/config/has/tm-gmtoff.c b/config/has/tm-gmtoff.c deleted file mode 100644 index 543df48..0000000 --- a/config/has/tm-gmtoff.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct tm tm = {0}; - return tm.tm_gmtoff; -} diff --git a/config/has/uselocale.c b/config/has/uselocale.c deleted file mode 100644 index a712ff8..0000000 --- a/config/has/uselocale.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - locale_t locale = uselocale((locale_t)0); - return locale == LC_GLOBAL_LOCALE; -} diff --git a/config/header.mk b/config/header.mk deleted file mode 100644 index 50ad5e8..0000000 --- a/config/header.mk +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile that generates gen/config.h - -include config/prelude.mk -include ${GEN}/config.mk -include config/exports.mk - -# All header fragments we generate -HEADERS := \ - ${GEN}/has/acl-get-entry.h \ - ${GEN}/has/acl-get-file.h \ - ${GEN}/has/acl-get-tag-type.h \ - ${GEN}/has/acl-is-trivial-np.h \ - ${GEN}/has/acl-trivial.h \ - ${GEN}/has/aligned-alloc.h \ - ${GEN}/has/confstr.h \ - ${GEN}/has/extattr-get-file.h \ - ${GEN}/has/extattr-get-link.h \ - ${GEN}/has/extattr-list-file.h \ - ${GEN}/has/extattr-list-link.h \ - ${GEN}/has/fdclosedir.h \ - ${GEN}/has/getdents.h \ - ${GEN}/has/getdents64.h \ - ${GEN}/has/getdents64-syscall.h \ - ${GEN}/has/getprogname.h \ - ${GEN}/has/getprogname-gnu.h \ - ${GEN}/has/max-align-t.h \ - ${GEN}/has/pipe2.h \ - ${GEN}/has/posix-spawn-addfchdir.h \ - ${GEN}/has/posix-spawn-addfchdir-np.h \ - ${GEN}/has/st-acmtim.h \ - ${GEN}/has/st-acmtimespec.h \ - ${GEN}/has/st-birthtim.h \ - ${GEN}/has/st-birthtimespec.h \ - ${GEN}/has/st-flags.h \ - ${GEN}/has/statx.h \ - ${GEN}/has/statx-syscall.h \ - ${GEN}/has/strerror-l.h \ - ${GEN}/has/strerror-r-gnu.h \ - ${GEN}/has/strerror-r-posix.h \ - ${GEN}/has/tm-gmtoff.h \ - ${GEN}/has/uselocale.h - -# Previously generated by pkgs.mk -PKG_HEADERS := ${ALL_PKGS:%=${GEN}/use/%.h} - -${GEN}/config.h: ${PKG_HEADERS} ${HEADERS} - ${MSG} "[ GEN] ${TGT}" - printf '// %s\n' "${TGT}" >$@ - printf '#ifndef BFS_CONFIG_H\n' >>$@ - printf '#define BFS_CONFIG_H\n' >>$@ - cat ${.ALLSRC} >>$@ - printf '#endif // BFS_CONFIG_H\n' >>$@ - cat ${.ALLSRC:%=%.log} >$@.log - ${VCAT} $@ -.PHONY: ${GEN}/config.h - -# The short name of the config test -SLUG = ${@:${GEN}/%.h=%} - -${HEADERS}:: - ${MKDIR} ${@D} - if config/define-if.sh ${SLUG} config/cc.sh config/${SLUG}.c >$@ 2>$@.log; then \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' ${SLUG}.c; \ - else \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' ${SLUG}.c; \ - fi diff --git a/config/pkgconf.sh b/config/pkgconf.sh deleted file mode 100755 index 365ed61..0000000 --- a/config/pkgconf.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/sh - -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# pkg-config wrapper with hardcoded fallbacks - -set -eu - -MODE= -case "${1:-}" in - --*) - MODE="$1" - shift -esac - -if [ $# -lt 1 ]; then - exit -fi - -case "$XNOLIBS" in - y|1) - exit 1 -esac - -if [ -z "$MODE" ]; then - # Check whether the libraries exist at all - for LIB; do - # Check ${USE_$LIB} - USE_LIB="USE_$(printf '%s' "$LIB" | tr 'a-z-' 'A-Z_')" - eval "USE=\"\${$USE_LIB:-}\"" - case "$USE" in - y|1) - continue - ;; - n|0) - exit 1 - ;; - esac - - CFLAGS=$("$0" --cflags "$LIB") || exit 1 - LDFLAGS=$("$0" --ldflags "$LIB") || exit 1 - LDLIBS=$("$0" --ldlibs "$LIB") || exit 1 - config/cc.sh $CFLAGS $LDFLAGS config/use/$LIB.c $LDLIBS || exit 1 - done -fi - -# Defer to pkg-config if possible -if command -v "${XPKG_CONFIG:-}" >/dev/null 2>&1; then - case "$MODE" in - --cflags) - "$XPKG_CONFIG" --cflags "$@" - ;; - --ldflags) - "$XPKG_CONFIG" --libs-only-L --libs-only-other "$@" - ;; - --ldlibs) - "$XPKG_CONFIG" --libs-only-l "$@" - ;; - esac - - exit -fi - -# pkg-config unavailable, emulate it ourselves -CFLAGS="" -LDFLAGS="" -LDLIBS="" - -for LIB; do - case "$LIB" in - libacl) - LDLIB=-lacl - ;; - libcap) - LDLIB=-lcap - ;; - libselinux) - LDLIB=-lselinux - ;; - liburing) - LDLIB=-luring - ;; - oniguruma) - LDLIB=-lonig - ;; - *) - printf 'error: Unknown package %s\n' "$LIB" >&2 - exit 1 - ;; - esac - - LDLIBS="$LDLIBS$LDLIB " -done - -case "$MODE" in - --ldlibs) - printf '%s\n' "$LDLIBS" - ;; -esac diff --git a/config/pkgs.mk b/config/pkgs.mk deleted file mode 100644 index 898f262..0000000 --- a/config/pkgs.mk +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile that generates gen/pkgs.mk - -include config/prelude.mk -include ${GEN}/vars.mk -include ${GEN}/flags.mk -include config/exports.mk - -HEADERS := ${ALL_PKGS:%=${GEN}/use/%.h} - -${GEN}/pkgs.mk: ${HEADERS} - ${MSG} "[ GEN] ${TGT}" - printf '# %s\n' "${TGT}" >$@ - gen() { \ - printf 'PKGS := %s\n' "$$*"; \ - printf 'CFLAGS += %s\n' "$$(config/pkgconf.sh --cflags "$$@")"; \ - printf 'LDFLAGS += %s\n' "$$(config/pkgconf.sh --ldflags "$$@")"; \ - printf 'LDLIBS := %s $${LDLIBS}\n' "$$(config/pkgconf.sh --ldlibs "$$@")"; \ - }; \ - gen $$(grep -l ' true$$' ${.ALLSRC} | sed 's|.*/\(.*\)\.h|\1|') >>$@ - ${VCAT} $@ - -.PHONY: ${GEN}/pkgs.mk - -# Convert ${GEN}/use/foo.h to foo -PKG = ${@:${GEN}/use/%.h=%} - -${HEADERS}:: - ${MKDIR} ${@D} - if config/define-if.sh use/${PKG} config/pkgconf.sh ${PKG} >$@ 2>$@.log; then \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' use/${PKG}.c; \ - else \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' use/${PKG}.c; \ - fi diff --git a/config/prelude.mk b/config/prelude.mk deleted file mode 100644 index 109e60c..0000000 --- a/config/prelude.mk +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Common makefile utilities. Compatible with both GNU make and most BSD makes. - -# BSD make will chdir into ${.OBJDIR} by default, unless we tell it not to -.OBJDIR: . - -# We don't use any suffix rules -.SUFFIXES: - -# GNU make has $^ for the full list of targets, while BSD make has $> and the -# long-form ${.ALLSRC}. We could write $^ $> to get them both, but that would -# break if one of them implemented support for the other. So instead, bring -# BSD's ${.ALLSRC} to GNU. -.ALLSRC ?= $^ - -# For out-of-tree builds, e.g. -# -# $ make config BUILDDIR=/path/to/build/dir -# $ make BUILDDIR=/path/to/build/dir -BUILDDIR ?= . - -# Shorthand for build subdirectories -BIN := ${BUILDDIR}/bin -GEN := ${BUILDDIR}/gen -OBJ := ${BUILDDIR}/obj - -# GNU make strips a leading ./ from target names, so do the same for BSD make -BIN := ${BIN:./%=%} -GEN := ${GEN:./%=%} -OBJ := ${OBJ:./%=%} - -# The configuration file generated by `make config` -CONFIG := ${GEN}/config.mk - -# Installation paths -DESTDIR ?= -PREFIX ?= /usr -MANDIR ?= ${PREFIX}/share/man - -# GNU make supports `export VAR`, but BSD make requires `export VAR=value`. -# Sadly, GNU make gives a recursion error on `export VAR=${VAR}`. -_BUILDDIR := ${BUILDDIR} -export BUILDDIR=${_BUILDDIR} - -# Configurable executables; can be overridden with -# -# $ make config CC=clang -CC ?= cc -INSTALL ?= install -MKDIR ?= mkdir -p -PKG_CONFIG ?= pkg-config -RM ?= rm -f - -# GNU and BSD make have incompatible syntax for conditionals, but we can do a -# lot with just nested variable expansion. We use "y" as the canonical -# truthy value, and "" (the empty string) as the canonical falsey value. -# -# To normalize a boolean, use ${TRUTHY,${VAR}}, which expands like this: -# -# VAR=y ${TRUTHY,${VAR}} => ${TRUTHY,y} => y -# VAR=1 ${TRUTHY,${VAR}} => ${TRUTHY,1} => y -# VAR=n ${TRUTHY,${VAR}} => ${TRUTHY,n} => [empty] -# VAR=other ${TRUTHY,${VAR}} => ${TRUTHY,other} => [empty] -# VAR= ${TRUTHY,${VAR}} => ${TRUTHY,} => [emtpy] -# -# Inspired by https://github.com/wahern/autoguess -TRUTHY,y := y -TRUTHY,1 := y - -# Boolean operators are also implemented with nested expansion -NOT, := y - -# Normalize ${V} to either "y" or "" -IS_V := ${TRUTHY,${V}} - -# Suppress output unless V=1 -Q, := @ -Q := ${Q,${IS_V}} - -# The current target, with ${BUILDDIR} stripped for shorter messages -TGT = ${@:${BUILDDIR}/%=%} - -# Show full commands with `make V=1`, otherwise short summaries -MSG = @msg() { \ - MSG="$$1"; \ - shift; \ - test "${IS_V}" || printf '%s\n' "$$MSG"; \ - test "$${1:-}" || return 0; \ - test "${IS_V}" && printf '%s\n' "$$*"; \ - "$$@"; \ - }; \ - msg - -# Maximum width of a short message, to align the ✔/✘ -MSG_WIDTH := 30 - -# cat a file if V=1 -VCAT,y := @cat -VCAT, := @: -VCAT := ${VCAT,${IS_V}} - -# All external dependencies -ALL_PKGS := \ - libacl \ - libcap \ - libselinux \ - liburing \ - oniguruma - -# List all object files here, as they're needed by both `make config` and `make` - -# All object files except the entry point -LIBBFS := \ - ${OBJ}/src/alloc.o \ - ${OBJ}/src/bar.o \ - ${OBJ}/src/bfstd.o \ - ${OBJ}/src/bftw.o \ - ${OBJ}/src/color.o \ - ${OBJ}/src/ctx.o \ - ${OBJ}/src/diag.o \ - ${OBJ}/src/dir.o \ - ${OBJ}/src/dstring.o \ - ${OBJ}/src/eval.o \ - ${OBJ}/src/exec.o \ - ${OBJ}/src/expr.o \ - ${OBJ}/src/fsade.o \ - ${OBJ}/src/ioq.o \ - ${OBJ}/src/mtab.o \ - ${OBJ}/src/opt.o \ - ${OBJ}/src/parse.o \ - ${OBJ}/src/printf.o \ - ${OBJ}/src/pwcache.o \ - ${OBJ}/src/stat.o \ - ${OBJ}/src/thread.o \ - ${OBJ}/src/trie.o \ - ${OBJ}/src/typo.o \ - ${OBJ}/src/xregex.o \ - ${OBJ}/src/xspawn.o \ - ${OBJ}/src/xtime.o \ - ${OBJ}/gen/version.o - -# Unit test objects -UNIT_OBJS := \ - ${OBJ}/tests/alloc.o \ - ${OBJ}/tests/bfstd.o \ - ${OBJ}/tests/bit.o \ - ${OBJ}/tests/ioq.o \ - ${OBJ}/tests/main.o \ - ${OBJ}/tests/trie.o \ - ${OBJ}/tests/xspawn.o \ - ${OBJ}/tests/xtime.o - -# All object files -OBJS := \ - ${OBJ}/src/main.o \ - ${OBJ}/tests/mksock.o \ - ${OBJ}/tests/xspawnee.o \ - ${OBJ}/tests/xtouch.o \ - ${LIBBFS} \ - ${UNIT_OBJS} diff --git a/config/use/libacl.c b/config/use/libacl.c deleted file mode 100644 index de1fe50..0000000 --- a/config/use/libacl.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - acl_free(0); - return 0; -} diff --git a/config/use/libcap.c b/config/use/libcap.c deleted file mode 100644 index 58e832c..0000000 --- a/config/use/libcap.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - cap_free(0); - return 0; -} diff --git a/config/use/libselinux.c b/config/use/libselinux.c deleted file mode 100644 index bca409d..0000000 --- a/config/use/libselinux.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - freecon(0); - return 0; -} diff --git a/config/use/liburing.c b/config/use/liburing.c deleted file mode 100644 index bea499a..0000000 --- a/config/use/liburing.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - io_uring_free_probe(0); - return 0; -} diff --git a/config/use/oniguruma.c b/config/use/oniguruma.c deleted file mode 100644 index cb17596..0000000 --- a/config/use/oniguruma.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - onig_free(0); - return 0; -} diff --git a/configure b/configure new file mode 100755 index 0000000..6920212 --- /dev/null +++ b/configure @@ -0,0 +1,98 @@ +#!/bin/sh + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# bfs build configuration script + +set -eu + +help() { + cat <] [CC=...] [CFLAGS=...] [...] + +Compiler configuration: + + CC + The C compiler to use + CPPFLAGS + C preprocessor flags + CFLAGS + C compiler flags + LDFLAGS + Linker flags + LDLIBS + Dynamic libraries to link + + EXTRA_CPPFLAGS + EXTRA_CFLAGS + EXTRA_LDFLAGS + EXTRA_LDLIBS + Adds to the default flags, instead of replacing them + +Build profiles: + + RELEASE=y + Enable optimizations, disable assertions + ASAN=y + LSAN=y + MSAN=y + TSAN=y + UBSAN=y + Enable sanitizers + GCOV=y + Enable code coverage instrumentation + +External dependencies: + + PKG_CONFIG + The pkg-config binary to use + + USE_LIBACL=[y|n] + USE_LIBCAP=[y|n] + USE_LIBSELINUX=[y|n] + USE_LIBURIG=[y|n] + USE_ONIGURUMA=[y|n] + Enable or disable external dependencies + +Packaging configuration: + + PREFIX + Set the installation prefix (default: /usr) + MANDIR + Set the man page directory (default: \$PREFIX/share/man) +EOF +} + +for arg; do + case "$arg" in + --help) + help + exit 0 + ;; + -*|*=*) + continue # make flag (-j2) or variable (CC=clang) + ;; + *) + printf 'error: Unrecognized option "%s"\n\n' "$arg" >&2 + help >&2 + exit 1 + ;; + esac +done + +# Get the relative path to the source tree based on how the script was run +DIR=$(dirname -- "$0") + +# Set up symbolic links for out-of-tree builds +for f in Makefile build completions docs src tests; do + test -e "$f" || ln -s "$DIR/$f" "$f" +done + +# Infer -jN unless MAKEFLAGS is set +j= +if ! [ "${MAKEFLAGS+y}" ]; then + j="-j$({ nproc || sysctl -n hw.ncpu || getconf _NPROCESSORS_ONLN || echo 1; } 2>/dev/null)" +fi + +${MAKE:-make} $j -rsf build/config.mk "$@" diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 4ed139c..db5d721 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -4,10 +4,9 @@ Building `bfs` Compiling --------- -`bfs` uses [GNU Make](https://www.gnu.org/software/make/) as its build system. A simple invocation of - $ make config + $ ./configure $ make should build `bfs` successfully. @@ -18,7 +17,6 @@ For example, to use all your cores, run `make -j$(nproc)`. | Command | Description | |------------------|---------------------------------------------------------------| -| `make config` | Configures the build system | | `make` | Builds just the `bfs` binary | | `make all` | Builds everything, including the tests (but doesn't run them) | | `make check` | Builds everything, and runs the tests | @@ -33,13 +31,13 @@ The configuration system provides a few shorthand flags for handy configurations | Command | Description | |-------------------------|-------------------------------------------------------------| -| `make config RELEASE=y` | Build `bfs` with optimizations, LTO, and without assertions | -| `make config ASAN=y` | Enable [AddressSanitizer] | -| `make config LSAN=y` | Enable [LeakSanitizer] | -| `make config MSAN=y` | Enable [MemorySanitizer] | -| `make config TSAN=y` | Enable [ThreadSanitizer] | -| `make config UBSAN=y` | Enable [UndefinedBehaviorSanitizer] | -| `make config GCOV=y` | Enable [code coverage] | +| `./configure RELEASE=y` | Build `bfs` with optimizations, LTO, and without assertions | +| `./configure ASAN=y` | Enable [AddressSanitizer] | +| `./configure LSAN=y` | Enable [LeakSanitizer] | +| `./configure MSAN=y` | Enable [MemorySanitizer] | +| `./configure TSAN=y` | Enable [ThreadSanitizer] | +| `./configure UBSAN=y` | Enable [UndefinedBehaviorSanitizer] | +| `./configure GCOV=y` | Enable [code coverage] | [AddressSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizer [LeakSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#stand-alone-mode @@ -48,21 +46,20 @@ The configuration system provides a few shorthand flags for handy configurations [UndefinedBehaviorSanitizer]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html [code coverage]: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html -You can combine multiple profiles (e.g. `make config ASAN=y UBSAN=y`), but not all of them will work together. +You can combine multiple profiles (e.g. `./configure ASAN=y UBSAN=y`), but not all of them will work together. ### Flags -Other flags can be specified on the `make config` command line or in the environment. +Other flags can be specified on the `./configure` command line or in the environment. Here are some of the common ones; check the [`Makefile`](/Makefile) for more. | Flag | Description | |-------------------------------------|----------------------------------------------------| -| `CC` | The C compiler to use, e.g. `make config CC=clang` | +| `CC` | The C compiler to use, e.g. `./configure CC=clang` | | `CFLAGS`
`EXTRA_CFLAGS` | Override/add to the default compiler flags | | `LDFLAGS`
`EXTRA_LDFLAGS` | Override/add to the linker flags | | `USE_LIBACL`
`USE_LIBCAP`
... | Enable/disable [optional dependencies] | | `TEST_FLAGS` | `tests.sh` flags for `make check` | -| `BUILDDIR` | The build output directory (default: `.`) | | `DESTDIR` | The root directory for `make install` | | `PREFIX` | The installation prefix (default: `/usr`) | | `MANDIR` | The man page installation directory | @@ -72,9 +69,9 @@ Here are some of the common ones; check the [`Makefile`](/Makefile) for more. ### Dependencies `bfs` depends on some system libraries for some of its features. -These dependencies are optional, and can be turned off in `make config` if necessary by setting the appropriate variable to `n` (e.g. `make config USE_ONIGURUMA=n`). +These dependencies are optional, and can be turned off in `./configure` if necessary by setting the appropriate variable to `n` (e.g. `./configure USE_ONIGURUMA=n`). -| Dependency | Platforms | `make config` flag | +| Dependency | Platforms | `./configure` flag | |--------------|------------|--------------------| | [libacl] | Linux only | `USE_LIBACL` | | [libcap] | Linux only | `USE_LIBCAP` | @@ -90,22 +87,28 @@ These dependencies are optional, and can be turned off in `make config` if neces ### Dependency tracking -The build system automatically tracks header dependencies with the `-M` family of compiler options (see `DEPFLAGS` in the [`Makefile`](/Makefile)). +The build system automatically tracks header dependencies with the `-M` family of compiler options (see `DEPFLAGS` in [`build/deps.mk`](/build/deps.mk)). So if you edit a header file, `make` will rebuild the necessary object files ensuring they don't go out of sync. We also add a dependency on the current configuration, so you can change configurations and rebuild without having to `make clean`. - -We go one step further than most build systems by tracking the flags that were used for the previous compilation. -That means you can change configurations without having to `make clean`. For example, - $ make config + $ ./configure $ make - $ make config RELEASE=y + $ ./configure RELEASE=y $ make will build the project in debug mode and then rebuild it in release mode. +### Out-of-tree builds + +You can set up an out-of-tree build by running the `configure` script from another directory, for example: + + $ mkdir out + $ cd out + $ ../configure + $ make + Testing ------- diff --git a/tests/util.sh b/tests/util.sh index 7dba9fb..3969db5 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -12,12 +12,9 @@ _realpath() ( ) # Globals -TESTS=$(_realpath "$TESTS") -if [ "${BUILDDIR-}" ]; then - BIN=$(_realpath "$BUILDDIR/bin") -else - BIN=$(_realpath "$TESTS/../bin") -fi +ROOT=$(_realpath "$(dirname -- "$TESTS")") +TESTS="$ROOT/tests" +BIN="$ROOT/bin" MKSOCK="$BIN/tests/mksock" XTOUCH="$BIN/tests/xtouch" UNAME=$(uname) diff --git a/tests/xspawn.c b/tests/xspawn.c index 7362aa5..785ea48 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -64,27 +64,20 @@ static bool check_use_path(bool use_posix) { spawn.flags &= ~BFS_SPAWN_USE_POSIX; } - const char *builddir = getenv("BUILDDIR"); - dchar *bin = dstrprintf("%s/bin", builddir ? builddir : "."); - ret &= bfs_pcheck(bin, "dstrprintf()"); - if (!ret) { - goto destroy; - } - - ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, bin, O_RDONLY | O_DIRECTORY, 0) == 0); + ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, "bin", O_RDONLY | O_DIRECTORY, 0) == 0); ret &= bfs_pcheck(bfs_spawn_adddup2(&spawn, 10, 11) == 0); ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 10) == 0); ret &= bfs_pcheck(bfs_spawn_addfchdir(&spawn, 11) == 0); ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 11) == 0); if (!ret) { - goto bin; + goto destroy; } // Check that $PATH is resolved in the parent's environment char **envp; ret &= bfs_pcheck(envp = envdup()); if (!ret) { - goto bin; + goto destroy; } // Check that $PATH is resolved after the file actions @@ -138,8 +131,6 @@ env: free(*var); } free(envp); -bin: - dstrfree(bin); destroy: ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0); out: -- cgit v1.2.3 From cf080ec377461ca6fe2e0e9bb8b25ad63bda6bc6 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 2 May 2024 11:25:42 -0400 Subject: Prefer ./configure --enable-release to RELEASE=y --- README.md | 2 +- bench/bench.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'bench') diff --git a/README.md b/README.md index 922ebdc..b9aad82 100644 --- a/README.md +++ b/README.md @@ -343,7 +343,7 @@ Run the test suite to make sure it works correctly: If you're interested in speed, you may want to build the release version instead: - $ ./configure RELEASE=y + $ ./configure --enable-release $ make Finally, if you want to install it globally, run diff --git a/bench/bench.sh b/bench/bench.sh index ba46599..cf1ae49 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -221,7 +221,7 @@ setup() { fi echo "Building bfs ..." - as-user ./configure RELEASE=y + as-user ./configure --enable-release as-user make -s -j"$nproc" all as-user mkdir -p bench/corpus @@ -255,7 +255,7 @@ setup() { cd "$worktree" as-user git checkout -qd "$commit" -- if [ -e configure ]; then - as-user ./configure RELEASE=1 + as-user ./configure --enable-release as-user make -s -j"$nproc" else as-user make -s -j"$nproc" release -- cgit v1.2.3