This is BootsOnTheGround [BOTG]! 
CMake macros for easy projects with TPLS C/C++/Fortran
BOTG provides macros for easy project/package setup and a set of FindTPL*.cmake files to find and link Third Party Libraries (TPLs) to other packages using the CMake / TriBITS framework for C/C++/Fortran code.
#Inside MyPackage/cmake/Dependencies.cmake
botgPackageDependencies(
LIB_REQUIRED_PACKAGES
TheirPackage
BootsOnTheGround_CURL
TEST_REQUIRED_PACKAGES
BootsOnTheGround_GTEST
)
In order to use this command, you need to add BootsOnTheGround as a package
to your PackagesList.cmake
file in your project. The amazing thing about
the BootsOnTheGround_XXX
dependencies are that they unify the normal way
of finding a TPL on your system with the Hunter system for downloading and
building TPLS on the fly!
The available XXX
allowed are listed below.
Currently we have the following TPLs wrapped up nice and purdy:
- BOOST_FILESYSTEM - Cross-platform file system queries [C++]
- BOOST_MATH - statistical distributions and special functions [C++]
- CURL - push and pull across the web [C++]
- FMT - amazing sprintf, printf replacement [C++]
- GFLAGS - command line flags parsing [C++]
- GTEST - Google's unit testing system [C++]
- HDF5 - hierarchical binary data containers [C++]
- NLJSON - NLohmann's JSON as a first-class citizen [C++]
- OPENSSL - hash for security [C++]
- SZIP - scientific zip algorithm [C++]
- SPDLOG - fast, versatile logging [C++]
- ZLIB - compession/decompression algorithm [C++]
- BLAS - basic linear algebra subprograms [Fortran]
- CBLAS - C bindings for BLAS [C/C++]
- LAPACK - linear algebra package [Fortran]
Once the interface of BOTG crystalizes, the only changes will be adding new TPLS, and adding versioning support for finding those TPLS.
BOTG provides a small set of macros for building projects from a pool of packages,
eventually with support for package versioning. These macros are
all contained in the BOTG.cmake
file which can be bootstrapped into any
project build.
botgProject()
- declare a project (inside root CMakeLists.txt)botgPackage( name )
- declare a package (inside subdir CMakeLists.txt)botgSuperPackage( name )
- declare a super package (i.e. package with subpackages)botgEnd()
- wrap-up processing a Project or PackagebotgAddCompilerFlags( lang compiler os flags )
- add compiler flags only for a particular language/compiler/os combinationbotgAddLinkerFlags( compiler os flags )
- add linker flags only for a particular compiler/os combinationbotgLibrary( name ... )
- declare and define a library using current compiler and linker flagsbotgTestDir( dir )
- declare a unit test directorybotgPackagesList( ... )
- declare the packages and subdirs in a project (insidePackagesList.cmake
)botgSuperPackageContents( ... )
- declare the packages and subdirs in a super package (insidecmake/Dependencies.cmake
for a packagebotgTPLsList( ... )
- declare the TPLs andfindTPL*.cmake
locations (insideTPLsList.cmake
)botgPackageDependencies( ... )
- declare the dependencies of a packagebotgDownloadExternalProjects( ... )
- download an external project at configure time (used to bootstrap BootsOnTheGround)
Take a look at Testing123 for an example of how to use BOTG.
Bootstrapping is the recommended way of using BOTG (hence the name!). You need to do four things to enable BOTG in your TriBITS C/C++/Fortran project.
- Copy
cmake/BOTG_INCLUDE.cmake
containing bootstrap commands to your project's rootcmake
directory. - Copy
external/BootsOnTheGround.in
containing repo link commands to your project'sexternal
directory. INCLUDE(cmake/BOTG_INCLUDE.cmake)
first thing in your rootCMakeLists.txt
file to execute the bootstrap.- Add BOTG to your TriBITS
PackagesList.cmake
file.
botgProjectContents(
BootsOnTheGround external/BootsOnTheGround/src ST
...
)
Note, if you don't want to bootstrap BOTG to the directory external
, then
you're going to have to change the line in BOTG_INCLUDE.cmake
that
references external/BootsOnTheGround.in
. See Testing123 for an example
of bootstrapping BOTG.
Then in your Dependencies.cmake
file for any package you can use the
botgPackageDependencies()
macro.
botgPackageDependencies(
LIB_REQUIRED_PACKAGES
BootsOnTheGround_SPDLOG
TEST_REQUIRED_PACKAGES
BootsOnTheGround_GTEST
)
Note that we are now linking to packages instead of TPLS through BOTG.
Behind the scenes, the botgPackageDependencies
macro adds the relevant actual TPL
links and calls TRIBITS_PACKAGE_DEFINE_DEPENDENCIES
.
Every software package needs to answer the question of why does it exist. This package could be seen as another layer on top of an already precarious cake (CMake bottom layer, TriBITS middle layer). And there is a really good reason not to create another CMake macro system, namely maintainability. CMake is a popular solution to a persistent problem (cross-platform C++ builds), which means there are many people out there who pick up CMake as a skill. But how many people know your macros? So you limit who can help with what we believe is the worst part of software development: configuration.
But we did it anyway!? We did it because we are targeting people without any CMake skill. These are generally scientists and engineers who:
- do not have a dedicated build guy,
- do not have time or want CMake as a skill,
- use or depend on a mix of C++ and Fortran,
- are using TriBITS anyway, and/or
- who hate writing configuration code.
For these people, the goal are simple.
Create and deploy software that solves a new scientific problem--NOT a software engineering one. So our (yes, we are those guys) requirements are something like:
- easily use existing TPLs with versioning,
- easily use each other's packages with versioning, and
- easily manage combinations of Fortran, C, and C++ code.
Yes easy is the key word. The versioning part is also important because we need reproducability. Once we are combining these various packages in new and interesting ways, knowing exactly what we have at any given time is really important.
So we've mentioned TriBITS and there is a section describing the role of TriBITS. But TriBITS does not really handle versioning of TPLS and packages, which we need. It also does not intend to provide a set of standard FindTPL*.cmake files, which we think needs to exist. (That's where this project started. :)) Finally, TriBITS is still a little tricky to use, and results in a decent amount of boilerplate and a mix of TriBITS and CMake where it's a little difficult to see exactly what's going on. The BOTG interface to define the software package is very simple. We don't really see it changing. As TriBITS and CMake evolve, the best practices that are used under the hood for defining the libraries and executables may change, but the interface is straightforward:
- Define a project as a collection of external and internal packages.
- Define for each internal package:
- dependency on external packages and TPLs;
- headers, libraries, and executables to deploy;
- unit tests; and the minimal
- compiler/linker flags or C++ standard needed to build.
TriBITS does all the heavy lifting of package dependency management, however, it has some limitations in dealing with TPLs. One TPL cannot be dependent on another TPL, and TPLs cannot have versions. The idea is that we wrap each TPL in a TriBITS package, which does provide this capability.
Say you needed TPL CURL
for your library and GTEST
for testing.
CURL
requires OPENSSL
and ZLIB
. In every TriBITS
cmake/Dependencies.cmake file, you would need to specify:
With BOTG, you can use instead a package dependencies. This will give us much more fine grain control over meeting requirements like specific versions.
botgPackageDependencies(
LIB_REQUIRED_PACKAGES
BootsOnTheGround_CURL
TEST_REQUIRED_PACKAGES
BootsOnTheGround_GTEST
)
Note, the other magic gained by using BOTG is that Hunter is used to download, build, and install any TPLs it cannot find!
BOTG should find local libraries on your machine that meet the version requirements. However, when it does not, BOTG uses Hunter, a CMake-based package manager. We looked at using `spack<https://spack.io/>`_ but it is not clear if they will ever have Windows support.
- If your project has much more than
100 + number of source files
lines of CMake, you're doing it wrong. - Every project should build and pass all tests with a simple
mkdir build && cd build && cmake .. && make && ctest
on - Windows, Mac, and Linux operating systems with - reasonably recent Intel, GNU, and Clang compilers. It may not be an optimal build, but it should work. - Use semantic versioning for your packages.
This repository uses Gitflow, i.e.
- Development is feature-based, always on
feature/X
branches ofdevelop
. Thedevelop
branch can be unstable. - The
master
branch is only updated fromdevelop
when all tests pass. Themaster
branch is always stable. - Releases are first created as a release branch,
release/vMAJOR.MINOR
, then when ready are merged into themaster
branch and taggedvMAJOR.MINOR.0
. - Hotfixes are created as a branch off
master
:hotfix/vMAJOR.MINOR.PATCH
, when finished are merged intomaster
and taggedvMAJOR.MINOR.PATCH
, then merged intodevelop
.
To enable the Travis CI to be able to use curl and https (for Hunter), I followed the steps on Cees-Jan Kiewiet's Blog Post.