# cmake-init: a C++ project template you copy instead of configure

> cmake-init is a MIT licensed CMake template for C and C++ projects that ships core, maintainer and development modules that each can be excluded. It assumes you will copy the tree and work through a checklist, and it deliberately refuses a few common CMake conveniences.

**cginternals/cmake-init** — Template for reliable, cross-platform C++ project setup using cmake.

- Repository: https://github.com/cginternals/cmake-init
- Stars: 942 · Forks: 115
- Language: CMake
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/cginternals-cmake-init

## A template meant to be copied, not installed

cmake-init does not solve a library problem. It solves the first week of a project, the part where every new repository reinvents the same build layout, the same test wiring, the same packaging layout, and usually the same CI configuration.

The described workflow is deliberately manual. The intended use is a copy of the current version of the template with a subsequent replacement of project names and customization of modules to your needs, documented in the adaption guide. A second approach exists, initialising a new CMake project and adopting features from cmake-init, but the project proposes the copy workflow.

That decision has consequences. There is no package manager entry to install and no version pinning mechanism of your own, because the version control system of the new repository becomes the version control of the template. The project assumes you have CMake 3.20 or above and a C or C++ compiler, and it targets desktop, laptop and server platforms, with Windows, macOS and GNU/Linux listed as supported. Other UNIX versions may work if CMake supports them.

The audience is a team that values a settled structure over a minimal one. If your project is a single translation unit, this is a lot of scaffolding for nothing.

## Three module tiers you can switch off

The template is organised in tiers, and the tier boundaries carry the design. Core modules cover CMake initialisation, backward compatibility, project meta information, meta information code generation and project build options. Maintainer modules sit above that, with the cmake-init Template Check module among them. Development modules cover version control system integration, build targets, documentation, tests, linter, continuous integration, deployment, packaging and run-time assets.

The property that makes this workable is exclusion. The project states that all modules are designed in a way that they can be excluded, and that the process of integration or removal of a module or feature is documented with each module. A new project should contain the core modules and then add the maintainer and development modules as required.

So this is not a batteries-included opinionated framework. It is a catalogue of practices with a documented removal path for each one. The risk sits on the other side of that trade: a copy made in haste can carry modules that are switched on but never configured, and those modules will still try to run in CI.

## Initialisation pins the version floor and three policies

The core CMake initialisation module does the ordinary setup work, but it does it in a way that is worth reading before you adopt the template, because these settings are inherited silently by every copy.

The minimum version is required and fatal if unmet:

```cmake
# CMake version
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
```

Three policies are then set explicitly, each with an inline comment naming the behaviour it changes:

```cmake
# Set policies
set_policy(CMP0054 NEW) # ENABLE CMP0054: Only interpret if() arguments as variables or keywords when unquoted.
set_policy(CMP0042 NEW) # ENABLE CMP0042: MACOSX_RPATH is enabled by default.
set_policy(CMP0063 NEW) # ENABLE CMP0063: Honor visibility properties for all target types.
```

The module path is adapted so that the bundled cmake directory is found, and two default modules are included that most projects need:

```cmake
# Include cmake modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
```

```cpp
include(GenerateExportHeader)
include(WriteCompilerDetectionHeader)
```

CMP0042 is the one to look at twice, since its inline comment states that MACOSX_RPATH is enabled by default. That is a global behavioural decision made once for every project that copies the template, and it is the kind of item you would want to reconsider for your own project rather than inherit by accident.

## Meta information is a set of variables, not a config file

Project-wide facts live in one block of set() calls in the main CMakeLists.txt, and everything downstream, documentation, testing and deployment, reads from there.

```cmake
#
# Project description and (meta) information
#

# Meta information about the project
set(META_PROJECT_NAME        "template")
set(META_PROJECT_DESCRIPTION "CMake Project Template")
set(META_AUTHOR_ORGANIZATION "CG Internals GmbH")
set(META_AUTHOR_DOMAIN       "https://github.com/cginternals/cmake-init/")
set(META_AUTHOR_MAINTAINER   "opensource@cginternals.com")
set(META_VERSION_MAJOR       "2")
set(META_VERSION_MINOR       "0")
set(META_VERSION_PATCH       "0")
set(META_VERSION_REVISION    "<REVISION>")
set(META_VERSION             "${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}")
set(META_NAME_VERSION        "${META_PROJECT_NAME} v${META_VERSION} (${META_VERSION_REVISION})")
set(META_CMAKE_INIT_SHA      "<CMAKE_INIT_REVISION>")
```

The version is assembled from major, minor and patch, and the project suggests semantic versioning for it. Two placeholders stand out: the revision and the cmake-init SHA. The revision is meant to be filled from your version control system, and the SHA records which upstream template the copy came from, which is what makes later updates traceable.

Finally the project name is turned into a derived identifier in two steps, once as a C identifier and once uppercased. A project called a library or a plugin can produce a name that needs care, and the derived identifier is what downstream code generation keys off.

## The Docker build shows the intended configure and build flow

