build: relocate libidn2 strchrnul prototype into config.h

Move the macOS strchrnul declaration from a side-channel header injected
via -include into vendor/libidn2/config.h itself, gated on __APPLE__.
config.h is the canonical place where libidn2 documents its
HAVE_STRCHRNUL detection — co-locating the platform fallback prototype
there mirrors gnulib's own approach (which declares missing symbols in
its substituted <string.h>) and removes the per-platform build-flag
asymmetry plus the dedicated include path.

The Darwin-only strchrnul.c implementation is unchanged; build.zig now
just adds it on Darwin without touching the lib/ flags array.

Verified that on both `-target x86_64-macos.14.0` (no libc strchrnul)
and `-target x86_64-macos.15.4` (libc has it) a TU mimicking lookup.c
(`#include "config.h"` then call strchrnul) compiles cleanly with no
implicit-declaration error and no redeclaration warning.
This commit is contained in:
Navid EMAD
2026-05-02 10:00:42 +02:00
parent 40900a358c
commit 7b5e18e23d
4 changed files with 29 additions and 30 deletions

View File

@@ -551,26 +551,23 @@ fn buildLibidn2(
// libc itself; on musl it would also need a separate -liconv.
mod.linkSystemLibrary("iconv", .{});
// libidn2's lib/lookup.c calls strchrnul() unconditionally — a glibc
// extension absent from macOS libc and not declared by <string.h>.
// Upstream relies on gnulib substituting <string.h> + linking
// gl/strchrnul.c; we don't wire up that overlay, so ship a small
// Darwin-only shim and inject its prototype via -include.
mod.addIncludePath(b.path("vendor/libidn2/darwin"));
// libidn2's lib/lookup.c calls strchrnul(), a glibc extension that
// macOS libc lacked before 15.4 and that lookup.c never gets a
// declaration for (it doesn't include <string.h>). Upstream solves
// this through gnulib's substituted <string.h> + gl/strchrnul.c;
// we don't wire up that overlay. Provide our own implementation —
// the matching prototype is declared in vendor/libidn2/config.h
// under #ifdef __APPLE__, so every libidn2 TU sees it via the
// existing #include <config.h>.
lib.addCSourceFile(.{
.file = b.path("vendor/libidn2/darwin/strchrnul.c"),
.flags = &.{},
});
}
const lib_flags: []const []const u8 = if (is_darwin)
&.{ "-DHAVE_CONFIG_H", "-DIDN2_STATIC", "-include", "strchrnul.h" }
else
&.{ "-DHAVE_CONFIG_H", "-DIDN2_STATIC" };
lib.addCSourceFiles(.{
.root = dep.path("lib"),
.flags = lib_flags,
.flags = &.{ "-DHAVE_CONFIG_H", "-DIDN2_STATIC" },
.files = &.{
"bidi.c", "context.c", "data.c", "decode.c",
"error.c", "free.c", "idna.c", "lookup.c",

View File

@@ -429,6 +429,17 @@
/* Define to 1 if you have the `strchrnul' function. */
#define HAVE_STRCHRNUL 1
/* lib/lookup.c calls strchrnul() but never #include <string.h>, so it relies
on a declaration being visible by the time it processes config.h. Upstream
gets it from gnulib's substituted <string.h>; we don't wire up that overlay
(see build.zig::buildLibidn2). On macOS the symbol is also missing from
libc before 15.4 — build.zig adds vendor/libidn2/darwin/strchrnul.c on
Darwin to satisfy the link. Declare the prototype here so every libidn2
TU that includes config.h sees a real declaration before preprocessing. */
#ifdef __APPLE__
char *strchrnul(const char *s, int c_in);
#endif
/* Define if you have `strerror_r'. */
#define HAVE_STRERROR_R 1

View File

@@ -1,15 +1,14 @@
/* macOS libc lacks strchrnul (a glibc extension). libidn2's lib/lookup.c
calls it directly to walk DNS label boundaries. Upstream's portable build
relies on gnulib substituting <string.h> with a declaration and linking
gl/strchrnul.c, but this build does not wire up that overlay. We provide
a small Darwin-only shim with the same semantics.
/* Darwin-only strchrnul shim for libidn2.
gnulib's strchrnul.c falls through to rawmemchr() when the search byte is
NUL — also a glibc extension. libidn2 only ever searches for '.', so a
straightforward byte scan is sufficient and avoids pulling in a second
shim. */
strchrnul is a glibc extension. On macOS it is missing from libc before
15.4 and is never made visible to libidn2's lib/lookup.c (which calls it
without including <string.h>). The matching prototype is declared in
vendor/libidn2/config.h under #ifdef __APPLE__, so callers compile;
this file provides the symbol so they link.
#include "strchrnul.h"
gnulib's strchrnul.c falls through to rawmemchr() when the search byte
is NUL — also a glibc extension. libidn2 only ever searches for '.', so
a straight byte scan is enough and avoids dragging in a second shim. */
char *strchrnul(const char *s, int c_in) {
const unsigned char c = (unsigned char) c_in;

View File

@@ -1,8 +0,0 @@
/* Prototype for the macOS strchrnul shim. See strchrnul.c. */
#ifndef LPD_LIBIDN2_DARWIN_STRCHRNUL_H
#define LPD_LIBIDN2_DARWIN_STRCHRNUL_H
char *strchrnul(const char *s, int c_in);
#endif