Notcurses-Native.git | scripts/ci/ | build-libunistring.sh


#!/usr/bin/env bash # Build + install GNU libunistring — the Unicode string library # notcurses uses for grapheme-cluster segmentation (unigbrk.h), # word breaking (uniwbrk.h) and character property lookups # (unictype.h). notcurses' CMakeLists.txt hard-requires it: # `find_path(unigbrk.h)` + `find_library(unistring unistring REQUIRED)`, # with a FATAL_ERROR if either misses. # # All four platforms source-build it now. It was the last # package-manager-sourced LGPL library left in the shipped packs, and # package-manager sourcing means we cannot point a user at the exact # corresponding source for the binary we handed them: dnf/apk/brew/ # pacman all move their package versions under us and garbage-collect # old builds, so "the source for the libunistring.so.5 in this # archive" would have been unanswerable a few months after any given # release. libunistring is LGPL-3.0-or-later OR GPL-2.0-or-later and # we convey it under the LGPL, which obliges us to be able to hand # over that source. A pinned tarball + recorded SHA-256 in # resources/third-party.json makes that answerable for good: # # * Linux manylinux_2_28 — was `dnf install libunistring-devel` # (0.9.9, so the packs shipped libunistring.so.2). # * Linux alpine 3.20 — was `apk add libunistring-dev`. # * macOS arm64 — was `brew install libunistring`. macOS x86_64 # already source-built it here, because brew's x86_64 bottles # target macOS 14+ and would fail that lane's 10.15 # deployment-target audit. # * Windows MSYS2 — was `pacman -S mingw-w64-*-libunistring`. # # Plain autotools, no unusual configure surface. Honours $PREFIX # (default /usr/local) so the install can target a workspace-relative # cache dir that actions/cache persists between runs, and honours # $MACOSX_DEPLOYMENT_TARGET on macOS — libtool passes it through to # clang, which stamps LC_BUILD_VERSION minos on the produced dylib. # # Platform notes: # * SONAME major is 5 for the whole 1.x series (lib/Makefile.am # pins -version-info 7:R:2, and 7 - 2 = 5), so the shipped file # is libunistring.so.5 / libunistring.5.dylib / libunistring-5.dll. # Bumping to a libunistring 2.x would change that, and the # belt-and-braces sweep list in .github/actions/bundle-dll plus # the basename patterns in resources/third-party.json both spell # the major out — grep for `libunistring-5` before bumping. # * MSYS2/mingw: libtool installs the DLL under $PREFIX/bin and the # import library under $PREFIX/lib, the same split ffmpeg's # mingw32 target uses. `find_library(unistring unistring)` # consumes $PREFIX/lib/libunistring.dll.a; bundle-dll picks the # DLL up out of $PREFIX/bin via its extra-search-path. # * iconv is left to configure's own detection on the POSIX lanes, # where glibc, musl and libSystem all provide it inside libc so # GNU libiconv never enters the picture. On MSYS2 there is no # iconv in the C runtime, AM_ICONV finds GNU libiconv, and # libunistring-5.dll ends up importing libiconv-2.dll — which is # why the Windows lanes now source-build libiconv too and this # script points AM_ICONV at it explicitly (see the # --with-libiconv-prefix block below). notcurses itself uses # unistr / unigbrk / unictype / uniwbrk and never touches # uniconv, so what libunistring does with iconv is a # redistribution question rather than a functional one. set -euxo pipefail # 1.4.2 — current stable. VERSION='1.4.2' # ftpmirror.gnu.org redirects to a random mirror and intermittently # 502s; retry it, then fall back to the canonical (slower) host. # Both spellings serve byte-identical tarballs — the SHA-256 recorded # in resources/third-party.json is checked against whichever answered. URL="https://ftpmirror.gnu.org/libunistring/libunistring-${VERSION}.tar.gz" FALLBACK_URL="https://ftp.gnu.org/gnu/libunistring/libunistring-${VERSION}.tar.gz" PREFIX="${PREFIX:-/usr/local}" mkdir -p "$PREFIX" # Portable parallelism: GNU nproc on Linux, sysctl on macOS, fall # back to 4 if neither. JOBS="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)" cd /tmp curl -fSL --retry 5 --retry-delay 10 -o libunistring.tar.gz "$URL" \ || curl -fSL --retry 5 --retry-delay 10 -o libunistring.tar.gz "$FALLBACK_URL" tar -xzf libunistring.tar.gz cd "libunistring-${VERSION}" # If a libiconv has been source-built into this prefix — which only # happens on the Windows lanes, where build-libiconv.sh runs before # this script — make AM_ICONV resolve THAT one rather than searching # the compiler's default paths and finding the msystem's pacman copy # under the identical basename. --with-libiconv-prefix is gnulib's # own lever for this (AC_LIB_LINKFLAGS_BODY): it puts # $PREFIX/include on the probe's -I list and $PREFIX/lib on its -L # list, so both the header and the import library come from us. # # Conditional, and keyed on the header actually being there, so the # POSIX lanes see a configure line byte-identical to the one they have # always run: they have iconv in libc, nothing installs an iconv.h # into their prefix, and pointing gnulib at a directory with no # libiconv in it would only add a pointless probe. iconv_flags=() if [[ -f "$PREFIX/include/iconv.h" ]]; then iconv_flags+=( "--with-libiconv-prefix=$PREFIX" ) fi # --enable-shared / --disable-static — bundleable .so / .dylib / .dll, # matching every other library in the pack. libunistring has no NLS # and no --disable-doc equivalent worth passing; the docs it installs # are info/man pages under $PREFIX/share, which nothing bundles. ./configure \ --prefix="$PREFIX" \ --enable-shared \ --disable-static \ ${iconv_flags[@]+"${iconv_flags[@]}"} make -j"$JOBS" make install # libunistring ships no pkg-config file, so there is no # `pkg-config --modversion` self-check to make like the codec # scripts do. Verify the two artefacts the consumers actually need # instead, and fail loudly if either is missing — a silent # half-install here surfaces ten minutes later as notcurses' cmake # saying "Couldn't find unigbrk.h from GNU libunistring", with no # hint that this script is where it went wrong. missing=0 if [[ ! -f "$PREFIX/include/unigbrk.h" ]]; then echo "::error::libunistring install left no \$PREFIX/include/unigbrk.h" missing=1 fi # The linkable artefact: libunistring.so.N / libunistring.N.dylib on # POSIX, libunistring.dll.a (import lib, with the DLL in bin/) on # mingw. `find` rather than a glob so an unmatched pattern is a # count of 0 rather than a literal-string test. if [[ $(find "$PREFIX/lib" -maxdepth 1 -name 'libunistring.*' | wc -l) -eq 0 ]]; then echo "::error::libunistring install left nothing matching \$PREFIX/lib/libunistring.*" missing=1 fi if (( missing != 0 )); then echo "❌ libunistring ${VERSION} did not install into $PREFIX as expected." ls -la "$PREFIX/lib" "$PREFIX/include" 2>/dev/null || true exit 1 fi echo "--- installed libunistring artefacts ---" find "$PREFIX/lib" "$PREFIX/bin" -maxdepth 1 -name 'libunistring*' 2>/dev/null || true cd / rm -rf "/tmp/libunistring-${VERSION}" /tmp/libunistring.tar.gz