The Windows build took PCRE2 from a hand-built out-of-tree directory
reached through $(Pcre2Dir). engine.vcxproj honoured that property for
its headers but hardcoded ..\..\src\pcre2\{Release,Debug} for its
libraries, so -p:Pcre2Dir= applied to one half and not the other and the
link failed with LNK1104 in any tree where PCRE2 was not at the default.
Declare pcre2 in mux/vcpkg.json instead, alongside grpc and
nlohmann-json, and point netmux, libmux and engine at $(VcpkgDir). All
three now read include and library paths from the same place. Note
vcpkg's layout is lib/ for release and debug/lib/ for debug, not
Release/ and Debug/.
PCRE2's JIT is required -- funceval2.cpp calls pcre2_jit_compile -- and
comes automatically: the port's platform-default-features pulls in jit
everywhere except emscripten and iOS.
Ship mux/vcpkg.json in both source TOCs. It was in neither, so the
source distribution carried no statement of its own dependencies; with
PCRE2 moving to vcpkg, the file that declares it has to travel with the
source or an unpacking reader is worse off than before.
Unix is unaffected -- configure finds system PCRE2 via pkg-config.
Verified on hatsuhara: Release and Debug both build clean with no
hand-built PCRE2 present anywhere on the box.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8.5 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).
Third-party dependencies come from vcpkg (#2212). mux/vcpkg.json declares
them — currently grpc, nlohmann-json and pcre2 — pinned to a
builtin-baseline commit:
git clone https://github.com/microsoft/vcpkg C:\vcpkg
C:\vcpkg\bootstrap-vcpkg.bat
cd mux && C:\vcpkg\vcpkg.exe install --triplet x64-windows
That materialises mux/vcpkg_installed/x64-windows/ (gitignored), which every
project reads via $(VcpkgDir). Release libraries land in lib/ and debug in
debug/lib/ — not Release/ and Debug/, which is what the old hand-built
PCRE2 layout used.
Two things that will waste your afternoon otherwise:
- Do not
git clone --depth 1. The pinned baseline commit is not in a shallow clone and resolution fails withfailed to git show versions/baseline.json. Recover withgit fetch --depth 1 origin <baseline-sha>. - First run is slow — grpc dominates, roughly an hour and ~11 GB. It is a
one-time cost: vcpkg's binary cache (
%LOCALAPPDATA%\vcpkg\archives) makes any later tree a few seconds.
PCRE2's JIT is required (funceval2.cpp calls pcre2_jit_compile) and arrives
automatically — the port's platform-default-features pulls in jit on every
platform except emscripten and iOS, so a plain "pcre2" dependency is enough.
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
#linenumbers. Regeneratingcolor_ops.cwith a different Ragel than the one that produced the committed copy yields a diff that is entirely#linedirectives — no code change. Revert it rather than committing it. A pre-commit hook blocks generated output committed without its source. -
make cleanremoves committed generated files.mux/lib/color_ops.cis checked in and deleted bymake 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. |
| Coherent wrong behaviour after a header edit; same commit green on another box | Objects present without sibling .d files — make has no header edges for them and under-rebuilds. #2118. Engine and libmux drop such orphans automatically; if the smell remains, make clean. |
That first partial-tree row 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. The same class of lie is a header edit that rebuilds only some of its
dependents when .d files are missing (#2118, closed the false #2107 chase):
the binary is internally inconsistent but fails in a way that looks like a
real defect. Prefer treating same-commit green on another host as a build-hygiene
signal before inventing a platform bug.
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.