FetchContent with Boost

Hi there,

I have been trying to make sense of the FetchContent documentation at:

However I must say I totally fail to understand the documentation in my simple case:

cmake_minimum_required(VERSION 3.24)
project(p)

set(BOOST_INCLUDE_LIBRARIES math filesystem system program_options)
set(BOOST_ENABLE_CMAKE ON)
include(FetchContent)
FetchContent_Declare(
  Boost
  GIT_REPOSITORY https://github.com/boostorg/boost.git
  GIT_TAG boost-1.80.0
)
FetchContent_MakeAvailable(Boost)

add_executable(boost_test boost_test.cpp)
target_link_libraries(boost_test PRIVATE Boost::filesystem
                                         Boost::program_options)

If I compile my code I get:

boost_test.cpp:2:10: fatal error: boost/math/special_functions/round.hpp: No such file or directory
    2 | #include <boost/math/special_functions/round.hpp>
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.

For some reason FetchContent_MakeAvailable did construct an almost working set of include directories, but for some reason did not add the math directories.

What did I misunderstood from FetchContent documentation ?

Thanks

Hi there,

Seeing the code it seems you are correctly calling boost and the modules you want. In the target_link_libraries I think you miss to add Boost::math, you are linking just to filesystem and program_options.

1 Like

I was hoping to get a more detailed answer in this case. The correct solution is indeed

target_link_libraries(boost_test PRIVATE Boost::math ...)

But when I read the documentation for target_link_libraries, here is what I see:

Specify libraries or flags to use when linking a given target and/or its dependents.

Could someone explain why target_link_libraries would have an influence target_include_directories ?

That’s because in CMake targets carry their dependencies when linking. Boost::math is a target, so it carries the information about needed defines, location of the libraries, and needed include directories. Those pieces of information will be added to your target when using target_link_libraries

1 Like

Thanks for the quick answer. Here is the documentation I am reading:

Each <item> may be:

  • A library target name: The generated link line will have the full path to the linkable library file associated with the target. The buildsystem will have a dependency to re-link <target> if the library file changes.

ref:

Would you mind sharing a link to the cmake documentation where you found this hint ? Thanks much

I’d love to give u a link, but I cannot find it either. It is one of the things that I have organically learned by using CMake and reading answers on SO, and MailingList.
If it’s written in the documentation in clear text, someone more familiar with the docu would have to point to it. Sorry.

Chapter 3 of the tutorial might be a good starting point.

1 Like

I was not expecting to get that much attention for my silly issue :smiley:

So given the interest, could someone please confirm what is the expected behavior of FetchContent for another cmake based project.

cmake_minimum_required(VERSION 3.24)
project(p)

include(FetchContent)
FetchContent_Declare(
  Log4CPP
  URL https://sourceforge.net/projects/log4cpp/files/log4cpp-1.1.x%20%28new%29/log4cpp-1.1/log4cpp-1.1.3.tar.gz
  DOWNLOAD_EXTRACT_TIMESTAMP TRUE
  )
FetchContent_MakeAvailable(Log4CPP)
if(TARGET log4cpp)
        message("I am a target")
endif()
add_executable(main main.cpp)
target_link_libraries(main PRIVATE log4cpp)

Short answer: the include_directory is not setup as in the previous example.

So where is the issue: in my code ? in cmake/FetchContent ? In log4cpp cmake project ?

Thanks

None of this is really specific to FetchContent. Your queries would be the same if you simply downloaded Boost sources yourself or added Boost as a git submodule, then brought those sources into your build with add_subdirectory(). The same applies to any other project you might choose to absorb into your build.

So to directly answer your last question, the expected behavior is: if log4cpp is a target that is being defined by the sources you’re downloading and absorbing into your build, then it should work fine as long as that target correctly adds its usage requirements. It would usually do that using things like target_include_directories(), target_compile_definitions(), etc. If the build doesn’t work, I would be looking into the log4cpp project to see why. Not all projects expect to be absorbed into a larger parent build this way, so you can’t always use FetchContent, git submodules, etc. to bring every project into your own.

Excuse the brevity, I’m answering queries between other obligations. :wink:

I know it’s a old thread, but I want to share this knowledge anyway with the world.

The fastest way now to download boost is to download the artifact in compressed format, instead of cloning the git repository with FetchContent, see below:

set(BOOST_INCLUDE_LIBRARIES asio regex algorithm)
set(BOOST_ENABLE_CMAKE ON)
FetchContent_Declare(
  Boost
  URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.7z
  USES_TERMINAL_DOWNLOAD TRUE
  DOWNLOAD_NO_EXTRACT FALSE
)
FetchContent_MakeAvailable(Boost)

Is there a usable CMakeLists.txt include in the boost tar archive?

1 Like

Yea there is. I now switched from 7z to tar.xz release archive. Works fine!

You can also add URL_MD5 with the md5sum hash, so you are sure you download the correct file also in the future. I now have the following:

set(BOOST_INCLUDE_LIBRARIES asio regex algorithm)
set(BOOST_ENABLE_CMAKE ON)
FetchContent_Declare(
  Boost
  URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz
  URL_MD5 893b5203b862eb9bbd08553e24ff146a
)
FetchContent_MakeAvailable(Boost)

Do you know Why you should NOT use Boost git repo with CPM.cmake or FetchContents

It works, but you can’t install any boost library, and so you can’t install your own library that is depending on a boost library!

