Skip to content
This repository has been archived by the owner on May 1, 2023. It is now read-only.
/ BootsOnTheGround Public archive

CMake/TriBITS third-party-library linker for C++/Fortran

License

Notifications You must be signed in to change notification settings

wawiesel/BootsOnTheGround

Repository files navigation

This is BootsOnTheGround [BOTG]! build

CMake macros for easy projects with TPLS C/C++/Fortran

image

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.

Current TPLs

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 Macros

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 Package
  • botgAddCompilerFlags( lang compiler os flags ) - add compiler flags only for a particular language/compiler/os combination
  • botgAddLinkerFlags( compiler os flags ) - add linker flags only for a particular compiler/os combination
  • botgLibrary( name ... ) - declare and define a library using current compiler and linker flags
  • botgTestDir( dir ) - declare a unit test directory
  • botgPackagesList( ... ) - declare the packages and subdirs in a project (inside PackagesList.cmake)
  • botgSuperPackageContents( ... ) - declare the packages and subdirs in a super package (inside cmake/Dependencies.cmake for a package
  • botgTPLsList( ... ) - declare the TPLs and findTPL*.cmake locations (inside TPLsList.cmake)
  • botgPackageDependencies( ... ) - declare the dependencies of a package
  • botgDownloadExternalProjects( ... ) - download an external project at configure time (used to bootstrap BootsOnTheGround)

Take a look at Testing123 for an example of how to use BOTG.

How do I get started?

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.

  1. Copy cmake/BOTG_INCLUDE.cmake containing bootstrap commands to your project's root cmake directory.
  2. Copy external/BootsOnTheGround.in containing repo link commands to your project's external directory.
  3. INCLUDE(cmake/BOTG_INCLUDE.cmake) first thing in your root CMakeLists.txt file to execute the bootstrap.
  4. 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.

Why?

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:

  1. do not have a dedicated build guy,
  2. do not have time or want CMake as a skill,
  3. use or depend on a mix of C++ and Fortran,
  4. are using TriBITS anyway, and/or
  5. 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:

  1. easily use existing TPLs with versioning,
  2. easily use each other's packages with versioning, and
  3. 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:

  1. Define a project as a collection of external and internal packages.
  2. Define for each internal package:
    1. dependency on external packages and TPLs;
    2. headers, libraries, and executables to deploy;
    3. unit tests; and the minimal
    4. compiler/linker flags or C++ standard needed to build.

Connection to TriBITS

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!

Connection to Hunter

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 but it is not clear if they will ever have Windows support.

Some Principles

  • 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.

Repository Structure

This repository uses Gitflow, i.e.

  1. Development is feature-based, always on feature/X branches of develop. The develop branch can be unstable.
  2. The master branch is only updated from develop when all tests pass. The master branch is always stable.
  3. Releases are first created as a release branch, release/vMAJOR.MINOR, then when ready are merged into the master branch and tagged vMAJOR.MINOR.0.
  4. Hotfixes are created as a branch off master: hotfix/vMAJOR.MINOR.PATCH, when finished are merged into master and tagged vMAJOR.MINOR.PATCH, then merged into develop.

Travis CI

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.