The Dockerfile in the repository documents the real entry point of the template: a configure script that generates the build tree, followed by a standard CMake build and install.

```dockerfile
WORKDIR $PROJECT_DIR
RUN ./configure
RUN CMAKE_OPTIONS="-DOPTION_BUILD_TESTS=Off -DCMAKE_INSTALL_PREFIX=$WORKSPACE/$PROJECT_NAME-install" ./configure
RUN cmake --build build -- $COMPILER_FLAGS
RUN cmake --build build --target install
```

The pattern is worth reading closely. The first configure runs with defaults, the second passes options through the CMAKE_OPTIONS environment variable, and the build happens in a directory called build, which the configure script creates. The build step forwards a parallel job count through a compiler flags variable, and a separate target install step copies the result to an install prefix.

The container image is built in two stages. The build stage installs cmake, git and build-essential on top of an ubuntu:20.04 base, adds the project directories one by one, then runs configure, build and install. The final stage keeps only cmake and copies the install tree across, so the shipped image contains no compiler toolchain.

If you are adopting the template, this file is the best single piece of documentation for what the build is supposed to do, because it shows option names, directory names and the configure convention all in one place.

## What the template refuses to do

The project lists its non-goals explicitly, and they are sharper than most boilerplate disclaimers.

Super-build is out. Combining multiple cmake-init projects into one super-build project is not officially supported, because of the current semantics of targets and CMake internals, and the workarounds are described as limited and restricting. The stated solution is to treat each project separately and use explicit dependency management.

High abstraction is out too. The stated reason is that low abstractions are used to avoid building a language on top of CMake that a user has to learn. That is a defensible position, though it also means every consumer inherits a raw CMake experience.

File globbing is rejected as well, with the argument that explicit source specification prevents erroneous cases when adding and removing sources from the project tree. The cost of that choice is that every new file needs a line added to a CMake list, which some teams find tedious enough to route around with a script of their own.

Taken together, these are the three places where a team is most likely to disagree with the template. A real alternative to the whole approach is a smaller hand-written CMakeLists.txt for a single project, where you accept that you will write the test, packaging and CI glue yourself in exchange for a build system you fully understand from the first day.

## Template check and update cost on a copied tree

Once a project has moved on, the template moves too, and the problem is merging upstream changes into a tree that has been edited. The project answers this with the cmake-init Template Check module, and suggests using it for an overview of changes. The alternative it mentions is updating the required modules selectively, which suits teams that removed most of the template and now only track a few parts of it.

The metadata block cooperates here. The META_CMAKE_INIT_SHA placeholder records the upstream revision a copy was based on, and the meta information code generation module and version control system integration exist to keep that identifier meaningful as the project moves. Without such a marker, a diff between two templates becomes guesswork, and the selective update path loses its anchor.

On the repository itself, the last push was on 2026-08-04 and the repository has no GitHub releases, so there is no tagged sequence to upgrade between. Copying the tree means copying whatever master holds at that moment. The licence is MIT, which allows modification and redistribution as long as the copyright and licence notice is kept.

## Conclusion

Use cmake-init when you are starting a multi-platform C or C++ repository and want testing, packaging and CI wired up from day one without reinventing the layout, and skip it if you want a super-build, which the project lists as a non-goal. Before adopting, read ADAPT.md end to end and confirm that the CMake 3.20 floor matches every machine in your build fleet, then start from the core modules and add the rest one at a time.

## FAQ

### How do I start a new C++ project with cmake-init?

The documented approach is to copy the current version of the template and then replace project names and customise the modules, working through the task checklist in ADAPT.md. The project proposes this copy workflow over initialising a new CMake project and adopting features from cmake-init.

### What are the minimum requirements for cmake-init?

The template assumes CMake 3.20 or above and a C or C++ compiler, and it sets that floor with cmake_minimum_required(VERSION 3.20 FATAL_ERROR). Windows, macOS and GNU/Linux are listed as supported platforms, and other UNIX versions may work as long as CMake supports them.

### Can I use cmake-init for a project that bundles several libraries together?

No. The project lists super-build as a non-goal and states that combining multiple cmake-init projects into one super-build project is not officially supported, with the suggested solution being to treat each project separately and use explicit dependency management.

### How does cmake-init handle updates after I have copied it?

The project suggests using the cmake-init Template Check module for an overview of upstream changes, or updating the required modules selectively. The META_CMAKE_INIT_SHA value in the meta information block records which template revision a copy was based on, which gives the update process an anchor.

### What is the licence on cmake-init?

The project is released under the MIT licence, and a LICENSE file sits in the repository root. The template is meant to be copied into new projects, so the licence terms carry over to the copies you adapt.

## Sources

- [cginternals/cmake-init on GitHub](https://github.com/cginternals/cmake-init)
- [Issues](https://github.com/cginternals/cmake-init/issues)
- [License: MIT](https://github.com/cginternals/cmake-init/blob/master/LICENSE)
- [README](https://github.com/cginternals/cmake-init/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cginternals-cmake-init
