Build system (CMake)¶
How miint's CMake is laid out, and what to edit when you add a source, a test or a dependency. Why each embedded library is built the way it is lives in embedded-tools.md.
Layout¶
| File | Holds |
|---|---|
CMakeLists.txt |
The MIINT_ENABLE_* options, the platform matrix (the only place a platform turns a feature off), the includes below in order, the always-compiled sources, and the three targets. |
cmake/miint_helpers.cmake |
miint_git_version(), miint_configure_target(), miint_check_unique(), and the cross-compile flags (MIINT_OSX_ARCH_CFLAGS, MIINT_AUTOTOOLS_HOST, EMSCRIPTEN_THREAD_CFLAGS). |
cmake/system_deps.cmake |
Libraries found rather than built: ZLIB, Threads, expat, Eigen (and its two defines), libdeflate, zstd. |
cmake/openmp.cmake |
The miint_openmp target (UniFrac needs it; krepp uses it if present). |
cmake/isa_dispatch.cmake |
Per-instruction-set builds of a kernel (miint_add_isa_variants). |
cmake/deps/<name>.cmake |
One embedded dependency each: its build, and everything miint compiles or links because of it. The file name is the option's suffix: MIINT_ENABLE_KREPP → krepp.cmake. htslib, minimap2, wfa2 and rust_glue are always built. Exceptions: sylph and st3 are cargo features of rust_glue.cmake, skb_arrow.cmake is included from gpl_boundary.cmake (it shares that option and transport), and libdeflate is in system_deps.cmake. |
The registration lists¶
A deps file builds its library, then appends to these lists. include() shares the caller's scope, so plain list(APPEND ...) is all it takes.
| List | Holds | Applied to |
|---|---|---|
MIINT_EXTENSION_SOURCES |
src/*.cpp compiled only when the feature is on |
static + loadable extension |
MIINT_TEST_SOURCES |
DuckDB-free src/*.cpp and test/cpp/test_*.cpp |
tests |
MIINT_DEFINITIONS |
MIINT_HAS_<NAME>; <NAME>_GIT_VERSION via miint_git_version() |
all three |
MIINT_INCLUDE_DIRS |
header directories | every target in the directory |
MIINT_LINK_LIBRARIES |
link items, in link order | all three |
MIINT_OBJECT_SOURCES |
$<TARGET_OBJECTS:...> |
all three |
MIINT_TARGET_DEPENDENCIES |
targets that must build first (e.g. ones linked by path) | all three |
MIINT_WASM_LIBS |
archive paths for the wasm side-module link | loadable, under Emscripten |
miint_configure_target() applies them, so the static extension, the loadable extension and tests cannot drift apart. What only one target needs sits next to its call in CMakeLists.txt.
Recipes¶
Add a source that is always compiled. Add it to MIINT_EXTENSION_SOURCES in CMakeLists.txt, keeping the list sorted. If it is DuckDB-free and has a C++ test, add it and the test to MIINT_TEST_SOURCES too.
Add a source that belongs to an optional feature. Append it in that feature's deps file, never in the core list: there it would compile with the feature off. miint_check_unique() stops configure if a source is in both.
Add a C++ unit test. Append test/cpp/test_<Name>.cpp, plus the src/ files it exercises, to MIINT_TEST_SOURCES: in CMakeLists.txt, or in the feature's deps file if it needs that feature. A test that needs a running DuckDB goes in the tests_duckdb executable at the end of CMakeLists.txt instead; one that also needs an optional feature is added there with target_sources under that feature's switch.
Add an embedded dependency.
1. Write cmake/deps/<name>.cmake: build or import the library, then append to the lists above. Call miint_git_version(<NAME> ext/<dir>) if it is a submodule.
2. Add option(MIINT_ENABLE_<NAME> ...) and, if some platform cannot build it, a line in the platform matrix.
3. Add <NAME> to the feature foreach in CMakeLists.txt, at its link-order position. A dependency that is always built goes in foreach(dep htslib minimap2 wfa2 rust_glue) instead, with no option or matrix line.
4. Report the version and document the library: embedded-tools.md.
Rules¶
- Include order is link order. The linker resolves an archive's undefined symbols only from libraries after it, so a library goes after every library that uses it. zlib, expat, the compression codecs, Threads and the platform libraries are appended last, in
CMakeLists.txt. - Link by path what is outside DuckDB's export set. The static extension is
install(EXPORT)ed with DuckDB, which rejects link dependencies on targets outside that set. Link such a library as$<TARGET_FILE:x>and addxtoMIINT_TARGET_DEPENDENCIES(seeabpoa.cmake,krepp.cmake); feed OBJECT libraries throughMIINT_OBJECT_SOURCES. - Platform gates live only in the matrix. A deps file may still turn its own feature off when a package is missing (
set(MIINT_ENABLE_<NAME> OFF)thenreturn()), as the tidy-check builds need. - Headers whose names collide get a narrower scope.
MIINT_INCLUDE_DIRSreaches every target in the directory, and its order decides which of two same-named headers an include finds when the header is not beside the including file. When a library ships headers named like another's, scope them to the files that need them withset_source_files_properties(seekrepp.cmake). testssources must be DuckDB-free.testshas no libduckdb and is built withoutMIINT_STATIC_BUILD, so a source in both lists is compiled twice with different#ifdefs. That is why the lists cannot share objects.- Format
cmake/yourself.make format-checkcovers the rootCMakeLists.txtbut nothing undercmake/; runcmake-format -ion what you change there, with cmake-format 0.6.13 (the version CI pins). CI'scmake-formatjob checks all ofcmake/.