Skip to content

Repository files navigation

mcpp

A modern C++ module-first build tool — written in pure C++23 modules, fully self-hosted

English | 简体中文

Release C++23 Module License

Documentation · Getting Started · mcpp.toml Guide · Examples · Toolchains
Package index mcpp-index · Module libraries mcpplibs · Community Forum · Issues · Releases
ci-linux ci-macos ci-windows

mcpp demo

Highlights

  • Modular build system — C++ modules first: import std handled automatically, file-level incremental builds, automatic dependency analysis, nothing to configure
  • Build plugins and heterogeneous hardwarebuild.mcpp and rule packages extend the build; CUDA, HIP, SYCL, Vulkan/SPIR-V and Ascend C are each a rule package
  • Package management and a module-library ecosystem — SemVer constraints, lockfile, cross-project BMI cache, custom indices; a library from mcpplibs is two lines away from import
  • Toolchain management and cross-compilationfamily@version installed on demand; --target moves the same build to Windows, macOS, Cortex-M or RISC-V bare metal, and one source tree reaches several hosted targets over openkal
  • Environment and runtime — the user-space environment xlings provides: toolchains and dependencies stay in an isolated sandbox, and a runner puts the artifact on a board or an emulator
  • Pure modular self-hosting — mcpp is written entirely in C++23 module interface units and builds itself

Why mcpp

mcpp is built specifically for C++23 module-first development. If you want to use import std, module interface units (.cppm), module partitions, and other modern C++ features in your project, mcpp gives you a smooth, friendly experience on Linux, macOS ARM64, and Windows x86_64.

C++ normally spreads these five jobs across five tools, and mcpp is one command for all five. The second row names what each column is usually recognised as.

mcpp build system build plugins package manager toolchain manager environment and runtime
closest to CMake + Ninja CMake modules, xmake rules vcpkg, Conan rustup, nvm conda, Nix

Note

Early-stage project — mcpp is under active development; interfaces and behavior may change in future releases. Developers interested in modern C++ module-first build tooling are welcome to contribute. Questions / feedback / ideas — drop a note in issues.

Getting Started

Install

Install via xlings (recommended)

xlings install mcpp -y
Don't have xlings yet? Click for the install command

Linux / macOS

curl -fsSL https://d2learn.org/xlings-install.sh | bash

Windows — PowerShell

irm https://d2learn.org/xlings-install.ps1.txt | iex

More about xlings → xlings.d2learn.org

Optional — short commands (mp, mbuild, mrun, …)
xlings install mcpp-short-cmd -y

Registers 30 shims, so mcpp build becomes mbuild. Naming rule: initial of every word but the last, plus the last word in full — mcpp self doctormsdoctor. mp is bare mcpp. They alias the mcpp shim rather than a fixed binary, so xlings use mcpp <ver> switches them too.

Short Expands to Short Expands to
mp mcpp mexpkg mcpp emit xpkg
mnew mcpp new mxparse mcpp xpkg parse
mbuild mcpp build mtinstall mcpp toolchain install
mrun mcpp run mtlist mcpp toolchain list
mtest mcpp test mtdefault mcpp toolchain default
mclean mcpp clean mcdir mcpp cache dir
madd mcpp add mclist mcpp cache list
mremove mcpp remove mcinfo mcpp cache info
mupdate mcpp update mcgc mcpp cache gc
msearch mcpp search milist mcpp index list
mpublish mcpp publish miadd mcpp index add
mpack mcpp pack miremove mcpp index remove
msdoctor mcpp self doctor miupdate mcpp index update
msenv mcpp self env msconfig mcpp self config
msversion mcpp self version msexplain mcpp self explain

Other options

Option 1 — one-line installer (Linux x86_64/aarch64, macOS ARM64)
curl -fsSL https://github.com/mcpp-community/mcpp/releases/latest/download/install.sh | bash

This installer does not support Windows; use the PowerShell xlings route above. It installs into ~/.mcpp/ and adds it to your shell PATH. Deleting ~/.mcpp uninstalls cleanly.

Option 2 — Homebrew (macOS / Linux)
brew install mcpp-community/mcpp/mcpp-m

One command — it taps mcpp-community/homebrew-mcpp and installs the same prebuilt release binary. macOS needs Apple silicon + macOS 14; per-user data lives in ~/.mcpp/.

Homebrew's mcpp is an unrelated C preprocessor, hence the mcpp-m formula name — the command it installs is still mcpp.