Did you also link boost to your binary? Like so:

target_link_libraries(your_target Boost::asio);

Of course you also need to include the boost header as well in your code.

Claus-iMac:netkit-tftp clausklein$ cmake --workflow --preset default
Executing workflow step 1 of 5: configure preset "default"

Preset CMake variables:

  BUILD_SHARED_LIBS:BOOL="TRUE"
  CMAKE_BUILD_TYPE="Release"
  CMAKE_CXX_STANDARD="20"
  CMAKE_DEBUG_POSTFIX="D"
  CMAKE_INSTALL_PREFIX:PATH="/Users/clausklein/Workspace/cpp/netkit-tftp/stagedir"
  CMAKE_PREFIX_PATH:STRING="/Users/clausklein/Workspace/cpp/netkit-tftp/stagedir"

Preset environment variables:

  CMAKE_EXPORT_COMPILE_COMMANDS="YES"
  CPM_SOURCE_CACHE="/Users/clausklein/.cache/CPM"
  CPM_USE_LOCAL_PACKAGES="NO"
  PATH="/Users/clausklein/.local/bin:/usr/local/opt/net-snmp/bin:/Users/clausklein/Library/Python/3.9/bin:/usr/local/opt/python/libexec/bin:/usr/local/opt/sqlite/bin:/usr/local/bin:/usr/local/sbin:/Users/clausklein/scripts:/Users/clausklein/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/texbin:/usr/local/MacGPG2/bin:/opt/X11/bin:/Library/Apple/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin"

-- Boost: Release build, shared libraries, MPI OFF, Python OFF, testing OFF
-- Boost: libraries included: asio;filesystem
CMake Warning (dev) at build/_deps/boost-src/tools/cmake/include/BoostRoot.cmake:308 (set):
  uninitialized variable 'CMAKE_FOLDER'
Call Stack (most recent call first):
  build/_deps/boost-src/CMakeLists.txt:20 (include)
This warning is for project developers.  Use -Wno-dev to suppress it.

CMake Warning (dev) at build/_deps/boost-src/tools/cmake/include/BoostRoot.cmake:310 (if):
  uninitialized variable 'CMAKE_FOLDER'
Call Stack (most recent call first):
  build/_deps/boost-src/CMakeLists.txt:20 (include)
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Boost.Context: architecture x86_64, binary format mach-o, ABI sysv, assembler gas, suffix .S, implementation fcontext
-- CPM: Adding package PackageProject.cmake@1.11.0 (v1.11.0 at /Users/clausklein/.cache/CPM/packageproject.cmake/fc402989ee6e628476b3d8661f8f96774d89df74)
-- CPM: Adding package project_options@0.32.1 (v0.32.1 at /Users/clausklein/.cache/CPM/project_options/74a42772cc49148bbbf34bad0ffe51f95b47cf3f)
-- Configuring done (0.9s)
CMake Error: install(EXPORT "tftpdTargets" ...) includes target "tftpd" which requires target "boost_filesystem" that is not in any export set.
-- Generating done (0.1s)
CMake Generate step failed.  Build files cannot be regenerated correctly.
Claus-iMac:netkit-tftp clausklein$ 

@ClausKlein While Boost itself might not define install(...) rules for you, that doesn’t stop you adding your own install(...) commands for Boost targets in your own project. Starting with CMake 3.13, you can put install(TARGETS ...) commands in a different directory scope to the one that defines those targets.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2fd501f..f99e70f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,11 +26,23 @@ include(GNUInstallDirs)
 # search required packages and libs
 #---------------------------------------------------------------------------------------
 
-find_package(
-    Boost 1.71 CONFIG
-    COMPONENTS filesystem
-    REQUIRED
+# find_package(
+#     Boost 1.84 CONFIG
+#     COMPONENTS filesystem
+#     REQUIRED
+# )
+
+include(FetchContent)
+
+set(BOOST_INCLUDE_LIBRARIES asio filesystem)
+option(BOOST_SKIP_INSTALL_RULES NO)
+FetchContent_Declare(
+  Boost
+  URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz
+  URL_MD5 893b5203b862eb9bbd08553e24ff146a
 )
+FetchContent_MakeAvailable(Boost)
+
 
 # ---- Add other dependencies via CPM ----
 # see https://github.com/cpm-cmake/CPM.cmake for more info
@@ -100,7 +112,7 @@ if(NOT CMAKE_SKIP_INSTALL_RULES)
         # EXPORT_HEADER ${EXPORT_HEADER_LOCATION}
         COMPATIBILITY SameMajorVersion
         DISABLE_VERSION_SUFFIX YES
-        DEPENDENCIES "boost 1.71"
+        DEPENDENCIES "Boost 1.84"
     )
     # NOTE: implicit done! add_library(tftpd::tftpd ALIAS tftpd)
     install(FILES async_tftpd_server.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

This line looks malformed, but it will happen to work by chance. The syntax is:

option(<varName> <helpString> [<value>])

In your case, you set the <helpString> to NO. It just so happens that when you omit the <value> it defaults to false anyway.

Boost cmake system is usable for developers only!

CMake Error at CMakeLists.txt:100 (install):
  install TARGETS given target "filesystem" which does not exist.


-- Configuring incomplete, errors occurred!

Find package is not required anymore since you use fetchcontent to retrieve the boost dependency.