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
#define
s 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./fips
it will install itself. - A
fips.yml
which describes the dependencies and exports for this project. - Modifications to the
CMakelists.txt
files 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 fetch
downloads the dependencies../fips update
updates the dependencies. It will also tell you if any of the dependencies have uncommitted changes (this is helpful)./fips gen
is the same as runningcmake
… it make the Makefiles./fips build
builds the software. It’s a combination offetch
,gen
andmake
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.