summaryrefslogtreecommitdiffstats
path: root/build
diff options
context:
space:
mode:
Diffstat (limited to 'build')
-rwxr-xr-xbuild/cc.sh34
-rw-r--r--build/config.mk57
-rwxr-xr-xbuild/define-if.sh18
-rwxr-xr-xbuild/embed.sh12
-rw-r--r--build/empty.c6
-rw-r--r--build/exports.mk20
-rwxr-xr-xbuild/flags-if.sh28
-rw-r--r--build/flags.mk136
-rw-r--r--build/flags/Wformat.c9
-rw-r--r--build/flags/Wimplicit-fallthrough.c9
-rw-r--r--build/flags/Wimplicit.c9
-rw-r--r--build/flags/Wmissing-decls.c9
-rw-r--r--build/flags/Wmissing-var-decls.c9
-rw-r--r--build/flags/Wshadow.c9
-rw-r--r--build/flags/Wsign-compare.c9
-rw-r--r--build/flags/Wstrict-prototypes.c9
-rw-r--r--build/flags/Wundef-prefix.c9
-rw-r--r--build/flags/bind-now.c8
-rw-r--r--build/flags/deps.c8
-rw-r--r--build/flags/pthread.c8
-rw-r--r--build/has/--st-birthtim.c9
-rw-r--r--build/has/_Fork.c8
-rw-r--r--build/has/acl-get-entry.c11
-rw-r--r--build/has/acl-get-file.c11
-rw-r--r--build/has/acl-get-tag-type.c13
-rw-r--r--build/has/acl-is-trivial-np.c12
-rw-r--r--build/has/acl-trivial.c8
-rw-r--r--build/has/builtin-riscv-pause.c7
-rw-r--r--build/has/confstr.c9
-rw-r--r--build/has/dprintf.c8
-rw-r--r--build/has/extattr-get-file.c10
-rw-r--r--build/has/extattr-get-link.c10
-rw-r--r--build/has/extattr-list-file.c10
-rw-r--r--build/has/extattr-list-link.c10
-rw-r--r--build/has/fdclosedir.c8
-rw-r--r--build/has/getdents.c9
-rw-r--r--build/has/getdents64-syscall.c11
-rw-r--r--build/has/getdents64.c9
-rw-r--r--build/has/getmntent-1.c9
-rw-r--r--build/has/getmntent-2.c10
-rw-r--r--build/has/getmntinfo.c10
-rw-r--r--build/has/getprogname-gnu.c9
-rw-r--r--build/has/getprogname.c9
-rw-r--r--build/has/io-uring-max-workers.c11
-rw-r--r--build/has/pipe2.c10
-rw-r--r--build/has/posix-getdents.c9
-rw-r--r--build/has/posix-spawn-addfchdir-np.c11
-rw-r--r--build/has/posix-spawn-addfchdir.c11
-rw-r--r--build/has/pragma-nounroll.c10
-rw-r--r--build/has/pthread-set-name-np.c10
-rw-r--r--build/has/pthread-setname-np.c8
-rw-r--r--build/has/sched-getaffinity.c9
-rw-r--r--build/has/st-acmtim.c12
-rw-r--r--build/has/st-acmtimespec.c12
-rw-r--r--build/has/st-birthtim.c9
-rw-r--r--build/has/st-birthtimespec.c9
-rw-r--r--build/has/st-flags.c9
-rw-r--r--build/has/statx-syscall.c13
-rw-r--r--build/has/statx.c11
-rw-r--r--build/has/strerror-l.c11
-rw-r--r--build/has/strerror-r-gnu.c11
-rw-r--r--build/has/strerror-r-posix.c11
-rw-r--r--build/has/string-to-flags.c9
-rw-r--r--build/has/strtofflags.c9
-rw-r--r--build/has/tcgetwinsize.c9
-rw-r--r--build/has/tcsetwinsize.c9
-rw-r--r--build/has/timegm.c9
-rw-r--r--build/has/timer-create.c9
-rw-r--r--build/has/tm-gmtoff.c9
-rw-r--r--build/has/uselocale.c9
-rw-r--r--build/header.mk93
-rwxr-xr-xbuild/msg-if.sh31
-rwxr-xr-xbuild/msg.sh62
-rwxr-xr-xbuild/pkgconf.sh96
-rw-r--r--build/pkgs.mk33
-rw-r--r--build/prelude.mk70
-rwxr-xr-xbuild/version.sh18
-rw-r--r--build/with/libacl.c9
-rw-r--r--build/with/libcap.c9
-rw-r--r--build/with/libselinux.c9
-rw-r--r--build/with/liburing.c9
-rw-r--r--build/with/oniguruma.c9
82 files changed, 1353 insertions, 0 deletions
diff --git a/build/cc.sh b/build/cc.sh
new file mode 100755
index 0000000..e1d2b0b
--- /dev/null
+++ b/build/cc.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Run the compiler and check if it succeeded. Usage:
+#
+# $ build/cc.sh [-q] path/to/file.c [-flags -Warnings ...]
+
+set -eu
+
+QUIET=
+if [ "$1" = "-q" ]; then
+ QUIET=y
+ shift
+fi
+
+# Source files can specify their own flags with lines like
+#
+# /// _CFLAGS += -Wmissing-variable-declarations
+#
+# which will be added to the makefile on success, or lines like
+#
+# /// -Werror
+#
+# which are just used for the current file.
+EXTRA_FLAGS=$(sed -n '\|^///|{s|^/// ||; s|[^=]*= ||; p;}' "$1")
+
+# Without -q, print the executed command for config.log
+if [ -z "$QUIET" ]; then
+ set -x
+fi
+
+$XCC $XCPPFLAGS $XCFLAGS $XLDFLAGS "$@" $EXTRA_FLAGS $XLDLIBS
diff --git a/build/config.mk b/build/config.mk
new file mode 100644
index 0000000..6296168
--- /dev/null
+++ b/build/config.mk
@@ -0,0 +1,57 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Makefile that implements `./configure`
+
+include build/prelude.mk
+include build/exports.mk
+
+# All configuration steps
+config: gen/config.mk
+.PHONY: config
+
+# Makefile fragments generated by `./configure`
+MKS := \
+ gen/vars.mk \
+ gen/flags.mk \
+ gen/pkgs.mk
+
+# The main configuration file, which includes the others
+gen/config.mk: ${MKS} gen/config.h
+ ${MSG} "[ GEN] $@"
+ @printf '# %s\n' "$@" >$@
+ @printf 'include %s\n' ${MKS} >>$@
+ ${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" >>$@
+ @test -z "$$VERSION" || printf 'export VERSION=%s\n' "$$VERSION" >>$@
+ ${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
+ @+XMAKEFLAGS="$$MAKEFLAGS" ${MAKE} -sf build/flags.mk $@
+.PHONY: gen/flags.mk
+
+# Auto-detect dependencies and their build flags
+gen/pkgs.mk: gen/flags.mk
+ @+XMAKEFLAGS="$$MAKEFLAGS" ${MAKE} -sf build/pkgs.mk $@
+.PHONY: gen/pkgs.mk
+
+# Compile-time feature detection
+gen/config.h: gen/pkgs.mk
+ @+XMAKEFLAGS="$$MAKEFLAGS" ${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..204cfa4
--- /dev/null
+++ b/build/define-if.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Output a C preprocessor definition based on whether a command succeeds
+
+set -eu
+
+MACRO=$(printf 'BFS_%s' "$1" | 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/embed.sh b/build/embed.sh
new file mode 100755
index 0000000..c0744f6
--- /dev/null
+++ b/build/embed.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Convert data into a C array like #embed
+
+set -eu
+
+{ cat; printf '\0'; } \
+ | od -An -tx1 \
+ | sed 's/[^ ][^ ]*/0x&,/g'
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 <tavianator@tavianator.com>
+// 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..913a1aa
--- /dev/null
+++ b/build/exports.mk
@@ -0,0 +1,20 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# 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-if.sh b/build/flags-if.sh
new file mode 100755
index 0000000..81eb345
--- /dev/null
+++ b/build/flags-if.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Add flags to a makefile if a build succeeds
+
+set -eu
+
+build/cc.sh "$@" || exit 1
+
+# If the build succeeded, print any lines like
+#
+# /// _CFLAGS += -foo
+#
+# (unless they're already set)
+OLD_FLAGS="$XCC $XCPPFLAGS $XCFLAGS $XLDFLAGS $XLDLIBS"
+
+while IFS="" read -r line; do
+ case "$line" in
+ ///*=*)
+ flag="${line#*= }"
+ if [ "${OLD_FLAGS#*"$flag"}" = "$OLD_FLAGS" ]; then
+ printf '%s\n' "${line#/// }"
+ fi
+ ;;
+ esac
+done <"$1"
diff --git a/build/flags.mk b/build/flags.mk
new file mode 100644
index 0000000..fbdf8cf
--- /dev/null
+++ b/build/flags.mk
@@ -0,0 +1,136 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Makefile that generates gen/flags.mk
+
+include build/prelude.mk
+include gen/vars.mk
+
+# Internal flags
+_CPPFLAGS := -Isrc -Igen -include src/prelude.h
+_CFLAGS := -std=c17
+_LDFLAGS :=
+_LDLIBS :=
+
+# Platform-specific system libraries
+LDLIBS,DragonFly := -lposix1e
+LDLIBS,FreeBSD := -lrt
+LDLIBS,Linux := -lrt
+LDLIBS,NetBSD := -lutil
+LDLIBS,QNX := -lregex -lsocket
+LDLIBS,SunOS := -lsec -lsocket -lnsl
+_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}}
+
+LTO ?= ${RELEASE}
+_LTO := ${TRUTHY,${LTO}}
+
+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
+
+_CFLAGS += ${ASAN_CFLAGS,${_ASAN}}
+_CFLAGS += ${LSAN_CFLAGS,${_LSAN}}
+_CFLAGS += ${MSAN_CFLAGS,${_MSAN}}
+_CFLAGS += ${TSAN_CFLAGS,${_TSAN}}
+_CFLAGS += ${UBSAN_CFLAGS,${_UBSAN}}
+
+SAN_CFLAGS,y := -fno-sanitize-recover=all
+INSANE := ${NOT,${_ASAN}${_LSAN}${_MSAN}${_TSAN}${_UBSAN}}
+SAN := ${NOT,${INSANE}}
+_CFLAGS += ${SAN_CFLAGS,${SAN}}
+
+# MSAN and TSAN both need all code to be instrumented
+YESLIBS := ${NOT,${_MSAN}${_TSAN}}
+NOLIBS ?= ${NOT,${YESLIBS}}
+
+# gcov only intercepts fork()/exec() with -std=gnu*
+GCOV_CFLAGS,y := -std=gnu17 --coverage
+_CFLAGS += ${GCOV_CFLAGS,${_GCOV}}
+
+LINT_CPPFLAGS,y := -D_FORTIFY_SOURCE=3 -DBFS_LINT
+LINT_CFLAGS,y := -Werror -O2
+
+_CPPFLAGS += ${LINT_CPPFLAGS,${_LINT}}
+_CFLAGS += ${LINT_CFLAGS,${_LINT}}
+
+RELEASE_CPPFLAGS,y := -DNDEBUG
+RELEASE_CFLAGS,y := -O3
+
+_CPPFLAGS += ${RELEASE_CPPFLAGS,${_RELEASE}}
+_CFLAGS += ${RELEASE_CFLAGS,${_RELEASE}}
+
+LTO_CFLAGS,y := -flto=auto
+_CFLAGS += ${LTO_CFLAGS,${_LTO}}
+
+# Configurable flags
+CFLAGS ?= -g -Wall
+
+# Add the configurable flags last so they can override ours
+_CPPFLAGS += ${CPPFLAGS} ${EXTRA_CPPFLAGS}
+_CFLAGS += ${CFLAGS} ${EXTRA_CFLAGS}
+_LDFLAGS += ${LDFLAGS} ${EXTRA_LDFLAGS}
+# (except LDLIBS, as earlier libs override later ones)
+_LDLIBS := ${LDLIBS} ${EXTRA_LDLIBS} ${_LDLIBS}
+
+include build/exports.mk
+
+# Conditionally-supported flags
+AUTO_FLAGS := \
+ gen/flags/Wformat.mk \
+ gen/flags/Wimplicit-fallthrough.mk \
+ gen/flags/Wimplicit.mk \
+ gen/flags/Wmissing-decls.mk \
+ gen/flags/Wmissing-var-decls.mk \
+ gen/flags/Wshadow.mk \
+ gen/flags/Wsign-compare.mk \
+ gen/flags/Wstrict-prototypes.mk \
+ gen/flags/Wundef-prefix.mk \
+ gen/flags/bind-now.mk \
+ gen/flags/deps.mk \
+ gen/flags/pthread.mk
+
+gen/flags.mk: ${AUTO_FLAGS}
+ ${MSG} "[ GEN] $@"
+ @printf '# %s\n' "$@" >$@
+ @printf '_CPPFLAGS := %s\n' "$$XCPPFLAGS" >>$@
+ @printf '_CFLAGS := %s\n' "$$XCFLAGS" >>$@
+ @printf '_LDFLAGS := %s\n' "$$XLDFLAGS" >>$@
+ @printf '_LDLIBS := %s\n' "$$XLDLIBS" >>$@
+ @printf 'NOLIBS := %s\n' "$$XNOLIBS" >>$@
+ @test "${OS}-${SAN}" != FreeBSD-y || printf 'POSTLINK = elfctl -e +noaslr $$@\n' >>$@
+ @cat ${.ALLSRC} >>$@
+ @cat ${.ALLSRC:%=%.log} >gen/flags.log
+ ${VCAT} $@
+.PHONY: gen/flags.mk
+
+# Check that the C compiler works at all
+cc::
+ @build/cc.sh -q build/empty.c -o gen/.cc.out; \
+ ret=$$?; \
+ build/msg-if.sh "[ CC ] build/empty.c" test $$ret -eq 0; \
+ exit $$ret
+
+# The short name of the config test
+SLUG = ${@:gen/%.mk=%}
+# The source file to build
+CSRC = build/${SLUG}.c
+# The hidden output file name
+OUT = ${SLUG:flags/%=gen/flags/.%.out}
+
+${AUTO_FLAGS}: cc
+ @${MKDIR} ${@D}
+ @build/flags-if.sh ${CSRC} -o ${OUT} >$@ 2>$@.log; \
+ build/msg-if.sh "[ CC ] ${SLUG}.c" test $$? -eq 0
+.PHONY: ${AUTO_FLAGS}
diff --git a/build/flags/Wformat.c b/build/flags/Wformat.c
new file mode 100644
index 0000000..287b209
--- /dev/null
+++ b/build/flags/Wformat.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CFLAGS += -Wformat=2
+/// -Werror
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/Wimplicit-fallthrough.c b/build/flags/Wimplicit-fallthrough.c
new file mode 100644
index 0000000..c32058d
--- /dev/null
+++ b/build/flags/Wimplicit-fallthrough.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CFLAGS += -Wimplicit-fallthrough
+/// -Werror
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/Wimplicit.c b/build/flags/Wimplicit.c
new file mode 100644
index 0000000..3ea2b90
--- /dev/null
+++ b/build/flags/Wimplicit.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CFLAGS += -Werror=implicit
+/// -Werror
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/Wmissing-decls.c b/build/flags/Wmissing-decls.c
new file mode 100644
index 0000000..5ef3e96
--- /dev/null
+++ b/build/flags/Wmissing-decls.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CFLAGS += -Wmissing-declarations
+/// -Werror
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/Wmissing-var-decls.c b/build/flags/Wmissing-var-decls.c
new file mode 100644
index 0000000..5c20cc6
--- /dev/null
+++ b/build/flags/Wmissing-var-decls.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CFLAGS += -Wmissing-variable-declarations
+/// -Werror
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/Wshadow.c b/build/flags/Wshadow.c
new file mode 100644
index 0000000..28f6ef3
--- /dev/null
+++ b/build/flags/Wshadow.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CFLAGS += -Wshadow
+/// -Werror
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/Wsign-compare.c b/build/flags/Wsign-compare.c
new file mode 100644
index 0000000..f083083
--- /dev/null
+++ b/build/flags/Wsign-compare.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CFLAGS += -Wsign-compare
+/// -Werror
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/Wstrict-prototypes.c b/build/flags/Wstrict-prototypes.c
new file mode 100644
index 0000000..9614bee
--- /dev/null
+++ b/build/flags/Wstrict-prototypes.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CFLAGS += -Wstrict-prototypes
+/// -Werror
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/Wundef-prefix.c b/build/flags/Wundef-prefix.c
new file mode 100644
index 0000000..3eaf82b
--- /dev/null
+++ b/build/flags/Wundef-prefix.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CPPFLAGS += -Wundef-prefix=BFS_
+/// -Werror
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/bind-now.c b/build/flags/bind-now.c
new file mode 100644
index 0000000..08bb4f2
--- /dev/null
+++ b/build/flags/bind-now.c
@@ -0,0 +1,8 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _LDFLAGS += -Wl,-z,now
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/deps.c b/build/flags/deps.c
new file mode 100644
index 0000000..1c8c309
--- /dev/null
+++ b/build/flags/deps.c
@@ -0,0 +1,8 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CPPFLAGS += -MD -MP
+
+int main(void) {
+ return 0;
+}
diff --git a/build/flags/pthread.c b/build/flags/pthread.c
new file mode 100644
index 0000000..db09aa4
--- /dev/null
+++ b/build/flags/pthread.c
@@ -0,0 +1,8 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// _CFLAGS += -pthread
+
+int main(void) {
+ return 0;
+}
diff --git a/build/has/--st-birthtim.c b/build/has/--st-birthtim.c
new file mode 100644
index 0000000..4da621f
--- /dev/null
+++ b/build/has/--st-birthtim.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/stat.h>
+
+int main(void) {
+ struct stat sb = {0};
+ return sb.__st_birthtim.tv_sec;
+}
diff --git a/build/has/_Fork.c b/build/has/_Fork.c
new file mode 100644
index 0000000..4d7fbd3
--- /dev/null
+++ b/build/has/_Fork.c
@@ -0,0 +1,8 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <unistd.h>
+
+int main(void) {
+ return _Fork();
+}
diff --git a/build/has/acl-get-entry.c b/build/has/acl-get-entry.c
new file mode 100644
index 0000000..1e7f473
--- /dev/null
+++ b/build/has/acl-get-entry.c
@@ -0,0 +1,11 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/types.h>
+#include <sys/acl.h>
+
+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..0b76ee2
--- /dev/null
+++ b/build/has/acl-get-file.c
@@ -0,0 +1,11 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/acl.h>
+
+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..67b7d37
--- /dev/null
+++ b/build/has/acl-get-tag-type.c
@@ -0,0 +1,13 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/acl.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/types.h>
+#include <sys/acl.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/acl.h>
+
+int main(void) {
+ return acl_trivial(".");
+}
diff --git a/build/has/builtin-riscv-pause.c b/build/has/builtin-riscv-pause.c
new file mode 100644
index 0000000..24b0675
--- /dev/null
+++ b/build/has/builtin-riscv-pause.c
@@ -0,0 +1,7 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+int main(void) {
+ __builtin_riscv_pause();
+ return 0;
+}
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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <unistd.h>
+
+int main(void) {
+ confstr(_CS_PATH, NULL, 0);
+ return 0;
+}
diff --git a/build/has/dprintf.c b/build/has/dprintf.c
new file mode 100644
index 0000000..c206fa3
--- /dev/null
+++ b/build/has/dprintf.c
@@ -0,0 +1,8 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stdio.h>
+
+int main(void) {
+ return dprintf(1, "%s\n", "Hello world!");
+}
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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/extattr.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/extattr.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/extattr.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/extattr.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <dirent.h>
+
+int main(void) {
+ return fdclosedir(opendir("."));
+}
diff --git a/build/has/getdents.c b/build/has/getdents.c
new file mode 100644
index 0000000..579898f
--- /dev/null
+++ b/build/has/getdents.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <dirent.h>
+
+int main(void) {
+ char buf[1024];
+ return getdents(3, (void *)buf, sizeof(buf));
+}
diff --git a/build/has/getdents64-syscall.c b/build/has/getdents64-syscall.c
new file mode 100644
index 0000000..7642d93
--- /dev/null
+++ b/build/has/getdents64-syscall.c
@@ -0,0 +1,11 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <dirent.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+int main(void) {
+ char buf[1024];
+ return syscall(SYS_getdents64, 3, (void *)buf, sizeof(buf));
+}
diff --git a/build/has/getdents64.c b/build/has/getdents64.c
new file mode 100644
index 0000000..d8e8062
--- /dev/null
+++ b/build/has/getdents64.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <dirent.h>
+
+int main(void) {
+ char buf[1024];
+ return getdents64(3, (void *)buf, sizeof(buf));
+}
diff --git a/build/has/getmntent-1.c b/build/has/getmntent-1.c
new file mode 100644
index 0000000..9854dcd
--- /dev/null
+++ b/build/has/getmntent-1.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <mntent.h>
+#include <stdio.h>
+
+int main(void) {
+ return !getmntent(stdin);
+}
diff --git a/build/has/getmntent-2.c b/build/has/getmntent-2.c
new file mode 100644
index 0000000..71f0220
--- /dev/null
+++ b/build/has/getmntent-2.c
@@ -0,0 +1,10 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stdio.h>
+#include <sys/mnttab.h>
+
+int main(void) {
+ struct mnttab mnt;
+ return getmntent(stdin, &mnt);
+}
diff --git a/build/has/getmntinfo.c b/build/has/getmntinfo.c
new file mode 100644
index 0000000..90ef5fb
--- /dev/null
+++ b/build/has/getmntinfo.c
@@ -0,0 +1,10 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+int main(void) {
+ return getmntinfo(NULL, MNT_WAIT);
+}
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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <errno.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stdlib.h>
+
+int main(void) {
+ const char *str = getprogname();
+ return str[0];
+}
diff --git a/build/has/io-uring-max-workers.c b/build/has/io-uring-max-workers.c
new file mode 100644
index 0000000..34ab5b7
--- /dev/null
+++ b/build/has/io-uring-max-workers.c
@@ -0,0 +1,11 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <liburing.h>
+
+int main(void) {
+ struct io_uring ring;
+ io_uring_queue_init(1, &ring, 0);
+ unsigned int values[] = {0, 0};
+ return io_uring_register_iowq_max_workers(&ring, values);
+}
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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <fcntl.h>
+#include <unistd.h>
+
+int main(void) {
+ int fds[2];
+ return pipe2(fds, O_CLOEXEC);
+}
diff --git a/build/has/posix-getdents.c b/build/has/posix-getdents.c
new file mode 100644
index 0000000..f74bbe5
--- /dev/null
+++ b/build/has/posix-getdents.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <dirent.h>
+
+int main(void) {
+ char buf[1024];
+ return posix_getdents(3, (void *)buf, sizeof(buf), 0);
+}
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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <spawn.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <spawn.h>
+
+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/pragma-nounroll.c b/build/has/pragma-nounroll.c
new file mode 100644
index 0000000..2bdae14
--- /dev/null
+++ b/build/has/pragma-nounroll.c
@@ -0,0 +1,10 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/// -Werror
+
+int main(void) {
+#pragma nounroll
+ for (int i = 0; i < 100; ++i);
+ return 0;
+}
diff --git a/build/has/pthread-set-name-np.c b/build/has/pthread-set-name-np.c
new file mode 100644
index 0000000..324aab9
--- /dev/null
+++ b/build/has/pthread-set-name-np.c
@@ -0,0 +1,10 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <pthread.h>
+#include <pthread_np.h>
+
+int main(void) {
+ pthread_set_name_np(pthread_self(), "name");
+ return 0;
+}
diff --git a/build/has/pthread-setname-np.c b/build/has/pthread-setname-np.c
new file mode 100644
index 0000000..a3b94c1
--- /dev/null
+++ b/build/has/pthread-setname-np.c
@@ -0,0 +1,8 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <pthread.h>
+
+int main(void) {
+ return pthread_setname_np(pthread_self(), "name");
+}
diff --git a/build/has/sched-getaffinity.c b/build/has/sched-getaffinity.c
new file mode 100644
index 0000000..6f8fd98
--- /dev/null
+++ b/build/has/sched-getaffinity.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sched.h>
+
+int main(void) {
+ cpu_set_t set;
+ return sched_getaffinity(0, sizeof(set), &set);
+}
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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/stat.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/stat.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/stat.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/stat.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/stat.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <fcntl.h>
+#include <linux/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <errno.h>
+#include <string.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <errno.h>
+#include <string.h>
+
+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/string-to-flags.c b/build/has/string-to-flags.c
new file mode 100644
index 0000000..027d72c
--- /dev/null
+++ b/build/has/string-to-flags.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stddef.h>
+#include <util.h>
+
+int main(void) {
+ return string_to_flags(NULL, NULL, NULL);
+}
diff --git a/build/has/strtofflags.c b/build/has/strtofflags.c
new file mode 100644
index 0000000..73ecbcb
--- /dev/null
+++ b/build/has/strtofflags.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stddef.h>
+#include <unistd.h>
+
+int main(void) {
+ return strtofflags(NULL, NULL, NULL);
+}
diff --git a/build/has/tcgetwinsize.c b/build/has/tcgetwinsize.c
new file mode 100644
index 0000000..d25d12b
--- /dev/null
+++ b/build/has/tcgetwinsize.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <termios.h>
+
+int main(void) {
+ struct winsize ws;
+ return tcgetwinsize(0, &ws);
+}
diff --git a/build/has/tcsetwinsize.c b/build/has/tcsetwinsize.c
new file mode 100644
index 0000000..6717415
--- /dev/null
+++ b/build/has/tcsetwinsize.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <termios.h>
+
+int main(void) {
+ const struct winsize ws = {0};
+ return tcsetwinsize(0, &ws);
+}
diff --git a/build/has/timegm.c b/build/has/timegm.c
new file mode 100644
index 0000000..6e2d155
--- /dev/null
+++ b/build/has/timegm.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <time.h>
+
+int main(void) {
+ struct tm tm = {0};
+ return (int)timegm(&tm);
+}
diff --git a/build/has/timer-create.c b/build/has/timer-create.c
new file mode 100644
index 0000000..d5354c3
--- /dev/null
+++ b/build/has/timer-create.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <time.h>
+
+int main(void) {
+ timer_t timer;
+ return timer_create(CLOCK_REALTIME, NULL, &timer);
+}
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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <time.h>
+
+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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <locale.h>
+
+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..13672ba
--- /dev/null
+++ b/build/header.mk
@@ -0,0 +1,93 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Makefile that generates gen/config.h
+
+include build/prelude.mk
+include gen/vars.mk
+include gen/flags.mk
+include gen/pkgs.mk
+include build/exports.mk
+
+# All header fragments we generate
+HEADERS := \
+ gen/has/--st-birthtim.h \
+ gen/has/_Fork.h \
+ 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/builtin-riscv-pause.h \
+ gen/has/confstr.h \
+ gen/has/dprintf.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-syscall.h \
+ gen/has/getdents64.h \
+ gen/has/getmntent-1.h \
+ gen/has/getmntent-2.h \
+ gen/has/getmntinfo.h \
+ gen/has/getprogname-gnu.h \
+ gen/has/getprogname.h \
+ gen/has/io-uring-max-workers.h \
+ gen/has/pipe2.h \
+ gen/has/pragma-nounroll.h \
+ gen/has/posix-getdents.h \
+ gen/has/posix-spawn-addfchdir-np.h \
+ gen/has/posix-spawn-addfchdir.h \
+ gen/has/pthread-set-name-np.h \
+ gen/has/pthread-setname-np.h \
+ gen/has/sched-getaffinity.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-syscall.h \
+ gen/has/statx.h \
+ gen/has/strerror-l.h \
+ gen/has/strerror-r-gnu.h \
+ gen/has/strerror-r-posix.h \
+ gen/has/string-to-flags.h \
+ gen/has/strtofflags.h \
+ gen/has/tcgetwinsize.h \
+ gen/has/tcsetwinsize.h \
+ gen/has/timegm.h \
+ gen/has/timer-create.h \
+ gen/has/tm-gmtoff.h \
+ gen/has/uselocale.h
+
+# Previously generated by pkgs.mk
+PKG_HEADERS := ${ALL_PKGS:%=gen/with/%.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 gen/flags.log ${.ALLSRC:%=%.log} >gen/config.log
+ ${VCAT} $@
+ @printf '%s' "$$CONFFLAGS" | build/embed.sh >gen/confflags.i
+ @printf '%s' "$$XCC" | build/embed.sh >gen/cc.i
+ @printf '%s' "$$XCPPFLAGS" | build/embed.sh >gen/cppflags.i
+ @printf '%s' "$$XCFLAGS" | build/embed.sh >gen/cflags.i
+ @printf '%s' "$$XLDFLAGS" | build/embed.sh >gen/ldflags.i
+ @printf '%s' "$$XLDLIBS" | build/embed.sh >gen/ldlibs.i
+.PHONY: gen/config.h
+
+# The short name of the config test
+SLUG = ${@:gen/%.h=%}
+# The hidden output file name
+OUT = ${SLUG:has/%=gen/has/.%.out}
+
+${HEADERS}::
+ @${MKDIR} ${@D}
+ @build/define-if.sh ${SLUG} build/cc.sh build/${SLUG}.c -o ${OUT} >$@ 2>$@.log; \
+ build/msg-if.sh "[ CC ] ${SLUG}.c" test $$? -eq 0
diff --git a/build/msg-if.sh b/build/msg-if.sh
new file mode 100755
index 0000000..afb478c
--- /dev/null
+++ b/build/msg-if.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Print a success/failure indicator from a makefile:
+#
+# $ ./configure
+# [ CC ] with/liburing.c ✘
+# [ CC ] with/oniguruma.c ✔
+
+set -eu
+
+MSG="$1"
+shift
+
+if [ -z "${NO_COLOR:-}" ] && [ -t 1 ]; then
+ Y='\033[1;32m✔\033[0m'
+ N='\033[1;31m✘\033[0m'
+else
+ Y='✔'
+ N='✘'
+fi
+
+if "$@"; then
+ YN="$Y"
+else
+ YN="$N"
+fi
+
+build/msg.sh "$(printf "%-37s $YN" "$MSG")"
diff --git a/build/msg.sh b/build/msg.sh
new file mode 100755
index 0000000..2249125
--- /dev/null
+++ b/build/msg.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Print a message from a makefile:
+#
+# $ make -s
+# $ make
+# [ CC ] src/main.c
+# $ make V=1
+# cc -Isrc -Igen -D...
+
+set -eu
+
+# Get the $MAKEFLAGS from the top-level make invocation
+MFLAGS="${XMAKEFLAGS-${MAKEFLAGS-}}"
+
+# Check if make should be quiet (make -s)
+is_quiet() {
+ # GNU make puts single-letter flags in the first word of $MAKEFLAGS,
+ # without a leading dash
+ case "${MFLAGS%% *}" in
+ -*) : ;;
+ *s*) return 0 ;;
+ esac
+
+ # BSD make puts each flag separately like -r -s -j 48
+ for flag in $MFLAGS; do
+ case "$flag" in
+ # Ignore things like --jobserver-auth
+ --*) continue ;;
+ # Skip variable assignments
+ *=*) break ;;
+ -*s*) return 0 ;;
+ esac
+ done
+
+ return 1
+}
+
+# Check if make should be loud (make V=1)
+is_loud() {
+ test "$XV"
+}
+
+MSG="$1"
+shift
+
+if ! is_quiet && ! is_loud; then
+ printf '%s\n' "$MSG"
+fi
+
+if [ $# -eq 0 ]; then
+ exit
+fi
+
+if is_loud; then
+ printf '%s\n' "$*"
+fi
+
+exec "$@"
diff --git a/build/pkgconf.sh b/build/pkgconf.sh
new file mode 100755
index 0000000..decf706
--- /dev/null
+++ b/build/pkgconf.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# 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 ${WITH_$LIB}
+ WITH_LIB="WITH_$(printf '%s' "$LIB" | tr 'a-z-' 'A-Z_')"
+ eval "WITH=\"\${$WITH_LIB:-}\""
+ case "$WITH" in
+ y|1) continue ;;
+ n|0) exit 1 ;;
+ esac
+
+ XCFLAGS="$XCFLAGS $("$0" --cflags "$LIB")" || exit 1
+ XLDFLAGS="$XLDFLAGS $("$0" --ldflags "$LIB")" || exit 1
+ XLDLIBS="$("$0" --ldlibs "$LIB") $XLDLIBS" || exit 1
+ build/cc.sh "build/with/$LIB.c" -o "gen/with/.$LIB.out" || 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..5de9ac2
--- /dev/null
+++ b/build/pkgs.mk
@@ -0,0 +1,33 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# 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/with/%.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/with/foo.h to foo
+PKG = ${@:gen/with/%.h=%}
+
+${HEADERS}::
+ @${MKDIR} ${@D}
+ @build/define-if.sh with/${PKG} build/pkgconf.sh ${PKG} >$@ 2>$@.log; \
+ build/msg-if.sh "[ CC ] with/${PKG}.c" test $$? -eq 0;
diff --git a/build/prelude.mk b/build/prelude.mk
new file mode 100644
index 0000000..c25dea4
--- /dev/null
+++ b/build/prelude.mk
@@ -0,0 +1,70 @@
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# 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,} => [empty]
+#
+# 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 ""
+export XV=${TRUTHY,${V}}
+
+# Suppress output unless V=1
+Q, := @
+Q := ${Q,${XV}}
+
+# Show full commands with `make V=1`, otherwise short summaries
+MSG = @build/msg.sh
+
+# cat a file if V=1
+VCAT,y := @cat
+VCAT, := @:
+VCAT := ${VCAT,${XV}}
+
+# All external dependencies
+ALL_PKGS := \
+ libacl \
+ libcap \
+ libselinux \
+ liburing \
+ oniguruma
diff --git a/build/version.sh b/build/version.sh
new file mode 100755
index 0000000..8f89921
--- /dev/null
+++ b/build/version.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# Copyright © Tavian Barnes <tavianator@tavianator.com>
+# SPDX-License-Identifier: 0BSD
+
+# Print the version number
+
+set -eu
+
+DIR="$(dirname -- "$0")/.."
+
+if [ "${VERSION-}" ]; then
+ printf '%s' "$VERSION"
+elif [ -e "$DIR/.git" ] && command -v git >/dev/null 2>&1; then
+ git -C "$DIR" describe --always --dirty
+else
+ echo "4.0.6"
+fi
diff --git a/build/with/libacl.c b/build/with/libacl.c
new file mode 100644
index 0000000..de1fe50
--- /dev/null
+++ b/build/with/libacl.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/acl.h>
+
+int main(void) {
+ acl_free(0);
+ return 0;
+}
diff --git a/build/with/libcap.c b/build/with/libcap.c
new file mode 100644
index 0000000..58e832c
--- /dev/null
+++ b/build/with/libcap.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <sys/capability.h>
+
+int main(void) {
+ cap_free(0);
+ return 0;
+}
diff --git a/build/with/libselinux.c b/build/with/libselinux.c
new file mode 100644
index 0000000..bca409d
--- /dev/null
+++ b/build/with/libselinux.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <selinux/selinux.h>
+
+int main(void) {
+ freecon(0);
+ return 0;
+}
diff --git a/build/with/liburing.c b/build/with/liburing.c
new file mode 100644
index 0000000..bea499a
--- /dev/null
+++ b/build/with/liburing.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <liburing.h>
+
+int main(void) {
+ io_uring_free_probe(0);
+ return 0;
+}
diff --git a/build/with/oniguruma.c b/build/with/oniguruma.c
new file mode 100644
index 0000000..cb17596
--- /dev/null
+++ b/build/with/oniguruma.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <oniguruma.h>
+
+int main(void) {
+ onig_free(0);
+ return 0;
+}