Files
solo-c/mkstaticdeps.sh
T
Luxferre 5852a9732b Fix the curl GitHub mirror, repair the troubleshooting table
The GitHub fallback for curl was a 404 written untested: curl tags its
releases with underscores in the version, 8.22.0 becoming curl-8_22_0,
while the asset name keeps the dots. Derive the tag from the version
rather than assuming the two match. Verified by making the primary host
unresolvable and letting the fallback carry the fetch; the tarball
passes xz -t and extracts to curl-8.22.0/.

The README troubleshooting section was a definition list. It is now a
three-column table, matching the reference sections elsewhere in the
file, with a row added for make static-deps failing to download, which
had no coverage despite the script fetching two tarballs.

Two of its entries were wrong:

- One documented "cannot find -lcares", which the Makefile can no longer
  produce. The recipe passes the explicit path static-deps/libcares.a
  and the prerequisite check fails first with its own message. Verified
  by moving the archive aside and reading what make actually says.

- The ares_* entry gave one diagnostic for two causes that read opposite
  ways. `nm -u static-deps/libcurl.a | grep -c ares_` returns zero when
  the archive was built without c-ares, and nonzero when c-ares is in
  there and the failure is link order. Split them apart.
2026-10-03 09:22:23 +03:00

139 lines
5.5 KiB
Bash
Executable File

