Building Zynx

Build targets, LLVM modes, test tiers, SDK knobs, and cleanup commands.

Zynx uses CMake and Ninja for the real build graph. The public developer interface is still make, so normal work starts with:

make
make test

The default build configures build/cmake, builds ./zynx, builds the runtime archive, and bootstraps the standard library archive plus .zxm module headers with the freshly built compiler.

Requirements

Install CMake 3.20 or newer, Ninja, Python 3, a C23-capable Clang toolchain, LLVM development packages with CMake package files, and libclang development headers for bindgen support.

On Debian or Ubuntu, matching packages such as llvm-18-dev, clang-18, libclang-18-dev, and lld-18 usually provide the expected layout.

Common Targets

make                  # Configure and build using system LLVM/shared libclang.
make configure        # Regenerate CMake/Ninja build files.
make test             # Run core and tooling tests.
make install          # Install under PREFIX, default /usr/local.
make uninstall        # Remove installed Zynx files and SDKs from PREFIX.
make libs             # Build compiler, runtime, and standard library archives.
make lint             # Run clang-tidy through compile_commands.json.
make static           # Build with bundled LLVM and static LLVM/Clang/libclang.
make deps-llvm        # Download/build/install the bundled LLVM dependency.
make sdk              # Build default SDK targets.
make clean            # Remove Zynx build outputs, preserving expensive deps.
make distclean        # Remove Zynx outputs plus build/deps and build/sdk.

Install locations can be changed with PREFIX:

make install PREFIX=/tmp/zynx-install

Test Tiers

The default test suite is hermetic by policy. Tests that require host facilities declare a .requires file with capability names such as loopback, filesystem-write, dynamic-linking, subprocess, or network-external.

Run the default compiler and tooling tiers with:

python3 tests/run.py
python3 tests/tooling/run.py
python3 tests/tooling/test_docs_examples.py

Loopback socket tests are a separate required host-capability tier:

python3 tests/run.py --require-capability loopback --only-requiring-capability loopback

network-external is not part of the default tier. It must be explicitly allowed when a platform-specific run really needs it.

Build Options

OptionDefaultMeaning
BUILDTYPEdebugdebug maps to CMake Debug; release maps to MinSizeRel.
PREFIX/usr/localInstall prefix used by make install and make uninstall.
CC / CXX / ARtoolchain defaultC compiler, C++ linker/compiler, and archive tool.
LLVM_PROVIDERsystemUse installed LLVM or bundled LLVM under build/deps.
LLVM_LINKsharedLink shared or static LLVM/Clang/libclang.
LLVM_VERSION22.1.5Bundled LLVM release; changing it needs a matching pinned hash.
LLVM_TARGETSNativeLLVM targets built for bundled LLVM.
LLVM_CONFIGllvm-configUsed to derive system LLVM package paths.
ZYNX_USE_LLDautoUse lld on ELF executable links.
ZYNX_LINK_ICFsafeIdentical code folding mode for static ELF links.
ZYNX_LINK_MAPOFFWrite a linker map for size inspection.
EXTRA_CFLAGSemptyExtra C compiler flags.
EXTRA_LDFLAGSemptyExtra executable linker flags.

LLVM Modes

The normal default uses system LLVM and a shared build-time libclang link:

make LLVM_PROVIDER=system LLVM_LINK=shared

Bindgen links to the Clang C API at build time. There is no runtime LIBCLANG_PATH discovery or dlopen path for bindgen.

Static compiler builds are explicit:

make static

That target forces bundled LLVM and static LLVM/Clang/libclang linkage into the zynx executable. Platform libraries such as libc, libm, pthreads, or macOS frameworks may still be dynamic.

Useful system LLVM overrides:

make LLVM_CONFIG=/opt/homebrew/opt/llvm/bin/llvm-config
make LLVM_DIR=/path/to/lib/cmake/llvm Clang_DIR=/path/to/lib/cmake/clang
make LIBCLANG_LIBRARY=/path/to/libclang.so
make CLANG_C_INCLUDE_DIR=/path/to/include

Bundled LLVM

make deps-llvm builds the pinned bundled LLVM dependency into build/deps/llvm/install. The bundled build uses LLVM 22.1.5 from the release tarball with a pinned SHA-256 hash in the repository CMake files.

Useful bundled dependency knobs:

make deps-llvm LLVM_JOBS=8
make deps-llvm LLVM_BUILDTYPE=MinSizeRel
make deps-llvm LLVM_CCACHE=on
make deps-llvm LLVM_BUNDLED_PREFIX=/opt/zynx-llvm

LLVM_CCACHE=auto uses ccache when it is installed. LLVM_CCACHE=on requires it.

SDK Build Knobs

The SDK is optional and is covered in detail on the Zynx SDK page. The build wrapper exposes these common selectors:

make sdk
make sdk TARGET=x86_64-linux-musl
make sdk SDK_TARGETS="x86_64-darwin-macho aarch64-darwin-macho"
make sdk-clean TARGET=x86_64-linux-musl

SDK source pins can be overridden for local experiments:

OptionDefaultMeaning
SDK_MUSL_VERSION1.2.6musl source release for default Linux SDK targets.
SDK_LINUX_KERNEL_VERSION6.12.9Linux UAPI headers release.
SDK_ZLIB_VERSION1.3.2zlib source release for SDK sysroots.
DARWIN_SDK_PROVIDERautoDarwin provider: auto, xcode, zig, or portable.
DARWIN_MIN_VERSION11.0Minimum macOS deployment version for Darwin targets.

SDK targets are static executable targets in the current SDK contract. Dynamic runtime-loadable modules remain host/system mode only.

Static Size

The shared build stays small because LLVM and libclang come from system shared libraries. Static mode is larger because libclang.a pulls in substantial Clang and LLVM code.

On Linux, static mode uses size-oriented options when available:

make static ZYNX_USE_LLD=on
make static ZYNX_USE_LLD=on ZYNX_LINK_ICF=safe
make static ZYNX_LINK_MAP=ON

Check dynamic dependencies after a static build with:

otool -L ./zynx # macOS
ldd ./zynx      # Linux

Static mode should not show dynamic libLLVM, libclang, or libclang-cpp dependencies.

Cleanup

make clean removes Zynx build products but keeps expensive dependency and SDK trees. Use make distclean when build/deps, build/sdk, and SDK driver outputs should be removed too.

Generate or refresh the compilation database with:

make compile_commands.json
make refresh-compile-commands