How to Integrate mulle-objc-runtime with the mulle-clang Compiler

The mulle-objc-runtime requires the mulle-clang compiler to inject specific preprocessor macros (TPS, FCS, TAO) and generate metadata, making integration a matter of installing the mulle-objc-cc toolchain and compiling .m source files with the mulle-clang driver.

The mulle-objc-runtime is a modern Objective-C runtime designed specifically for use with the custom mulle-clang compiler front-end. Unlike standard Objective-C runtimes that compile with generic toolchains, mulle-objc-runtime depends on compiler-generated metadata and specific macro definitions that only mulle-clang provides. Attempting to compile the runtime headers with a standard C compiler or vanilla clang triggers explicit #error directives in src/mulle-objc-runtime.h that halt the build immediately.

Why mulle-clang is Mandatory

The runtime’s public header src/mulle-objc-runtime.h contains compile-time sanity checks that enforce the presence of compiler-injected symbols. Lines 45–51 validate that type-preserving-symbols (TPS) switches are defined, while lines 52–63 enforce fast class setup (FCS), TAO, and universe identifiers. These macros—__MULLE_OBJC_TPS__, __MULLE_OBJC_FCS__, __MULLE_OBJC_TAO__, and __MULLE_OBJC_UNIVERSEID__—are injected automatically by the mulle-clang driver during compilation.

Without these definitions, the header aborts with an error directing you to use mulle-clang. This design creates a closed loop where only code compiled through mulle-clang can successfully link against the runtime.

Architecture Overview

Understanding how the components interact helps clarify the integration requirements:

  • src/mulle-objc-runtime.h – The public API header that gate-keeps compilation. It performs sanity checks for mulle-clang-injected macros and includes all internal runtime headers.

  • cmake/share/CompilerDetectionC.cmake – CMake logic that detects whether the invoked compiler is mulle-clang by searching for MULLECLANG in the compiler executable name (lines 15–31). It sets the internal MULLE_C_COMPILER_ID variable for downstream flag handling.

  • cmake/share/CompilerFlagsC.cmake – Adds common compiler definitions like -DMULLE_INCLUDE_DYNAMIC=1. While it does not currently add mulle-clang-specific flags, the detection step ensures the compiler ID is known for Objective-C-specific configurations.

  • mulle-objc-cc package – Supplies the mulle-clang driver and auxiliary tooling. This package is typically installed via mulle-sde and must be available on the PATH.

  • Test suites – The test-compiler and test-compiler-runtime directories contain examples that explicitly require mulle-clang, as noted in their README files, demonstrating valid compiler usage patterns.

Step-by-Step Integration Guide

Follow these steps to configure a working build environment that correctly integrates the runtime with the compiler.

1. Install the Toolchain

Use mulle-sde to install the runtime and the compiler wrapper:

mulle-sde install --prefix /usr/local \
    https://github.com/mulle-objc/mulle-objc-runtime/archive/latest.tar.gz

This command pulls in mulle-objc-runtime, mulle-objc-debug, mulle-objc-runtime-startup, and the mulle-objc-cc package containing the compiler driver.

2. Configure Your Project

For new or existing projects, add the required dependencies:

mulle-sde init -d my-project -m mulle-c/c-developer executable
cd my-project
mulle-sde dep add github:mulle-objc/mulle-objc-runtime
mulle-sde dep add github:mulle-objc/mulle-objc-debug
mulle-sde dep add --startup github:mulle-objc/mulle-objc-runtime-startup
mulle-sde dep add github:mulle-cc/mulle-objc-cc
mulle-sde mark mulle-objc-cc no-bequeath
mulle-sde mark mulle-objc-cc no-link
mulle-sde mark mulle-objc-cc no-import
mulle-sde mark mulle-objc-cc no-header

The mark commands ensure the compiler wrapper is used only for compilation, not linked as a library.

3. Write Objective-C Source Files

Rename any .c files to .m. The README explicitly warns that .c files will not work with mulle-clang and must use the Objective-C extension.

Create a source file that includes the runtime header:

// hello.m
#import <mulle-objc-runtime/mulle-objc-runtime.h>

