A minimal mcpp C++23 module library template. English · 简体中文 · 繁體中文
The repository expresses four things and nothing more:
.xlings.jsondeclares the project tool environment (which mcpp version builds this).mcpp.tomldeclares the C++23 module library and its test dependencies; runtime dependencies are added as needed.templates/declares the project templates the library ships to its users.mcpp build,mcpp test,mcpp runverify the library, the unit tests and the example.
- Sources are globbed from
src/**/*.{cppm,cpp,cc,c,S,s,asm}by default. - With
src/*.cppmand nosrc/main.cpp, mcpp infers a lib target named after the package (Inferred target mylib (lib from .cppm in src/)). Write[targets.<name>]explicitly only when you need to override that. - The lib-root module defaults to
src/<last segment of the package name>.cppm;[lib] pathoverrides the location. - Dependencies go in
[dependencies]or[dependencies.<namespace>]; test-only ones go in[dev-dependencies](mcpp buildignores those — onlymcpp testresolves them). - Unit tests live in
tests/**/*.cppand are discovered bymcpp test, one binary per file. Test files containTEST(...)cases only — never their ownmain(). - The default build profile is
dev(-O0 -g);mcpp build --releaseproduces the optimized build. The C++ standard defaults toc++23and belongs in[package] standard, never incxxflags. mcpp.lockis not committed here: it is a library, so the versions that matter are the consumer's. Applications generated fromtemplates/may want to commit theirs.
.
├── .xlings.json
├── mcpp.toml
├── src/mylib.cppm
├── tests/mylib_test.cpp
├── examples/basic/
│ ├── mcpp.toml
│ └── src/main.cpp
├── templates/
│ ├── basic/ # default template: console app
│ │ ├── template.toml
│ │ ├── mcpp.toml.in
│ │ ├── src/main.cpp.in
│ │ └── .gitignore
│ └── lib/ # downstream module library + gtest
│ ├── template.toml
│ ├── mcpp.toml.in
│ ├── src/lib.cppm.in
│ └── tests/lib_test.cpp.in
├── tools/template_smoke.sh
└── .github/workflows/{ci-linux,ci-macos,ci-windows}.yml
[package]
namespace = "mcpplibs"
name = "mylib"
version = "0.1.0"
[dev-dependencies]
gtest = "1.15.2"Every package has a two-part identity: a namespace and a name. namespace = "mcpplibs"
plus name = "mylib" means consumers write mylib = "0.1.0" under [dependencies.mcpplibs]
(or as a bare name — mcpplibs is the default namespace mcpp searches first).
src/mylib.cppm exports the module mcpplibs.mylib. It re-exports nothing: a dependency is
imported where it is used, not forwarded through the root module, unless the public API
genuinely exposes that dependency's types.
Declare the dependency in the root mcpp.toml:
[dependencies.mcpplibs]
cmdline = "0.0.2"and import it where you need it:
import mcpplibs.cmdline;export import is not the default. Only re-export when your public API hands out types that
belong to the dependency.
examples/basic is a standalone mcpp package that consumes the root library through a path
dependency:
[dependencies.mcpplibs]
mylib = { path = "../.." }Run it:
cd examples/basic
mcpp runIt exists to prove the library is usable from the outside — with its own manifest and its own
resolution — which importing from tests/ cannot show.
templates/<name>/ is how a library ships project scaffolds to its users:
mcpp new myapp --template mylib renders one into a new project, and the template version
tracks the library version automatically.
template.toml—description, at most onedefault = trueacross all templates, and an optionalpost_messageprinted after generation. It is metadata; it is never copied.*.in— rendered, then the suffix is stripped. Placeholders:{{project.name}},{{self.name}},{{self.version}}.- Everything else — copied verbatim.
Templates are pure data: mcpp renders and copies, and runs no hooks or scripts. File names
are not rendered, only file contents — that is why the lib template ships src/lib.cppm and
points [lib] path at it instead of relying on the src/<package name>.cppm convention.
If a rendered mcpp.toml does not declare a dependency on the shipping library, mcpp injects
one; the templates here declare it explicitly via {{self.version}}, so nothing is injected.
tools/template_smoke.sh renders every template the way mcpp new does, repoints the
dependency at this checkout (so templates are verified before a release exists in the index),
then builds it — and runs it when the template produced a binary.
Three workflows, one per platform (.github/workflows/ci-{linux,macos,windows}.yml), each
running the same path a new user walks:
xlings install -y # project-env mcpp, at the .xlings.json pin
mcpp build
mcpp test
cd examples/basic && mcpp run
bash tools/template_smoke.shSplitting per platform keeps one badge and one log per OS, so a macOS or Windows regression cannot hide behind a green Linux run.