Maud¶
A low configuration convention for C++ projects.
Maud arose from the observed high overhead in setting up new C++
projects. Leaving documentation unwritten or in comments or in .md
files, copy-pasting scripts from older projects, unit testing with
bare assert(), … These are all things I’ve done because I wanted
to start work on one part of a project and all the other parts were
not already there.
The more conventional solution to this problem is to provide a starter project. There are a number of good ones out there, but starter projects have a drawback: they are tantamount to a dependency which you fork into your own repo. This makes it far harder to tell where the copypasta came from and it means you probably can’t just apply upstream fixes to known issues.
To me it seems better to provide a minimal, uniform project structure which
is mostly defined by things that aren’t there; an empty directory is
a valid Maud project. If you want to start by writing a unit test,
you can do so by writing its C++ source and nothing else.
If you want to start by writing documentation, you can do so by writing its
rst source and nothing else. Without consulting any
script or manifest, it’s possible to read a C++ source and know what it’s
compiled and linked to and how the build artifact will be installed.
Explicit configuration is always available when necessary, but I think it’s beautiful to (mostly) sidestep it.
Features¶
- Turbocharged CMake
Maudis built on CMake, but works hard to eliminate boilerplate. For simple projects, no hand writtencmakeis required. Whenever explicit configuration becomes necessary, minimal and focusedcmakecan be written wherever makes the most sense for your project.- Globbing
Maudextends CMake’s built in globbing support with more expressive patterns, support for exclusion as well as inclusion, and greater performance.To briefly summarize, Built-in globs are used to find:
cmake_modulesdirectories, which are added to the module path.cmakemodules, which are automatically included.in2template files, which are renderedincludedirectories, which are added to the include pathC++ source files, which are scanned for modules and automatically attached to build targets
.rstfiles, which are rendered to HTML
- Documentation
Rendering really usable documentation is a priority for
Maud. All we really need for that is a Python3 interpreter, which is used to run Sphinx and render all.rstfiles to lovely HTML (or other formats).- Inferrence of targets from C++ modules
The executables, libraries, and tests defined by a project are inferred from automatic scans of C++ sources. Hello world with
Maudis a single source file:#include <iostream> import executable; int main() { std::cout << "hello world!" << std::endl; }
Targets are also automatically linked to anything they import and are set up for installation. For library targets, installation includes making them automatically discoverable by other
Maudprojects which import them.- More sophisticated options
Maud backwards-compatibly overloads cmake’s built-in option function to provide support for more sophisticated configuration options:
uniform declaration for all types of option
resolution of interdependent option values
easy access to options in C++ as predefined macros
clean summarization of all options, complete with multiline help strings
serialization to preset JSON for repeatability
option( FOO_LEVEL ENUM LOW MED HI " What level of FOO API should be requested. LOW is primarily used for testing and is not otherwise recommended. " DEFAULT MED REQUIRES IF HI # LOW or MED levels can be emulated but HI requires a physical FOO endpoint. FOO_EMULATED OFF ADD_COMPILE_DEFINITIONS )
- First class support for generated files
A common source of cmake boilerplate is wiring up rendering of template files, running schema compilers, and otherwise generating code.
Maudprovides a single build subdirectory for these files to land in and natively supports including them in any file set: all globs will include matching files in${MAUD_DIR}/renderedas well as those in${CMAKE_SOURCE_DIR}(unless explicitly excluded).Additionally, projects using
Maudcan use a built-in template format inspired byconfigure_file()to smoothly render configuration information into generated code. If the template file${CMAKE_SOURCE_DIR}/dir/foo.cxx.in2exists, it will automatically be rendered to${MAUD_DIR}/rendered/dir/foo.cxxand included in compilation alongside non-generated C++:#define FOO_ENABLED @FOO_ENABLED | if_else(1 0)@ // renders to #define FOO_ENABLED 1