0

I'm pulling in Boost using CPM to build another dependency of my project.

CPMAddPackage(
  NAME Boost
  VERSION 1.77.0
  GITHUB_REPOSITORY "boostorg/boost"
  GIT_TAG "boost-1.77.0"
)

The issue I'm having is exposing the header files to my dependency. I dumped all of the cmake variables, and there are many variables like boost_accumulators_SOURCE_DIR, boost_algorithm_SOURCE_DIR, boost_any_SOURCE_DIR, boost_asio_SOURCE_DIR, etc. My dependency depends on many of these libraries, and it's really tedious to list them all as include directories:

target_include_directories(
  nghttp2_asio
  PRIVATE "${boost_system_SOURCE_DIR}/include"
  PRIVATE "${boost_config_SOURCE_DIR}/include"
  PRIVATE "${boost_asio_SOURCE_DIR}/include"
  PRIVATE "${boost_throw_exception_SOURCE_DIR}/include"
  PRIVATE "${boost_assert_SOURCE_DIR}/include"
  PRIVATE "${BoostAlign_SOURCE_DIR}/include"
  PRIVATE "${boost_date_time_SOURCE_DIR}/include"
)

Is there a better way to do this?

Max
  • 15,157
  • 17
  • 82
  • 127
  • Possible dup: [How do you add Boost libraries in CMakeLists.txt?](https://stackoverflow.com/questions/6646405/how-do-you-add-boost-libraries-in-cmakelists-txt) – Dúthomhas Jan 30 '22 at 05:14
  • I don't think that's a dupe because I'm not using `find_package` to add Boost, at least directly. Also `Boost_INCLUDE_DIRS` is not a defined variable. – Max Jan 30 '22 at 05:18
  • I haven't used CPMAddPackage before. Does it allow you to use `find_package` afterwards? If so, I'd recommend doing that with the specific components you need. Then you don't mess with include directories, you can just link your target against the appropriate boost targets (`Boost::headers` and `Boost::date_time` in this case it looks like) – JohnFilleau Jan 30 '22 at 06:16
  • Contrary to the name, CPM does not perform any dependency management. It's more like an alternative to find_package that does not require mandatory "cmake-install" step, more suitable to workspace-based project organization. You need to write your own dependency management framework on top of it. – user7860670 Jan 30 '22 at 07:12

1 Answers1

1

CPM is just a thin layer around FetchContent, which in turn downloads your dependency into your build folder and then attempts to add_subdirectory it, adding it to your main build.

I think this is a bad idea for a lot of reasons...

  1. Boost is one of the most commonly packaged C++ libraries, period. Integrating your project into an existing environment (like a Linux distro, or another package manager like Conan or Vcpkg) is going to be difficult if not impossible (without patching, I mean) since it will surely want your project to use the curated build of Boost.
  2. Adding any third party CMake code to your own build is signing up for headaches... have you noticed how hard it is to write correct CMake code? What if Boost clobbers your cache variables or defines targets that conflict with yours?
  3. CMake has built-in and standard support for locating many versions of Boost, including the newer versions that provide first-party CMake config modules.
  4. Boost now provides first-party CMake config modules.

I don't think any build using Boost should be any more complex than this:

cmake_minimum_required(VERSION 3.22)
project(boost-usage-example)

find_package(Boost 1.77 REQUIRED system date_time)

add_executable(nghttp2_asio main.cpp ...)
target_link_libraries(
  nghttp2_asio
  PRIVATE
  Boost::boost  # all header-only libs: config asio throw_exception assert align
  Boost::date_time
  Boost::system
)

See the documentation here: https://cmake.org/cmake/help/latest/module/FindBoost.html

You only list as components (after REQUIRED) the non-header-only libraries. Those are also the ones that require special addition via target_link_libraries. Link to Boost::boost to get all the header-only modules.

This will work no matter what package manager you're using.


If you want to use vcpkg, create a file called vcpkg.json in your project root with the following contents:

{
  "name": "boost-usage-example",
  "version-string": "0.1.0-dev",
  "dependencies": [
    "boost-system",
    "boost-config",
    "boost-asio",
    "boost-throw-exception",
    "boost-assert",
    "boost-align",
    "boost-date-time"
  ]
}

You can also depend on just boost and it will acquire all boost modules, not just the ones you need. Then acquire vcpkg:

$ git clone https://github.com/microsoft/vcpkg.git
$ ./vcpkg/bootstrap-vcpkg.sh

Then build with:

$ cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$PWD/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release
$ cmake --build build

Done!


Suppose on the other hand that you wanted to use Conan. No big deal, just install Conan in a pip virtual environment:

$ python3 -m venv venv
$ . venv/bin/activate
$ python3 -m pip install -U pip setuptools wheel
$ python3 -m pip install conan

Then create a conanfile.txt with the following contents:

[requires]
boost/1.77.0

[generators]
cmake_paths

Install the dependencies:

$ mkdir build && pushd build && conan install .. && popd

And then build, using Conan's generated toolchain file:

$ cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$PWD/build/conan_paths.cmake -DCMAKE_BUILD_TYPE=Release
$ cmake --build build

In fact, you can provide both a vcpkg.json and a conanfile.txt and your users will be free to use either one or neither and rely on their system package manager or a package manager you don't know about. In any case it will just work and you free yourself of a mountain of maintenance burdens.

Alex Reinking
  • 16,724
  • 5
  • 52
  • 86