System-installed libraries don’t make sense in a microcontroller project, which naturally wants all code to be in the project tree. Vendoring1 creates the tree by hand, offering cumbersome ways to specify the version of a dependency and to update the tree when it changes. This habit may struggle to keep a pace of development that supports software over a long lifecycle.
Git submodules simplify specifying a version and updating the tree automatically. However, their reputation for fragility has been earned fairly.
Meson wraps provide a workable alternative to Git submodules. While both support specifying a commit for a dependency, an important step toward a reproducible build2, a wrap can also specify the version of a dependency whose origin isn’t a Git repository.
A wrap file subprojects/freertos.wrap
specifies a dependency:
[wrap-git]
url = https://github.com/FreeRTOS/FreeRTOS-Kernel
revision = V10.5.1
depth = 1
This compares favorably with adding a Git submodule:
git submodule add https://github.com/FreeRTOS/FreeRTOS-Kernel freertos
cd freertos
git checkout V10.5.1
cd ..
git commit -m 'Add FreeRTOS V10.5.1'
which integrates the source tree but not the build system.
A wrap reduces maintenance burden through declarative syntax and ephemeral state. The dependency is checked out not upon clone, but when you tell Meson to set up a new build directory. Having the build system own the dependency frees the version control system to focus on its own content.
Wraps interoperate with other projects using Meson or CMake. A
microcontroller project integrates FreeRTOS by specifying the path of
FreeRTOSConfig.h
and the name of the subproject as indicated by the
wrap file. There are a few CMake options to set.
cmake = import('cmake')
freertos_options = cmake.subproject_options()
freertos_options.append_compile_args(
'c',
'-I' + meson.global_source_root() / 'include',
)
freertos_options.add_cmake_defines({
'CMAKE_TRY_COMPILE_TARGET_TYPE': 'STATIC_LIBRARY',
'FREERTOS_CONFIG_FILE_DIRECTORY': 'include',
'FREERTOS_PORT': 'GCC_ARM_CM4F',
})
executable(
'firmware',
'main.c',
dependencies: cmake.subproject(
'freertos',
options: freertos_options,
).dependency('freertos_kernel'),
)
Meson’s easy syntax and immutable data structures enable simpler engineering of build systems. Wraps help projects taking advantage of C to move through the lifecycle gracefully.
A caveat of wraps is the lack of support for legacy build systems. But porting a build system to Meson can be its own reward.
I enjoyed Tom MacWright’s comparison of a range of well-known dependency strategies. ↩︎
A reproducible build satisfies the criteria of a pure mathematical function: if the inputs haven’t changed, then the output must be the same. Automation aids reproducibility and quick incorporation of newcomers to the project. ↩︎
Discuss this page by emailing my public inbox. Please note the etiquette guidelines.
© 2024 Karl Schultheisz