Repo Organization (for C++) and fips
Consider this my “current code standards” for C++ projects:
- Use the repo directory structure and tools listed below.
- Put public code on Github.
- Organize your code “like Python packages”: many atomic, layered packages which are easy to combine; rather than large packages which need a lot of CMake options to configure.
- Git branches for features and for driving a repo to a well-defined endpoint
(like to finish a project), but use repos for configuration. For example, if you have sensors A, B, and C, create a package X which talks to A+B, and package Y which talks to B+C. Don’t create a single package where you can enable or disable A or C using
#defines or CMake options. If there’s a lot of repeated code, it should be pushed upstream into a package shared between X and Y.
fips
This layering approach is enabled by fips (@ github). fips is a Python tool which handles dependency management for programs written in CMake. It has some idiosyncrasies, but it works well for my workflow.
The only real downside is that a fips-ified project is not backwards compatible with standard CMake. The changes are relatively minor (fips uses a few custom CMake commands), but it does present a maintenance headache. I’ve decided to sacrifice share-ability to gain the benefits from fips. In some cases this means I maintain a fips-ified version of other open source project (CLI11, g3log). Forking the upstream project seems to be more effective than writing a wrapper.
Converting a CMake project to fips requires three things:
- A copy of the
fips(example) script. This is just a thin wrapper around the fips python module; as a side benefit, it’s also self-installing. The first time you call./fipsit will install itself. - A
fips.ymlwhich describes the dependencies and exports for this project. - Modifications to the
CMakelists.txtfiles in a project to use the macros provided by fips.
The first idiosyncracy is that fips assumes a directory structure of:
┣━━ fips/
┣━━ fips-build/
┣━━ fips-deploy/
┣━━ project-1/
┣━━ project-2/
┣━━ .../
The first is the installation of fips itself, and the next two are fips working directories. The project-* directories are your code and its dependencies. If you’re starting afresh, clone the first repository in an empty directory! An upside of this is that all of the dependencies are “first class” checkouts, which is good if you’re likely to be working on both the dependencies and the “final repo” at the same time (a lot like Go in this respect).
fips has a number of subcommands, here are the ones I use:
./fips fetchdownloads the dependencies../fips updateupdates the dependencies. It will also tell you if any of the dependencies have uncommitted changes (this is helpful)./fips genis the same as runningcmake… it make the Makefiles./fips buildbuilds the software. It’s a combination offetch,genandmake
In general fips build is all you need. If you get “no Makefile”, the initial CMake generation failed and you need to run fips gen manually.
Fips configuration
Fips has a couple of dials which can be tuned. fips list settings will show the current settings:
=== settings:
config: osx-make-unittest
target: None (default value)
jobs: 6 (default value)
ccache: off
iosteam: None (default value)
config defines the current build. In general, it auto-detects correctly, although I typically set the build to linux-make-unittest or osx-make-unittest rather than *-build, as this causes the unit tests to be run automatically after every build (see below) — if FIPS_UNITTESTS_RUN_AFTER_BUILD is defined in fips.yml.
jobs defines the make parallelism (same is make -j{number})
ccache defines whether ccache is used. It helps!
These values can be set with (for example):
./fips set jobs 1
./fips set config linux-make-unittest
Directory structure
My preferred directory structure looks like:
┣━━ include/libraryname/
┣━━ lib/
┣━━ tools/
┣━━ test/unit/
┣━━ .../
I add include/ to the library path so #includes are of the form “libraryname/header.h”
I’ve created a template C++ project here: apl-ocean-engineering/cpp-project-template.