Homebrew 6 gates third-party taps. The fully-qualified command above is read as explicit intent and works as-is, but every other spelling — the short brew install mcpp-m, the mcpp alias, and later upgrades — is refused with:

Refusing to load formula mcpp-community/mcpp/mcpp-m from untrusted tap
mcpp-community/mcpp.

Trust the tap once and all of them work:

brew trust mcpp-community/mcpp
Option 3 — Arch Linux (AUR)
yay -S mcpp-bin      # prebuilt release binary
yay -S mcpp-m        # or build from source (bootstrapped with mcpp-bin)

Installs the mcpp command system-wide; per-user data still lives in ~/.mcpp/. On Arch the name mcpp is an unrelated C preprocessor, so the packages are mcpp-bin / mcpp-m (see scripts/aur/). Stable-release automation reconciles mcpp-bin only; mcpp-m and mcpp-git remain manually maintained and may intentionally lag.

Option 4 — let an AI assistant install it for you

Copy the following prompt to your AI coding assistant (Claude Code / Cursor / Copilot, etc.):

Read the README of https://github.com/mcpp-community/mcpp,
then install mcpp for me and create a C++23 module project, build and run it.
The repo's .agents/skills/mcpp-usage/SKILL.md has a detailed usage guide.

Create, build & run a project

mcpp new hello
cd hello
mcpp build
mcpp run

Note: the first build initializes the environment and fetches the toolchain, which may take a while.

Project layout

hello/
├── mcpp.toml             ← project manifest
├── src/
│   └── main.cpp          ← import std; works directly
└── tests/
    └── test_smoke.cpp    ← discovered by `mcpp test`
# mcpp.toml
[package]
name        = "hello"
version     = "0.1.0"
description = "A modular C++23 package"
license     = "Apache-2.0"

The built-in scaffold relies on convention: it does not write [targets.hello]. src/main.cpp infers the binary target, and mcpp test automatically discovers tests/test_smoke.cpp.

Using module libraries

Add a two-line dependency to mcpp.toml to pull in a community module library from mcpplibs:

[dependencies]
cmdline = "0.0.2"

Then import it directly in your code:

import mcpplibs.cmdline;

For more dependency options (version constraints, namespaces, Git references, local paths, etc.), see the mcpp.toml guide — dependency management.

Feature Overview

Build system
  • Native C++20/23/26 module support (interface units, implementation units, module partitions), plus c++latest / c++fly experimental modes
  • Fully automatic precompilation and caching of import std / import std.compat
  • Three-layer incremental optimization: front-end dirty check + per-file P1689 dyndep + BMI copy-if-different restat
  • Fingerprinted BMI cache: hashed by compiler/flags/standard library, shared across projects
  • Ninja backend: auto-generated build.ninja, parallel compilation
  • compile_commands.json generated automatically (ready for clangd / ccls); use mcpp build --configure-only to refresh it before ordinary sources compile
  • First-class C support: .c files auto-detected, mixed C/C++ projects
  • User-defined cflags / cxxflags / ldflags / c_standard
Toolchain management
  • Bundled GCC 16.1.0 + LLVM/Clang 20.1.7, one-command install
  • Host-aware defaults: native glibc GCC on Linux x86_64, musl GCC on other Linux architectures, LLVM on macOS and on Windows with usable MSVC, MinGW-w64 GCC on bare Windows
  • Multiple versions side by side: mcpp toolchain install gcc 16 / mcpp toolchain install llvm 20
  • Isolated sandbox: all toolchains live in ~/.mcpp/registry/, leaving the system untouched
  • Per-platform selection: linux = "gcc@16", macos = "llvm@20"
  • GCC and Clang compile pipelines at parity (driven by the BmiTraits abstraction layer)
Cross-compilation, bare metal and devices
  • mcpp build --target <triple> — one flag; the toolchain payload for that target is resolved and installed automatically
  • Targets from x86_64-linux-gnu to Cortex-M, Cortex-A and RISC-V bare metal; the full table is under Platform Support
  • Freestanding targets carry no operating system: the C library, startup code, memory layout and emulator travel with a board-support package rather than with mcpp
  • Runners reach an artifact that cannot run on the build machine — mcpp run --runner flash, --list-runners, mcpp why runners
  • mcpp new --template riscv-virt-rt — a board template a package ships, instantiated by name
  • Cross-compilation over openkal: a portable program builds for a target whose kernel interface and C library come from packages
