Skip to content

Vendor PCRE2 (compile from source via zig cc) so std.regex works in aetherc->zig->crossbuild without a sysroot #1389

Description

@paul-hammant

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:

  1. Vendor the source subsetpcre2_compile.c, pcre2_match.c, pcre2_chartables.c, pcre2_tables.c, pcre2_ucd.c, and the ~dozen supporting .c files.
  2. 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.
  3. 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).
  4. JIT decisionpcre2_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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions