Primary motivation: unblock aetherc -> zig -> crossbuild for regex-using programs
Today, cross-compiling any program that uses std.regex requires a CROSSBUILD_SYSROOT that has libpcre2 staged for the target triple. From tools/ae_cross.c:307-315:
"openssl/nghttp2/zlib/pcre2 are not bundled by zig for any target; aether-crossbuild's provision.sh <triple> builds them into sysroots/<triple>/. … Without it, warn-and-omit stands (the features report unavailable at runtime)."
So ae build --target <triple> of a regex program either needs a fully-provisioned per-target sysroot, or std.regex silently degrades to "unavailable at runtime" on the cross build. That's the blocker: the point of the zig cross backend is self-contained cross-compilation (zig bundles each target's libc — Tier A is truly no-sysroot), and pcre2 is one of the Tier-2 libs that breaks that promise for regex.
Vendoring PCRE2 and compiling it from source through zig cc removes the sysroot requirement for std.regex — a regex program then cross-builds for aarch64-linux / macos / windows / freebsd with no per-target pcre2 provisioning. PCRE2 is pure, dependency-free, portable C (BSD-licensed), which is exactly the case zig cc cross-compiles best.
Same tricks as miniaudio / the SQLite proposal (#1372)
The real caveat: PCRE2 is NOT a single amalgamation (harder than SQLite)
Unlike sqlite3.c (one file) or miniaudio.h (one header), PCRE2 is a multi-file, CMake/autotools-configured build. Vendoring means:
- Vendor the source subset —
pcre2_compile.c, pcre2_match.c, pcre2_chartables.c, pcre2_tables.c, pcre2_ucd.c, and the ~dozen supporting .c files.
- Pre-generate + commit the config that CMake normally produces:
config.h (fixed feature set), pcre2.h (from pcre2.h.generic), and pcre2_chartables.c (default table). This is the crux — a pinned, committed config so no CMake/autotools runs at Aether build time.
- Compile flags:
-DPCRE2_CODE_UNIT_WIDTH=8 -DHAVE_CONFIG_H (matching the existing std/regex/aether_regex.c which already uses the 8-bit width).
- JIT decision —
pcre2_jit_compile.c is the fast path but is architecture-specific and the thorniest thing to cross-compile. Recommended: JIT off for vendored/cross builds (interpreted matcher only — correct, just slower), keep system-pcre2-with-JIT as the native default (see next).
Keep it opt-in / degradable (std.regex already is)
std.regex is already optional — AETHER_HAS_PCRE2 (set when pkg-config libpcre2-8 succeeds), else every entry point returns "regex: built without libpcre2-8". So vendoring can be additive and flag-gated:
- Native builds that find a system libpcre2 keep using it (with JIT).
- Cross builds (and native builds without system pcre2, or an explicit
--with-vendored-pcre2) compile the vendored source. This preserves today's behaviour and only adds the sysroot-free cross path.
Version pinning / provenance
Pin an exact PCRE2 release + SHA-256 for the vendored/generated files, documented, so the config generation is reproducible and auditable. As with #1372, prefer fetch-on-demand + checksum over committing the full tree if repo size is a concern (PCRE2 source is a few MB).
Acceptance
ae build --target aarch64-linux prog.ae where prog.ae uses std.regex links and runs a working regex engine with no CROSSBUILD_SYSROOT and no system libpcre2 for the target — the vendored source is compiled via zig cc and cached per (triple, source-hash). Native builds with a system libpcre2 are unchanged (still JIT).
Related
Primary motivation: unblock
aetherc -> zig -> crossbuildfor regex-using programsToday, cross-compiling any program that uses
std.regexrequires aCROSSBUILD_SYSROOTthat has libpcre2 staged for the target triple. Fromtools/ae_cross.c:307-315:So
ae build --target <triple>of a regex program either needs a fully-provisioned per-target sysroot, orstd.regexsilently degrades to "unavailable at runtime" on the cross build. That's the blocker: the point of the zig cross backend is self-contained cross-compilation (zig bundles each target's libc — Tier A is truly no-sysroot), and pcre2 is one of the Tier-2 libs that breaks that promise for regex.Vendoring PCRE2 and compiling it from source through zig cc removes the sysroot requirement for
std.regex— a regex program then cross-builds for aarch64-linux / macos / windows / freebsd with no per-target pcre2 provisioning. PCRE2 is pure, dependency-free, portable C (BSD-licensed), which is exactly the case zig cc cross-compiles best.Same tricks as miniaudio / the SQLite proposal (#1372)
std.audio's vendored miniaudio (.github/workflows/ci.yml"miniaudio object cache"): key = per-(OS/CC/harden) + source-hash, split restore/save (PRs read, merge-to-main fills on a miss). Pcre2's object is ~seconds to compile, cached thereafter.matrix.os/matrix.ccin the key), so cross caching is a deliberate follow-on for both this and contrib.sqlite: vendor the SQLite amalgamation + compile via zig cc for self-contained cross builds (miniaudio-style cache) #1372.fix/pcre2-static-mingwpain (there's already a branch for it) — vendoring the source sidesteps "find/build a static libpcre2 per platform," Windows especially.The real caveat: PCRE2 is NOT a single amalgamation (harder than SQLite)
Unlike
sqlite3.c(one file) orminiaudio.h(one header), PCRE2 is a multi-file, CMake/autotools-configured build. Vendoring means:pcre2_compile.c,pcre2_match.c,pcre2_chartables.c,pcre2_tables.c,pcre2_ucd.c, and the ~dozen supporting.cfiles.config.h(fixed feature set),pcre2.h(frompcre2.h.generic), andpcre2_chartables.c(default table). This is the crux — a pinned, committed config so no CMake/autotools runs at Aether build time.-DPCRE2_CODE_UNIT_WIDTH=8 -DHAVE_CONFIG_H(matching the existingstd/regex/aether_regex.cwhich already uses the 8-bit width).pcre2_jit_compile.cis the fast path but is architecture-specific and the thorniest thing to cross-compile. Recommended: JIT off for vendored/cross builds (interpreted matcher only — correct, just slower), keep system-pcre2-with-JIT as the native default (see next).Keep it opt-in / degradable (std.regex already is)
std.regexis already optional —AETHER_HAS_PCRE2(set whenpkg-config libpcre2-8succeeds), else every entry point returns "regex: built without libpcre2-8". So vendoring can be additive and flag-gated:--with-vendored-pcre2) compile the vendored source. This preserves today's behaviour and only adds the sysroot-free cross path.Version pinning / provenance
Pin an exact PCRE2 release + SHA-256 for the vendored/generated files, documented, so the config generation is reproducible and auditable. As with #1372, prefer fetch-on-demand + checksum over committing the full tree if repo size is a concern (PCRE2 source is a few MB).
Acceptance
ae build --target aarch64-linux prog.aewhereprog.aeusesstd.regexlinks and runs a working regex engine with noCROSSBUILD_SYSROOTand no system libpcre2 for the target — the vendored source is compiled via zig cc and cached per (triple, source-hash). Native builds with a system libpcre2 are unchanged (still JIT).Related
std.regex) rather than a contrib addition.