int main(void)
{
    struct _mulle_objc_universe *universe = 
        mulle_objc_global_get_universe_inline(__MULLE_OBJC_UNIVERSEID__);
    
    if (universe && universe->name)
        printf("Universe: %s\n", universe->name);
    
    mulle_objc_global_finish();
    return 0;
}

4. Build with mulle-clang

Compile using the mulle-clang driver, which automatically defines the required macros:

mulle-clang -Wall -Wextra -O2 -o hello hello.m -lmulle-objc-runtime

If you inspect the pre-processed output, you will see __MULLE_OBJC_TPS__, __MULLE_OBJC_FCS__, and __MULLE_OBJC_TAO__ injected at the top of the translation unit.

5. Run the Binary

Execute your program:

./hello

The default universe typically returns (null) for the name unless explicitly configured otherwise.

Working with Custom Universes

For applications requiring isolated runtime environments, create named universes using the TAO-enabled functions:

// custom-universe.m
#import <mulle-objc-runtime/mulle-objc-runtime.h>

static const char *MyUniverseName = "MyApp";

int main(void)
{
    struct _mulle_objc_universe *universe;

    universe = mulle_objc_universe_create(0, MyUniverseName);
    mulle_objc_global_set_universe(universe);

    printf("Created universe %s\n", MyUniverseName);
    
    mulle_objc_global_finish();
    return 0;
}

Compile using the same command. The mulle_objc_global_finish() call is mandatory for custom universes to release resources, though optional for the default universe.

Troubleshooting Common Integration Errors

Symptom Root Cause Solution
#error "Use the mulle-clang …" Compiling with standard clang/gcc or using .c extension Rename files to .m and ensure mulle-clang is the compiler executable
Undefined __MULLE_OBJC_TPS__ Build system bypassing the mulle-clang driver Configure CMake to use mulle-clang as both the C and ObjC compiler; verify CompilerDetectionC.cmake detects MULLECLANG
Linker errors for mulle_objc_* Runtime library not linked Add -lmulle-objc-runtime to linker flags or use mulle-sde link
Runtime crash on allocation Universe not initialized or __MULLE_OBJC_NO_TPS__ manually defined Keep default TPS configuration; do not disable type-preserving symbols unless you understand the memory layout implications

Summary

  • mulle-clang is non-negotiable: The runtime headers in src/mulle-objc-runtime.h enforce this at compile time via #error directives.
  • Use .m extensions: The README explicitly states that .c files fail; Objective-C mode is required.
  • Install mulle-objc-cc: This package provides the driver that defines __MULLE_OBJC_TPS__, __MULLE_OBJC_FCS__, and __MULLE_OBJC_TAO__.
  • Link correctly: Always link against -lmulle-objc-runtime when building executables.
  • Manage universes: Use mulle_objc_global_get_universe_inline for the default universe or mulle_objc_universe_create for isolated environments, ensuring proper cleanup with mulle_objc_global_finish.

Frequently Asked Questions

Can I compile mulle-objc-runtime with standard clang or GCC?

No. The header src/mulle-objc-runtime.h contains explicit checks for macros like __MULLE_OBJC_TPS__ and __MULLE_OBJC_FCS__ that only mulle-clang injects. Attempting to compile with standard compilers results in an immediate #error directive aborting the build.

Why must source files use the .m extension instead of .c?

According to the README (lines 60–68), mulle-clang treats .m files as Objective-C source and enables the specific language mode required to generate the metadata the runtime expects. Compiling .c files bypasses these code paths, leaving required symbols undefined.

How does CMake detect mulle-clang?

The build system uses cmake/share/CompilerDetectionC.cmake (lines 15–31) to inspect the compiler executable name for the substring MULLECLANG. When detected, it sets MULLE_C_COMPILER_ID to identify the compiler for downstream flag configuration in CompilerFlagsC.cmake.

What are TPS, FCS, and TAO in the context of mulle-objc?

These are compiler features that mulle-clang enables via injected macros: Type-Preserving-Symbols (TPS) maintains type information through the compilation pipeline; Fast Class Setup (FCS) optimizes class initialization; and TAO (Tiny/special Object handling) relates to universe and object lifecycle management. These must be present for the runtime in src/mulle-objc-runtime.h to compile successfully.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →