boostorg / cmake

CMake support infrastructure Boost submodule
82 stars 27 forks source link

Boost CMake support infrastructure

This repository hosts the tools/cmake Boost submodule, containing the CMake support infrastructure for Boost.

Note that the officially supported way to build Boost remains with b2.

Building Boost with CMake

The first thing you need to know is that the official Boost releases can't be built with CMake. Even though the Boost Github repository contains a CMakeLists.txt file, it's removed from the release.

That's because the file and directory layout of Boost releases, for historical reasons, has all the Boost header files copied into a single boost/ directory. These headers are then removed from the individual library include/ directories. The CMake support infrastructure expects the headers to remain in their respective libs/<libname>/include directories, and therefore does not work on a release archive.

To build Boost with CMake, you will need either a Git clone of Boost (git clone --recurse-submodules https://github.com/boostorg/boost) or the alternative archives available on Github.

Once you have cloned, or downloaded and extracted, Boost, use the usual procedure of

mkdir __build
cd __build
cmake ..
cmake --build .

to build it with CMake. To install it, add

cmake --build . --target install

Under Windows (when using the Visual Studio generator), you can control whether Debug or Release variants are built by adding --config Debug or --config Release to the cmake --build lines:

cmake --build . --config Debug
cmake --build . --target install --config Debug

The default is Debug. You can build and install both Debug and Release at the same time, by running the respective cmake --build line twice, once per --config:

cmake --build . --target install --config Debug
cmake --build . --target install --config Release

Configuration variables

The following variables are supported and can be set either from the command line as cmake -DVARIABLE=VALUE .., or via ccmake or cmake-gui:

Library-specific configuration variables

Some Boost libraries provide their own configuration variables, some of which are given below.

Context

Fiber

IOStreams

Locale

Stacktrace

Thread

Testing Boost with CMake

To run the Boost tests with CMake/CTest, first configure as before, but with BUILD_TESTING=ON:

mkdir __build
cd __build
cmake -DBUILD_TESTING=ON ..

then build the tests:

cmake --build . --target tests

and then run them:

ctest --output-on-failure --no-tests=error

Under Windows, you need to select a configuration (Debug or Release):

cmake --build . --target tests --config Debug
ctest --output-on-failure --no-tests=error -C Debug

To only build the tests for a specific library, and not the entire Boost, use BOOST_INCLUDE_LIBRARIES:

cmake -DBUILD_TESTING=ON -DBOOST_INCLUDE_LIBRARIES=timer ..

To build and run in parallel using more than one core, use the -j option:

cmake --build . --target tests -j 16
ctest --output-on-failure --no-tests=error -j 16

A convenience target check is provided that first builds the tests and then invokes ctest:

cmake --build . --target check

but it doesn't support running the tests in parallel.

Using Boost after building and installing it with CMake

Normally, a Boost installation is used from CMake by means of find_package(Boost). However, up to and including release 1.81.0, installing Boost with CMake did not deploy the necessary CMake configuration file for the Boost package, so find_package(Boost) did not work. (It also did not provide the Boost::boost and Boost::headers targets, on which many existing CMakeLists.txt files rely.)

Instead, the individual Boost libraries needed to be referenced as in

find_package(boost_filesystem 1.81 REQUIRED)

This has been rectified in Boost 1.82, which installs an umbrella CMake configuration file for the Boost package (BoostConfig.cmake) and provides the Boost::boost and Boost::headers compatibility targets.

Using Boost with add_subdirectory

Assuming that your project already has a copy of Boost in a subdirectory, either deployed as a Git submodule or extracted manually by the user as a prerequisite, using it is relatively straightforward:

add_subdirectory(deps/boost)

However, as-is, this will configure all Boost libraries and build them by default regardless of whether they are used. It's better to use

add_subdirectory(deps/boost EXCLUDE_FROM_ALL)

so that only the libraries that are referenced by the project are built, and it's even better to set BOOST_INCLUDE_LIBRARIES before the add_subdirectory call to a list of the Boost libraries that need to be configured:

set(BOOST_INCLUDE_LIBRARIES filesystem regex)
add_subdirectory(deps/boost EXCLUDE_FROM_ALL)

Using an individual Boost library with add_subdirectory

Boost is a large dependency, and sometimes a project only needs a single library. It's possible to use add_subdirectory with individual Boost libraries (https://github.com/boostorg/<libname>) instead of the entire superproject or release archive. However, since Boost libraries depend on each other quite extensively, all library dependencies also need to be added (again via add_subdirectory.)

As an example, this is how one would use Boost.Timer in this manner:

set(libs

  timer

  # Primary dependencies

  chrono
  config
  core
  io
  predef
  system
  throw_exception

  # Secondary dependencies

  assert
  integer
  move
  mpl
  ratio
  static_assert
  type_traits
  typeof
  utility
  winapi
  variant2
  preprocessor
  rational
  mp11
)

foreach(lib IN LISTS libs)

  add_subdirectory(deps/boost/${lib} EXCLUDE_FROM_ALL)

endforeach()

assuming that the individual libraries have been placed in subdirectories of deps/boost.

(The list of required dependencies above has been produced by running boostdep --brief timer. See the documentation of Boostdep.)

Using Boost with FetchContent

FetchContent downloads the required dependencies as part of CMake's project configuration phase. While this is convenient because it doesn't require the user to acquire the dependencies beforehand, in the case of Boost it involves an 87 MB download, so you should carefully weigh the pros and cons of this approach.

That said, here's how one would use Boost with FetchContent:

include(FetchContent)

FetchContent_Declare(
  Boost
  URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz
  URL_MD5 893b5203b862eb9bbd08553e24ff146a
  DOWNLOAD_EXTRACT_TIMESTAMP ON
)

FetchContent_MakeAvailable(Boost)

This has the same drawback as the simple add_subdirectory call -- all Boost libraries are configured and built, even if not used by the project.

To configure only some Boost libraries, set BOOST_INCLUDE_LIBRARIES before the FetchContent_MakeAvailable call:

set(BOOST_INCLUDE_LIBRARIES timer filesystem regex)
FetchContent_MakeAvailable(Boost)

To perform the add_subdirectory call with the EXCLUDE_FROM_ALL option, if you are using CMake 3.28 or newer, you can simply pass EXCLUDE_FROM_ALL to FetchContent_Declare:

FetchContent_Declare(
  Boost
  URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz
  URL_MD5 893b5203b862eb9bbd08553e24ff146a
  DOWNLOAD_EXTRACT_TIMESTAMP ON
  EXCLUDE_FROM_ALL
)

For earlier versions of CMake, you can replace FetchContent_MakeAvailable(Boost) with this:

FetchContent_GetProperties(Boost)

if(NOT Boost_POPULATED)

  message(STATUS "Fetching Boost")
  FetchContent_Populate(Boost)

  message(STATUS "Configuring Boost")
  add_subdirectory(${Boost_SOURCE_DIR} ${Boost_BINARY_DIR} EXCLUDE_FROM_ALL)

endif()