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.
139 lines
5.5 KiB
Bash
Executable File
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
|