#!/bin/sh
# Rebuild static-deps/libcurl.a and static-deps/libcares.a.
#
# Why this exists: the distro libcurl.a cannot be linked statically on Alpine
# because it pulls in libpsl, libbrotli, libzstd and libnghttp2, and those
# static archives ship LTO bytecode produced by GCC 14 while the installed
# compiler is GCC 15. The linker cannot read it:
# bytecode stream in file '.../libpsl.a' generated with LTO version 14.0
# instead of the expected 15.1
# so psl_*, BrotliDecoder* and ZSTD_* simply never resolve. Passing
# -fno-lto does not help: the objects are LTO bytecode, not C.
#
# The fix is a libcurl built without those four libraries. What is left
# (ssl, crypto, z, cares, idn2, unistring) is all LTO-free, so it links
# cleanly into a fully static binary. Cost: no HTTP/2, no brotli/zstd
# Content-Encoding and no public-suffix cookie checks, none of which a
# plain JSON API client needs.
#
# c-ares is built here too, for two reasons. On a minimal system there is often
# no libcares.a at all (Void's c-ares-devel ships the shared library only), so
# `make static` would fail on -lcares; and where an archive does exist it can
# carry the same LTO bytecode problem as the distro libcurl.a. Building our own
# pins the version and guarantees the archive is plain objects.
#
# curl 8.22 takes c-ares as --enable-ares[=PREFIX], not the older
# --with-cares. It reads PREFIX/lib/pkgconfig/libcares.pc, which `make install`
# of c-ares writes, so the prefix built below is enough. Both spellings are
# wrong in one way or another: --with-cares no longer exists, and unknown
# configure options only produce a warning, so a typo here silently disables
# async DNS and you would not notice until the link failed. Same for
# --without-librtmp, which curl 8.22 no longer knows either; it is gone below.
#
# Overridable: CURL_VERSION, CARES_VERSION, OUT (or $1), FORCE=1.
set -e
CURL_VERSION=${CURL_VERSION:-8.22.0}
CARES_VERSION=${CARES_VERSION:-1.34.8}
OUT=${1:-$(pwd)/static-deps}
FORCE=${FORCE:-0}
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT INT TERM
JOBS=$(nproc 2>/dev/null || echo 2)
CA=""
for c in /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-bundle.crt \
/etc/pki/tls/certs/ca-bundle.crt; do
[ -f "$c" ] && { CA=$c; break; }
done
# fetch DEST URL...: first URL that downloads wins.
fetch() {
_dest=$1
shift
for _u in "$@"; do
echo "fetching $_u"
if curl -fsSL -o "$_dest" "$_u"; then
return 0
fi
done
echo "mkstaticdeps: no mirror worked for $_dest" >&2
return 1
}
# An archive is only reused when the stamp beside it still names the version we
# would build. Without this a stale libcurl.a from some earlier build silently
# skips the whole curl step, which is exactly the kind of thing that hides a
# broken configure line.
fresh() {
[ "$FORCE" != 1 ] && [ -f "$1" ] && [ -f "$2" ] &&
[ "$(cat "$2" 2>/dev/null)" = "$3" ]
}
stamp() { echo "$2" > "$1"; }
mkdir -p "$OUT"
# --- c-ares -------------------------------------------------------------
# curl's configure wants the headers and the .pc file, not just the archive, so
# this prefix outlives the run. It lives under static-deps/, which is ignored,
# and it is what makes the skip below safe: without the .pc file a rerun would
# point --enable-ares at a prefix that no longer exists.
CARES_PREFIX=${CARES_PREFIX:-$OUT/cares-prefix}
CARES_STAMP=$OUT/.cares-version
if fresh "$OUT/libcares.a" "$CARES_STAMP" "$CARES_VERSION" &&
[ -f "$CARES_PREFIX/lib/pkgconfig/libcares.pc" ]; then
echo "libcares.a $CARES_VERSION present, skipping (FORCE=1 to rebuild)"
else
fetch "$WORK/cares.tgz" \
"https://c-ares.dev/downloads/c-ares-$CARES_VERSION.tar.gz" \
"https://github.com/c-ares/c-ares/releases/download/v$CARES_VERSION/c-ares-$CARES_VERSION.tar.gz"
echo "extracting c-ares $CARES_VERSION"
tar xzf "$WORK/cares.tgz" -C "$WORK"
cd "$WORK/c-ares-$CARES_VERSION"
# c-ares 1.34 has no --disable-tools; only the test suite is optional.
./configure --prefix="$CARES_PREFIX" \
--enable-static --disable-shared \
--disable-tests \
CFLAGS="-O2 -fno-lto"
make -j"$JOBS"
make install
cp "$CARES_PREFIX/lib/libcares.a" "$OUT/libcares.a"
stamp "$CARES_STAMP" "$CARES_VERSION"
echo "wrote $OUT/libcares.a"
fi
# --- curl ---------------------------------------------------------------
CURL_STAMP=$OUT/.curl-version
if fresh "$OUT/libcurl.a" "$CURL_STAMP" "$CURL_VERSION"; then
echo "libcurl.a $CURL_VERSION present, skipping (FORCE=1 to rebuild)"
else
# curl tags its GitHub releases with underscores in the version (8.22.0 ->
# curl-8_22_0), the file name keeps the dots. Verified 2026-10-03.
CURL_TAG=$(printf '%s' "$CURL_VERSION" | tr . _)
fetch "$WORK/curl.tar.xz" \
"https://curl.se/download/curl-$CURL_VERSION.tar.xz" \
"https://github.com/curl/curl/releases/download/curl-$CURL_TAG/curl-$CURL_VERSION.tar.xz"
echo "extracting curl $CURL_VERSION"
tar xf "$WORK/curl.tar.xz" -C "$WORK"
cd "$WORK/curl-$CURL_VERSION"
set -- --disable-shared --enable-static \
--with-ssl \
--enable-ares="$CARES_PREFIX" \
--without-libpsl --without-brotli --without-zstd --without-nghttp2 \
--without-libssh2 \
--disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet \
--disable-tftp --disable-pop3 --disable-imap --disable-smtp --disable-gopher \
--disable-mqtt --disable-manual --disable-docs \
CFLAGS="-O2 -fno-lto"
[ -n "$CA" ] && set -- "$@" --with-ca-bundle="$CA"
./configure "$@"
make -j"$JOBS"
mkdir -p "$OUT"
cp lib/.libs/libcurl.a "$OUT/libcurl.a"
stamp "$CURL_STAMP" "$CURL_VERSION"
echo "wrote $OUT/libcurl.a"
fi