2026-10-03 09:15:02 +03:00
|
|
|
#!/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
|
2026-10-03 09:22:23 +03:00
|
|
|
# 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 . _)
|
2026-10-03 09:15:02 +03:00
|
|
|
fetch "$WORK/curl.tar.xz" \
|
|
|
|
|
"https://curl.se/download/curl-$CURL_VERSION.tar.xz" \
|
2026-10-03 09:22:23 +03:00
|
|
|
"https://github.com/curl/curl/releases/download/curl-$CURL_TAG/curl-$CURL_VERSION.tar.xz"
|
2026-10-03 09:15:02 +03:00
|
|
|
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
|