A workspace lets you organize and manage multiple related mcpp packages (libraries or applications) within a single repository. Member packages share a unified set of dependency versions and toolchain settings while each keeping its own mcpp.toml project file.
Workspaces address the following problems:
- Unified dependency-version management — multiple sub-packages use the same versions of third-party dependencies, avoiding duplicate declarations and version drift.
- Shared toolchain configuration — declare the toolchain once at the workspace root; members inherit it or override it as needed.
- Multi-package co-development — libraries and applications are developed in the same repository and reference one another through
pathdependencies.
A workspace does not change how dependencies are declared. Members reference one another through the existing path = "..." mechanism, exactly as in a non-workspace project.
Declare [workspace] in the mcpp.toml at the repository root:
[workspace]
members = [
"libs/core",
"libs/http",
"apps/server",
]members lists the relative path of each member package; every such path must contain its own mcpp.toml.
The optional exclude field excludes specific paths:
[workspace]
members = ["libs/*"]
exclude = ["libs/experimental"]Virtual workspace: the root mcpp.toml contains only [workspace] and no [package]. The root produces no build artifacts and serves purely as a management node.
# Virtual workspace — [workspace] only
[workspace]
members = ["libs/core", "apps/server"]Root-package workspace: the root mcpp.toml contains both [package] and [workspace]. The root itself is also a buildable package.
[workspace]
members = ["libs/core"]
[package]
name = "myapp"
version = "0.1.0"
[dependencies]
core = { path = "libs/core" }Each member maintains its own mcpp.toml, structured just like a regular project:
# libs/core/mcpp.toml
[package]
namespace = "myproject"
name = "core"
version = "0.1.0"
[targets.core]
kind = "lib"Members reference one another through path dependencies:
# libs/http/mcpp.toml
[package]
namespace = "myproject"
name = "http"
version = "0.1.0"
[dependencies]
core = { path = "../core" }
[dependencies.compat]
mbedtls.workspace = trueDeclare dependency versions centrally under [workspace.dependencies]; members inherit them with .workspace = true:
# root mcpp.toml
[workspace.dependencies]
cmdline = "0.0.2"
capi.lua = "0.0.3" # dotted selector: mcpplibs.capi/lua, then capi/lua
[workspace.dependencies.compat]
mbedtls = "3.6.1"
gtest = "1.15.2"# member mcpp.toml
[dependencies.compat]
mbedtls.workspace = true # inherits version → "3.6.1"
[dev-dependencies.compat]
gtest.workspace = true # inherits version → "1.15.2"A member can override an inherited version:
[dependencies.compat]
mbedtls = "4.0.0" # override; does not use the workspace versionThe workspace root's [toolchain] and [target.<triple>] settings are automatically inherited by all members. A member can override them in its own project file.
Configuration precedence (highest to lowest):
- Command-line arguments (
--target,--static) - Declarations in the member
mcpp.toml - Declarations in the workspace-root
mcpp.toml - Global configuration (
~/.mcpp/config.toml) - Built-in defaults
# workspace root
[toolchain]
default = "gcc@16.1.0"
[target.x86_64-linux-musl]
toolchain = "gcc@15.1.0-musl"
linkage = "static"# a member overrides the toolchain
[toolchain]
default = "clang@19.0"mcpp build # build the default target (auto-selects the member with a binary target)
mcpp build -p server # build a specific member and its dependencies
mcpp build -p core # build a specific library membercd libs/http
mcpp build # auto-detects the workspace and builds the current membermcpp searches upward from the current directory; if it finds an mcpp.toml containing [workspace] and the current directory is listed in members, it automatically enters workspace mode and inherits the workspace configuration.
-p works with build, test, run, and other commands to select the target member. Its value is either the last path segment of a member's directory name or the full relative path:
mcpp build -p server # matches apps/server
mcpp test -p core # matches libs/core
mcpp run -p server -- --port 8080The recommended directory layout for a workspace:
myproject/
├── mcpp.toml # [workspace] declaration
├── libs/
│ ├── core/
│ │ ├── mcpp.toml # [package] namespace="myproject" name="core"
│ │ └── src/
│ │ └── core.cppm # export module myproject.core;
│ └── http/
│ ├── mcpp.toml
│ └── src/
│ └── http.cppm # export module myproject.http;
└── apps/
└── server/
├── mcpp.toml
└── src/
└── main.cpp # import myproject.http;
Each member's build artifacts live under its own target/ subdirectory.
Workspaces work in concert with the C++23 module mechanism:
- Interface visibility is controlled by the language —
export moduleandimportstatements determine a module's public interface; the workspace imposes no additional visibility restrictions. - Module names are chosen by the library author — the workspace does not require module names to match the package name or namespace.
- Partitions are for internal organization — a partition imported via
import :internal;(withoutexport) is invisible to consumers, with no build-tool involvement required.
See examples/04-workspace/ for a complete, runnable example of a three-member workspace.