Heterogeneous builds and accelerators
  • [build] accel = "cuda12.9+{sm_89}, vulkan1.2" — a build names one or more device backends, and cfg(accelerator = "cuda") is true in that build
  • Five programming models have rule packages today: CUDA, HIP, SYCL, Vulkan/SPIR-V and Ascend C
  • Device translation units are compiled by their own compiler and join the ordinary link; the host/device boundary is generated rather than written twice
  • A constrained glob selects device sources: { glob = "src/kernels/**/*.cu", accel = "cuda12.9+{sm_89}" }
  • Nothing in the engine holds a vendor name, so a sixth backend is a package rather than an engine change
Package & dependency management
  • SemVer constraint resolution: ^, ~, ranges, exact versions
  • Three-stage resolution: constraint merging → multi-version mangling fallback → exact match
  • Lockfile mcpp.lock (v2 format: index snapshot + namespaces)
  • Namespace system: [dependencies.myteam] foo = "1.0"
  • Custom package indices: [indices] acme = "git@..." / { path = "..." }
  • Project-level index isolation (.mcpp/ directory, no global pollution)
  • Dependency sources: index / Git / local path
Workspaces
  • [workspace] members = ["libs/*", "apps/*"]
  • Unified lockfile + unified target directory
  • Centralized version management: [workspace.dependencies] + .workspace = true
  • Selective builds: mcpp build -p member-name
  • Config inheritance: toolchains, build flags, and indices cascade from root to members
Packaging & publishing
  • mcpp pack: four Linux release modes — system / vendored (default) / self-contained / static; bundle-project and bundle-all remain compatibility aliases
  • Fully static musl binaries: single-file distribution, no glibc dependency (matching Linux x86_64 or aarch64 target)
  • mcpp publish: generates xpkg.lua + publishes to a package index
  • Automatic RPATH fix-up via patchelf (Linux)
Extending the build
  • build.mcpp — a build program for a step mcpp has no rule for, speaking a directive protocol that is versioned rather than guessed
  • mcpp::action declares work with explicit inputs and outputs, so a generated file takes part in the incremental graph instead of sitting outside it
  • A rule package carries that step to other projects: it declares a rule module, and a consumer selects it as a feature
  • Payloads, runtime adapters and board-support packages are ordinary packages — a tool, a driver or a board is installed by the resolver that installs a library
Developer experience
  • mcpp new — create a modular project; --template [ns.]name[@version][:tname] uses a package-provided template with the same exact identity style as mcpp add. A sole template is the default even without default = true; --list-templates [ns.]name[@version] lists ambiguous sets
  • mcpp run [-- args] — build and run
  • mcpp test [pattern] [-- args] — auto-discover and run tests (filter by name; --list, --timeout <s>, --message-format json)
  • mcpp search — search package indices
  • mcpp add / remove / update — dependency management
  • Profiles and features on the command line: --release / --profile <name> on build and run, --features <list> on build, run and test
  • mcpp why [toolchain|runtime|deps|runners] — explain resolved build decisions; --format json for a machine reader
  • mcpp emit sbom — a CycloneDX bill of materials for the resolution just recorded
  • mcpp --offline / MCPP_OFFLINE=1 — use only already available local state
  • mcpp explain E0001 — detailed error-code explanations
  • mcpp self doctor — environment self-diagnosis

Benchmark

Building mcpp itself — 137 module interface units and 57k lines at the pinned workload, every one of them import std; — with four engines handed the same compiler binary. Each cell is the median of 3 samples and how many times faster it is than cmake. Every column comes from one run.

scenario mcpp mcpp +opt mcpp (old) cmake xmake
cold 86.69s · 1.1x 35.73s · 2.6x 86.75s · 1.1x 91.74s · 1.0x 90.54s · 1.0x
noop 0.16s · 2.0x 0.18s · 1.8x 0.24s · 1.3x 0.32s · 1.0x 0.38s · 0.8x
touch-hub 0.42s · 197.7x 0.42s · 197.2x 81.72s · 1.0x 83.21s · 1.0x 82.48s · 1.0x
edit-body 80.87s · 1.1x 29.83s · 2.9x 81.19s · 1.1x 85.30s · 1.0x 84.33s · 1.0x
edit-comment 0.40s · 207.0x 0.40s · 207.0x 79.11s · 1.1x 83.21s · 1.0x 82.15s · 1.0x

cold nothing built yet · noop nothing at all · touch-hub mtime only, content unchanged · edit-body a real edit inside a function body · edit-comment a comment added to a hub interface.

mcpp = mcpp@2026.8.13.1, the build under test · mcpp +opt = the SAME binary as mcpp, with the opt-in key [build] bmi_schedule = "on" (off by default) · mcpp (old) = mcpp@2026.8.11.3, the previously published release.
Linux x86_64 · i9-13900K · gcc 16.1.0 · n=3 · pinned workload a749e9f · cmake 4.4.2 / xmake 3.1.0 · min/max sit within 4% of every median above 1s · data: standard-20260814-linux-x86_64.

  • Cascade suppression accounts for the touch-hub and edit-comment rows. cmake and xmake decide by timestamp and rebuild every downstream unit; mcpp compares the BMI the compiler has just produced against the previous one and skips the cascade when the interface is unchanged. This is default behaviour and requires no configuration. mcpp (old) measures the previous release at 81.72s, level with cmake, so the effect is new in this revision.
  • edit-body is the control, and mcpp is deliberately not fast on it. The perturbation inserts a statement into an interface unit, which moves the source positions GCC records, so the BMI changes and every importer is owed a rebuild — an engine that were fast on that row would be skipping work it owes. Whether an edit owes a cascade depends on where the body lives: an in-place edit of the same length, or a body in a separate .cpp, owes none and lands near the 200x rows. Measured in .agents/docs/2026-08-15-module-edit-granularity.md.
  • bmi_schedule is opt-in and disabled by default (auto resolves to off). It moves code generation off the critical path, so it pays only where a cascade is owed — cold 86.69s → 35.73s, edit-body 80.87s → 29.83s, and nothing on the two rows mcpp already skips. An incorrect scheduling change fails silently rather than loudly, so the default is not changed on the evidence of a single machine.

Methodology, pinned versions, and the full data → bench/README.md · 简体中文

Platform Support

mcpp's identity model has two orthogonal axes: a toolchain is family@version (family ∈ gcc | llvm | msvc), a target is a triple arch-os[-env]. Cross-compiling is just mcpp build --target <triple> — the right toolchain payload is resolved and installed automatically. mcpp toolchain list shows live status on your machine.

Hosts (where mcpp itself runs): Linux x86_64 / aarch64, macOS arm64, Windows x86_64.

Targets (what --target accepts; the rows and their tiers are the ones in modules/toolchain-model/src/triple.cppm, which is also what mcpp toolchain list reports for this machine):

Target Convention toolchain Tier
x86_64-linux-gnu gcc (Linux default) or llvm verified
x86_64-linux-musl gcc 16, fully static verified
aarch64-linux-musl gcc 16, fully static — cross from x86_64 (qemu) or native verified
x86_64-windows-gnu gcc 16 MinGW-w64 — native on Windows, cross from Linux (wine) (Windows default without Visual Studio) verified
x86_64-windows-msvc msvc@system (detected VS/BuildTools) or llvm ¹ (Windows default with Visual Studio) verified
x86_64-windows-musl llvm 22 — a PE with a musl C library, which no gcc emits; the system comes from the dependency graph preview
aarch64-macos llvm (macOS default) verified
riscv64-none-elf · riscv32-none-elf llvm 22 — bare metal, xim:picolibc-riscv ² verified
thumbv6m-none-eabi · thumbv7m-none-eabi llvm 22 — Cortex-M0/M0+/M1, Cortex-M3 ² verified
thumbv7em-none-eabihf · thumbv8m.main-none-eabi llvm 22 — Cortex-M4F/M7F hard float, Cortex-M33/M55 soft float ² verified
armv7a-none-eabi · armv7a-none-eabihf llvm 22 — Cortex-A 32-bit, the first row with an MMU ² verified
aarch64-none-elf · x86_64-none-elf llvm 22 — bare metal, no C library by default ² preview
thumbv7em-none-eabi · thumbv8m.base-none-eabi · thumbv8m.main-none-eabihf llvm 22 — Cortex-M4/M7 soft float, M23, M33F/M55F ² preview
riscv64-linux-musl · aarch64-linux-gnu · x86_64-macos planned

verified an image has been built and run for the row, qemu and wine included · preview it builds and links, and no emulator run has been recorded · planned registered in the vocabulary and nothing wired yet — a build for such a target is refused rather than attempted.

Linux release binaries are fully static musl builds for x86_64 and aarch64 (x86_64-linux-musl and aarch64-linux-musl). Legacy spellings — x86_64-w64-mingw32, gcc@16.1.0-musl, mingw-cross@…, musl-gcc@… — stay permanently accepted as aliases and normalize to the canonical forms above.

¹ On Windows, llvm targets the MSVC ABI and therefore requires an existing MSVC BuildTools or Visual Studio (UCRT, Windows SDK, MSVC STL). You do not have to arrange this: on first run mcpp checks for a usable MSVC and, finding none, defaults to x86_64-windows-gnu (winlibs MinGW-w64) — fully self-contained, no Visual Studio, import std included. Nothing to install or configure; mcpp new && mcpp build just works on a stock Windows box. An explicit [toolchain] in mcpp.toml is always respected as written — mcpp revises its own default, never yours.

² The bare-metal rows carry no operating system: clang and lld are cross-compilers by construction, so any host that can install the LLVM payload produces these targets. The C library, startup code, memory layout and emulator travel with a board-support package rather than with mcpp — see 40 — Bare-Metal and Freestanding Targets.

Documentation

docs/ is the manual. A chapter's first digit says which part it belongs to, and the index carries the reverse lookup — from a manifest key or a command in front of a reader, to the chapter that owns it.

Part Start at
0x fundamentals 01 Getting Started · 04 The mcpp.toml Manifest · 09 Commands by Scenario
1x publishing 10 Packaging an Application for Release · 11 Publishing a Library to mcpp-index · 12 Distributing a Prebuilt Library
2x toolchains and targets 20 Toolchain Management · 21 The Target Triple · 24 Cross-Compilation Over openkal
3x extending mcpp 30 Build Programs: build.mcpp · 31 Authoring a Rule Package · 34 Authoring a Board-Support Package
4x devices and accelerators 40 Bare-Metal and Freestanding Targets · 41 Reaching a Device · 42 Heterogeneous Builds
5x contracts for programs 50 Machine-Readable Output · 51 Supported Versions and Compatibility · the specifications
9x mcpp itself 90 Building from Source and Contributing · 92 Releasing mcpp

Every directory under examples/ is a project that builds, and 03 — Examples says which one teaches what. Full options for any command are available via mcpp <cmd> --help.

AI-assisted learning: send the following prompt to an AI coding assistant to get up to speed with mcpp quickly:

Read .agents/skills/mcpp-usage/SKILL.md and the docs/ directory of the
https://github.com/mcpp-community/mcpp repository,
then tell me how to create a C++23 module project with dependencies using mcpp.

Who's Using mcpp

Real projects built with mcpp — import-able C++23 modules and the toolchain it builds on:

Project Description
mcpp mcpp itself — written in C++23 modules, fully self-hosted
xlings Toolchain and package-management foundation mcpp builds on
tinyhttps Minimal C++23 HTTP/HTTPS client with SSE streaming
llmapi Modern C++ LLM API client (OpenAI-compatible)
imgui-m Dear ImGui as a C++23 module package
cmdline Command-line parsing library / framework (mcpp uses it)

More modular libraries → mcpplibs · package index → mcpp-index

Contributing

Contributions via issues and PRs are welcome. The project accepts contributions developed with AI agents.

Basic workflow

  1. Open an issue — for bug fixes, new features, or improvements, start a discussion in issues first
  2. Implement the change — fork the repo, create a branch, and verify according to scope (mcpp build plus relevant tests for behavior changes; examples and links for documentation-only changes)
  3. Submit a PR — use gh pr create and make sure CI passes
  4. CI must pass — PRs with failing CI will not be merged

Commit message convention: feat: / fix: / test: / docs: / refactor: prefixes

AI agent contributions: the repo's .agents/skills/mcpp-contributing/SKILL.md provides a complete agent contribution workflow and project structure guide. Just send this prompt to your AI assistant:

Read .agents/skills/mcpp-contributing/SKILL.md of the
https://github.com/mcpp-community/mcpp repository,
then follow the guide to help me submit a contribution to mcpp.

Community & Ecosystem

Acknowledgements

Dependencies and sources of inspiration:

  • xlings — toolchain / package-management foundation
  • mcpplibs.cmdline — CLI framework
  • ninja — underlying build engine
  • xmake — cross-platform build tool
  • cargo — Rust package manager

About

A modern C++ module-first build tool — written in pure C++23 modules, fully self-hosted

Topics

Resources

Stars

130 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages