Vendoring a C library

Vendoring a C library

The standard library vendors two: libsodium for crypto and BearSSL for TLS. Both are organized the same way, and it is the pattern to copy.

The layout

c/crypto/vendor/
  libsodium/                  headers, architecture independent
  macos/arm/libsodium.a       one archive per os and arch
  macos/x86_64/libsodium.a
  linux/arm/libsodium.a
  linux/x86_64/libsodium.a
  ios/arm/libsodium.a
  ios/x86_64/libsodium.a
  android/arm/libsodium.a
  android/x86_64/libsodium.a
  windows/libsodium.lib       Windows is x86_64 only, so no arch level

Declaring it

The header directory goes in include, and each archive is listed per target:

[native]
include = ["c/crypto/vendor"]

[native.libs]
macos-arm    = ["c/crypto/vendor/macos/arm/libsodium.a"]
macos-x86_64 = ["c/crypto/vendor/macos/x86_64/libsodium.a"]
linux-arm    = ["c/crypto/vendor/linux/arm/libsodium.a"]
linux-x86_64 = ["c/crypto/vendor/linux/x86_64/libsodium.a"]
windows      = ["c/crypto/vendor/windows/libsodium.lib"]

Keys are all, an OS name, or <os>-<arch>. Every matching key contributes, and the paths are passed to the linker literally rather than as -l flags.

If the library needs a system library too, add it under [native.link].

Wrapping it

You generally cannot declare a vendored library's functions directly in a .peko.h, because their signatures use C types the FFI surface cannot express: structs, enums, function pointers, size_t.

So write a thin C wrapper that exposes only p_*-mappable entry points, and declare those. That is what the standard library does: one C file wraps libsodium, another wraps BearSSL, and the headers describe the wrappers rather than the libraries.

This is a feature, not an inconvenience. The wrapper is where you decide what crosses the boundary as a managed buffer, what stays an opaque handle, and where the lifetime rules are enforced.

Building the archives

Each archive must be built for the same target triple the Peko toolchain uses for that platform, which the toolchain descriptors record. An archive built for the wrong triple fails at link time with a mismatched target rather than something readable.

A checklist

  1. Build one static archive per platform and architecture you support.
  2. Put the public headers in one directory and add it to include.
  3. List each archive under [native.libs] with its target key.
  4. Add any system libraries to [native.link].
  5. Write a C wrapper exposing only FFI-expressible entry points.
  6. Declare the wrapper in a .peko.h.