Diff options
Diffstat (limited to '')
-rwxr-xr-xtools/build-development.sh36
-rwxr-xr-xtools/build-release.sh33
-rwxr-xr-xtools/serve.py15
3 files changed, 51 insertions, 33 deletions
diff --git a/tools/build-development.sh b/tools/build-development.sh
index 79b9259..00ff569 100755
--- a/tools/build-development.sh
+++ b/tools/build-development.sh
@@ -1,13 +1,10 @@
#!/bin/sh
-# Build cgit for development and debugging. It compiles without optimisation
-# and with full debug information, holds the sources to git's DEVELOPER
-# warnings, and links in AddressSanitizer and UndefinedBehaviorSanitizer, so a
-# memory error or undefined behaviour stops the program where it happens. With
-# clang it also leaves a compilation database in build/compile_commands.json,
-# where a language server looks for one, and with ctags a tags file in
-# build/tags. The sanitizers make cgit slow, so the result is for testing and
-# debugging rather than serving.
+# Build cgit for development and debugging. The sanitizers the compiler offers
+# stop the program at a memory error or undefined behaviour, which makes it
+# slow, so the result is for testing rather than serving. With clang it also
+# leaves a compilation database in build/ for a language server, and with
+# ctags a tags file there.
#
# Any arguments go on to make, the way Lua flags do where pkg-config cannot
# find Lua:
@@ -18,14 +15,33 @@
set -eu
-# Every make target below is written relative to the repository root.
cd "$(dirname "$0")/.."
# The build starts from clean so the bundled git objects pick up the same
# flags, and cleanall rather than clean because only cleanall descends into
# vendor/git.
make cleanall
-make "$@" CFLAGS="-O0 -g3" DEVELOPER=1 SANITIZE=address,undefined \
+
+# Named once, so the probe and the build use the same compiler. CC goes on
+# make's command line because git's Makefile sets it, which would override
+# the one from the environment.
+CC=${CC:-cc}
+
+# OpenBSD's compiler has no AddressSanitizer, so the build falls back to the
+# undefined behaviour checks alone, and to no sanitizer where neither links.
+probe=$(mktemp "${TMPDIR:-/tmp}/cgit-probe.XXXXXX")
+trap 'rm -f "$probe" "$probe.c"' EXIT
+printf 'int main(void){return 0;}\n' >"$probe.c"
+sanitize=""
+for checks in address,undefined undefined
+do
+ if $CC -fsanitize="$checks" -o "$probe" "$probe.c" >/dev/null 2>&1
+ then
+ sanitize="SANITIZE=$checks"
+ break
+ fi
+done
+make "$@" CC="$CC" CFLAGS="-O0 -g3" DEVELOPER=1 $sanitize \
GENERATE_COMPILATION_DATABASE=yes
# clang writes one entry per object under vendor/git/compile_commands, each
diff --git a/tools/build-release.sh b/tools/build-release.sh
index ade979c..b5e6faa 100755
--- a/tools/build-release.sh
+++ b/tools/build-release.sh
@@ -1,8 +1,9 @@
#!/bin/sh
-# Build cgit for a release with Linux hardening flags, which are ELF and GCC or
-# Clang specific. The flags add a stack protector, fortified libc calls, a
-# position independent executable, and full RELRO. By default Lua is pinned off
+# Build cgit for a release with hardening flags: a stack protector, fortified
+# libc calls, a position independent executable, and full RELRO. Each flag a
+# toolchain may lack is probed first and left out where it is missing, so the
+# script builds on Linux, the BSDs and macOS alike. By default Lua is pinned off
# so the binary needs no Lua at runtime, and running it as
# ./tools/build-release.sh lua links in the backend behind the "lua:" filter
# prefix instead, which needs a Lua dev package installed.
@@ -27,7 +28,6 @@ lua)
;;
esac
-# Every make target below is written relative to the repository root.
cd "$(dirname "$0")/.."
CC=${CC:-cc}
@@ -71,17 +71,24 @@ then
CFLAGS="$CFLAGS -ffile-prefix-map=$PWD=."
fi
-LDFLAGS="-pie -Wl,-z,relro,-z,now -Wl,-z,noexecstack"
+# The linker flags are probed the same way, since Apple's linker has no -z
+# options and makes a position independent executable unasked.
+LDFLAGS=""
+if supports "-fPIE -pie"
+then
+ LDFLAGS="-pie"
+fi
+for flag in -Wl,-z,relro,-z,now -Wl,-z,noexecstack
+do
+ if supports "$flag"
+ then
+ LDFLAGS="$LDFLAGS $flag"
+ fi
+done
# These reach only the cgit objects, so git's own sources are not held to them.
# -Wformat-security is an error because a non-literal format with no arguments
-# is never intentional. -Wformat-nonliteral would catch the module-link shape,
-# a non-literal that does take arguments, but it also fires on va_list
-# forwarding and on local format constants, so it is left to manual runs when
-# touching anything that formats.
-#
-# make cgit CGIT_EXTRA_CFLAGS=-Wformat-nonliteral
-#
+# is never intentional.
CGIT_EXTRA_CFLAGS="-Wformat -Wformat-security -Werror=format-security"
# The build starts from clean so the bundled git objects pick up the same
@@ -90,5 +97,5 @@ CGIT_EXTRA_CFLAGS="-Wformat -Wformat-security -Werror=format-security"
# which would override the one from the environment the flags were probed
# with.
make cleanall
-exec make "$@" CC="$CC" CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" \
+make "$@" CC="$CC" CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" \
CGIT_EXTRA_CFLAGS="$CGIT_EXTRA_CFLAGS"
diff --git a/tools/serve.py b/tools/serve.py
index 8924e53..492345e 100755
--- a/tools/serve.py
+++ b/tools/serve.py
@@ -65,12 +65,9 @@ def split_cgi_output(output: bytes) -> CgiResponse:
The header block ends at the first blank line. cgit ends its lines with a
bare LF while a filter writing its own headers may use CRLF, so whichever
- terminator appears earliest is the real one. Looking for CRLF everywhere
- before falling back to LF would instead find the first CRLF in the body,
- and a commit message with DOS line endings has one, which swallowed the
- whole document head into the headers. Splitting on the separator rather
- than on a non-empty body keeps a legitimately empty body, such as the one
- a redirect leaves behind, from being read as headers.
+ terminator appears earliest is the real one, and a later blank line of the
+ other kind, such as one in a commit message with DOS line endings, belongs
+ to the body. The body may also be empty, as a redirect leaves it.
"""
ends = [(at, len(sep)) for sep in (b"\r\n\r\n", b"\n\n") if (at := output.find(sep)) >= 0]
if ends:
@@ -233,8 +230,6 @@ class CgitHandler(BaseHTTPRequestHandler):
_ = self.wfile.write(response.body)
def log_message(self, format: str, *args: object) -> None:
- # The signature matches BaseHTTPRequestHandler, so the parameter here
- # shadows the builtin of the same name.
_ = sys.stderr.write(f" {format % args}\n")
@@ -262,14 +257,14 @@ class Options(argparse.Namespace):
data: str = str(REPO_ROOT / "assets")
-def parse_args(argv: list[str] | None = None) -> Options:
+def parse_args() -> Options:
parser = argparse.ArgumentParser(description="Preview cgit locally.")
_ = parser.add_argument("--config", default=Options.config, help="path to cgitrc (default: ./cgitrc)")
_ = parser.add_argument("--port", type=int, default=Options.port)
_ = parser.add_argument("--host", default=Options.host)
_ = parser.add_argument("--cgit", default=Options.cgit, help="path to the cgit binary")
_ = parser.add_argument("--data", default=Options.data, help="directory holding cgit.css, cgit.js, images")
- return parser.parse_args(argv, namespace=Options())
+ return parser.parse_args(namespace=Options())
def main() -> None: