tinymux/docs/building.md
Stephen Dennis da522e4762 build: find PCRE2 and OpenSSL via pkg-config, and probe OpenSSL before using it
On macOS the documented configure line did not work:

    cd mux && ./configure --enable-jit --enable-realitylvls --enable-wodrealms
    configure: error: pcre2.h header not found

Homebrew installs into a prefix clang does not search by default, and PCRE2
was discovered with AC_CHECK_LIB/AC_CHECK_HEADERS, which only look where the
compiler already looks.  OpenSSL, two lines below it, went through
PKG_CHECK_MODULES and worked fine.  Everyone on macOS had been passing
CPPFLAGS/LDFLAGS by hand to paper over the difference.

Put PCRE2 on pkg-config too, keeping the old link check as a fallback for
platforms that ship PCRE2 without a .pc file.  The discovered include path
goes into CPPFLAGS rather than a new PCRE2_CFLAGS substitution: CPPFLAGS
already propagates to every subdirectory, and the pcre2.h probe further down
preprocesses against it.

Removing the hand-passed flags then exposed two things they had been hiding.

**ganl/ never had the OpenSSL include path.**  src/openssl_transport.cpp
includes <openssl/ssl.h>, but ganl/Makefile.am lists neither $(OPENSSL_CFLAGS)
nor an equivalent -I.  It is the only subdirectory including OpenSSL headers
without it -- the other six have it.  Fixed by folding OPENSSL_CFLAGS into
CPPFLAGS, not by editing ganl/Makefile.am: the committed Makefile.in files come
from automake 1.16.5, and regenerating one with a newer automake rewrites the
whole file for the next contributor to churn back (#1477).

**OpenSSL was discovered ~460 lines after it was first needed.**
PKG_CHECK_MODULES([OPENSSL]) sat just above AC_OUTPUT, while the AC_CHECK_LIB
probes for SSL_new and EVP_sha256, and AC_CHECK_FUNCS(EVP_MD_CTX_create
EVP_MD_CTX_new SHA_Init), all run earlier.  Those probe by compiling and
linking, so with OpenSSL out of scope they did not error -- they answered "no",
and modules/engine/funmath.cpp turns that pair of "no"s into

    #error Need EVP_MD_CTX_new() or EVP_MD_CTX_create().

which surfaces during make, in a source file, far from the cause.  Moved the
block up next to the other library checks.  Only the -L entries go into
LDFLAGS; OPENSSL_LIBS also carries -lssl -lcrypto, which do not belong on every
link line.

--enable-nls still needs the prefix passed in on macOS, and always will:
gettext ships no .pc file, so there is nothing to discover it by.  That error
message now says so, and names the flags, instead of just reporting a missing
header.  Same for the pcre2.h message.

Verified on macOS arm64:

  * bare configure + make install + smoke: 1505 passed / 0 failed, 316/316
    dispatched, with no CPPFLAGS or LDFLAGS anywhere
  * the pkg-config fallback still works -- hid libpcre2-8.pc from pkg-config
    while leaving openssl visible, confirmed configure falls through to
    AC_CHECK_LIB and succeeds
  * the old flag-passing invocation still configures cleanly, so nobody's
    existing command line or alias breaks
  * every OpenSSL probe now answers correctly: SSL_new yes, EVP_sha256 yes,
    EVP_MD_CTX_new yes, EVP_MD_CTX_create no (right for OpenSSL 3.x)

configure regenerated with autoconf 2.73, matching the committed one.

Also adds docs/building.md -- per-platform prerequisites, the autotools
version constraints above, the Ragel #line churn, and a symptom-to-cause table
for the failures above -- and points CLAUDE.md at it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 21:15:24 -06:00

6.6 KiB

Building TinyMUX — platform notes

CLAUDE.md carries the short version: configure, make install from the repo root, make test. This file is for when that does not just work, and for the platform-specific traps that cost time.

The short version

cd mux && ./configure --enable-jit --enable-realitylvls --enable-wodrealms
cd .. && make install
make test

As of the PCRE2/OpenSSL discovery fix, that line needs no CPPFLAGS or LDFLAGS on any supported platform for the default build. If you are carrying such flags in muscle memory or a shell alias, you can drop them; they still work and are still needed for --enable-nls on macOS (below).

Prerequisites

Debian / Ubuntu

sudo apt install build-essential autoconf automake libtool pkg-config \
                 libpcre2-dev libssl-dev

Optional, only if you regenerate things:

Package Needed for
ragel regenerating *_scan.cpp, color_ops.c, muxescape.cpp from .rl
bison the omega converter (see reference_omega_build)

gettext for --enable-nls comes with libc6-dev — glibc provides it, so there is nothing extra to install and nothing extra to pass.

macOS (Homebrew)

brew install pcre2 openssl@3 pkg-config

Optional: gettext (only for --enable-nls), ragel and bison (only to regenerate), autoconf automake libtool (only to regenerate configure).

Homebrew's prefix is /opt/homebrew on Apple Silicon and /usr/local on Intel. Use $(brew --prefix) in scripts rather than hardcoding either.

The thing worth understanding about macOS: clang does not search Homebrew's prefix by default. /opt/homebrew/include is not on the default include path, so a library being "installed" is not the same as the compiler being able to find it. PCRE2 and OpenSSL are located through pkg-config, which knows the real paths, which is why the default build needs no flags. Anything without pkg-config metadata has to be pointed at by hand.

Windows

MSVC via the .vcxproj files; there is no autotools path. See CLAUDE.md for the GANL test harness invocation. Note issue #1499 (/utf-8).

When you still need flags

--enable-nls on macOS. GNU gettext ships no .pc file, so unlike PCRE2 and OpenSSL it cannot be discovered:

cd mux && ./configure --enable-jit --enable-realitylvls --enable-wodrealms \
    --enable-nls \
    CPPFLAGS="-I$(brew --prefix)/include" LDFLAGS="-L$(brew --prefix)/lib"

libintl.h is symlinked into the main prefix (gettext is not keg-only), so the plain prefix is enough — no gettext-specific opt/gettext paths are required.

On Linux, --enable-nls needs no flags at all.

Anything installed outside pkg-config's view. Point PKG_CONFIG_PATH at the directory holding the .pc file rather than reaching for CPPFLAGS; that keeps the include and library paths consistent with each other.

Regenerating build system files — read before running autotools

The committed configure and Makefile.in files are checked in, and the tool versions that produced them matter. Using a different version rewrites the whole file and produces a diff that the next contributor, on the version you did not have, will churn straight back.

File Generated by Regenerate with
mux/configure autoconf 2.73 cd mux && autoconf
mux/*/Makefile.in automake 1.16.5 avoid — see below

Prefer not to touch Makefile.am. If a change can be expressed in configure.ac instead, do it there: configure.ac needs only autoconf, and 2.73 is what the tree already uses. Adding an include path is the common case, and folding it into CPPFLAGS in configure.ac reaches every subdirectory without regenerating a single Makefile.in.

autoconf will warn that aclocal.m4 was generated for 2.71. That warning is expected and the output is correct; do not "fix" it by running autoreconf --force --install, which rewrites the aux scripts for no benefit.

Related history: #1477, where a configure regenerated on one autoconf version blocked every agent on the other.

Generated source files

See generated-files.md for the full map. Two behaviours that look like bugs and are not:

  • Ragel output churns #line numbers. Regenerating color_ops.c with a different Ragel than the one that produced the committed copy yields a diff that is entirely #line directives — no code change. Revert it rather than committing it. A pre-commit hook blocks generated output committed without its source.

  • make clean removes committed generated files. mux/lib/color_ops.c is checked in and deleted by make clean, so the tree reads as dirty-by- deletion until the next build regenerates it. git checkout -- restores it.

Failure modes and what they actually mean

Symptom Cause
pcre2.h header not found PCRE2 dev files missing, or installed where pkg-config cannot see them. Install libpcre2-dev / brew install pcre2, or set PKG_CONFIG_PATH.
openssl/ssl.h file not found during make OpenSSL include path not reaching a subdirectory. Should not happen since the discovery fix; if it does, the subdirectory's AM_CPPFLAGS is the place to look.
#error Need EVP_MD_CTX_new() or EVP_MD_CTX_create() configure's OpenSSL function probes answered "no". They probe by linking, so this means OpenSSL was not in scope at configure time — re-run configure and check checking for EVP_MD_CTX_new... yes.
--enable-nls requires libintl.h macOS without the Homebrew prefix passed in. See above.
ASan reports "Interceptors are not working" A partially-built tree: some objects instrumented, some not. make clean and rebuild the whole thing. Do not debug the report itself.

That last one deserves emphasis, because it wastes an afternoon every time. A partially-built tree lies. After changing configure flags — sanitizers, --enable-nls, JIT — run make clean before make install. A stale muxscript against a fresh engine.so produces failures that describe nothing real.

Verifying a build

make test          # builds, installs, runs the smoke suite

A healthy run ends with a count and a dispatch tally, both of which matter:

=== Smoke: ALL 1505 TESTS PASSED ===
  Dispatched: 316 / 316 generated

The dispatch line is the completeness gate (#1391). A suite that passes everything it ran while quietly running less than it generated is not green. Trust the harness's own counters over a grep -c of the log; verdict phrasing varies and hand-rolled greps undercount.