[CMake] Target not linking to Boost header libs compiled with ExternalProject_Add

Thiago Crepaldi dev at thiagocrepaldi.com
Tue Mar 12 19:24:24 EDT 2019


Hi all,

I have created a small example app that links to Boost regex (works)
and to a Boost header-only lib property_tree (fails).

Using CMake 3.12.2 on a Ubuntu 18.10 x64, I get this error:

[50%] Building CXX object CMakeFiles/dummy.dir/main.cpp.o
small_superbuild/src/main.cpp:3:10: fatal error:
boost/property_tree/ptree.hpp: No such file or directory
#include <boost/property_tree/ptree.hpp>

I have used the superbuild pattern to build upstream boost if not
found in the system and finally link to the main app called "dummy".
The idea is from
https://github.com/dev-cafe/cmake-cookbook/tree/master/chapter-08/recipe-02/cxx-example

The folder structure for this dummy project is as follow:
*******************************************************************************
CMakeLists.txt
*******************************************************************************
cmake_minimum_required(VERSION 3.5)
set( DUMMY_APP_PROJECT_VERSION 0.1
CACHE INTERNAL "Dummy App project version" FORCE)

project(dummy_app VERSION ${DUMMY_APP_PROJECT_VERSION} LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Superbuild stuff
set(EP_BASE ${CMAKE_BINARY_DIR}/subprojects)
set_property(DIRECTORY PROPERTY EP_BASE ${EP_BASE})
set(STAGED_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/stage)
include(ExternalProject)

# Add external dependencies
add_subdirectory(external/upstream)

# Dummy App project (main target)
ExternalProject_Add(${PROJECT_NAME}_core
DEPENDS boost_external
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/src
CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
-DCMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}
-DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}/dummy_app
CMAKE_CACHE_ARGS -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
-DCMAKE_INCLUDE_PATH:INTERNAL=${BOOST_INCLUDEDIR};${GOOGLETEST_INCLUDEDIR}
-DCMAKE_LIBRARY_PATH:INTERNAL=${BOOST_LIBRARYDIR};${GOOGLETEST_LIBRARYDIR}
-DDUMMY_APP_PROJECT_VERSION:INTERNAL=${DUMMY_APP_PROJECT_VERSION}
-DSTAGED_INSTALL_PREFIX:INTERNAL=${STAGED_INSTALL_PREFIX}
-DBOOST_ROOT:INTERNAL=${BOOST_ROOT}
-DBOOST_INCLUDEDIR:INTERNAL=${BOOST_INCLUDEDIR}
-DBOOST_LIBRARYDIR:INTERNAL=${BOOST_LIBRARYDIR}
-DBOOST_MINIMUM_REQUIRED:INTERNAL=${BOOST_MINIMUM_REQUIRED}
-DBOOST_COMPONENTS_REQUIRED:INTERNAL=${BOOST_COMPONENTS_REQUIRED}
BUILD_ALWAYS 1
)

*******************************************************************************
external/upstream/CMakeLists.txt
*******************************************************************************
# Boost dependencies
add_subdirectory(boost)

*******************************************************************************
external/upstream/boost/CMakeLists.txt
*******************************************************************************
set(
BOOST_MINIMUM_REQUIRED "1.65"
CACHE INTERNAL "Minimum Boost version required by the build"
FORCE
)
set(
BOOST_COMPONENTS_REQUIRED regex
CACHE INTERNAL "Boost components required by the build (separated by semicolon)"
FORCE
)

find_package(Boost ${BOOST_MINIMUM_REQUIRED} COMPONENTS
${BOOST_COMPONENTS_REQUIRED})
if(Boost_FOUND)
message(STATUS "Found Boost version
${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}
at the host environment")
add_library(boost_external INTERFACE)
get_filename_component(_boost_root_dir "${Boost_LIBRARY_DIRS}" PATH)
set(
BOOST_ROOT ${_boost_root_dir}
CACHE INTERNAL "Path to externally built Boost installation root"
FORCE
)
set(
BOOST_INCLUDEDIR ${Boost_INCLUDE_DIRS}
CACHE INTERNAL "Path to externally built Boost include directories"
FORCE
)
set(
BOOST_LIBRARYDIR ${Boost_LIBRARY_DIRS}
CACHE INTERNAL "Path to externally built Boost library directories"
FORCE
)

# Unset internal variables
unset(_boost_root_dir)
else()
message(STATUS "Boost ${BOOST_MINIMUM_REQUIRED} could not be located.
Building Boost instead")

if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
if(APPLE)
set(_toolset "darwin")
else()
set(_toolset "gcc")
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
set(_toolset "clang")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
if(APPLE)
set(_toolset "intel-darwin")
else()
set(_toolset "intel-linux")
endif()
endif()

# Non-empty list. Compiled libraries needed
if(NOT "${BOOST_COMPONENTS_REQUIRED}" STREQUAL "")
# Replace unit_test_framework (used by CMake's find_package) with test
(understood by Boost build toolchain)
string(REPLACE "unit_test_framework" "test" _b2_needed_components
"${BOOST_COMPONENTS_REQUIRED}")
# Generate argument for BUILD_BYPRODUCTS
set(_build_byproducts)
set(_b2_select_libraries)
foreach(_lib IN LISTS _b2_needed_components)
list(APPEND _build_byproducts
${STAGED_INSTALL_PREFIX}/boost/lib/libboost_${_lib}${CMAKE_SHARED_LIBRARY_SUFFIX})
list(APPEND _b2_select_libraries --with-${_lib})
endforeach()
# Transform the ;-separated list to a ,-separated list (digested by
the Boost build toolchain!)
string(REPLACE ";" "," _b2_needed_components "${_b2_needed_components}")
set(_bootstrap_select_libraries "--with-libraries=${_b2_needed_components}")
string(REPLACE ";" ", " printout "${BOOST_COMPONENTS_REQUIRED}")
message(STATUS " Libraries to be built: ${printout}")
endif()

include(ExternalProject)
ExternalProject_Add(boost_external
GIT_REPOSITORY https://github.com/boostorg/boost.git
GIT_TAG "boost-1.65.0"
GIT_PROGRESS 0
CONFIGURE_COMMAND
<SOURCE_DIR>/bootstrap.sh
--with-toolset=${_toolset}
--prefix=${STAGED_INSTALL_PREFIX}/boost
${_bootstrap_select_libraries}
BUILD_COMMAND
<SOURCE_DIR>/b2 -q
link=shared
threading=multi
variant=release
toolset=${_toolset}
${_b2_select_libraries}
LOG_BUILD
1
BUILD_IN_SOURCE
1
INSTALL_COMMAND
<SOURCE_DIR>/b2 -q install
link=shared
threading=multi
variant=release
toolset=${_toolset}
${_b2_select_libraries}
LOG_INSTALL
1
BUILD_BYPRODUCTS
"${_build_byproducts}"
)

set(
BOOST_ROOT ${STAGED_INSTALL_PREFIX}/boost
CACHE INTERNAL "Path to internally built Boost installation root"
FORCE
)
set(
BOOST_INCLUDEDIR ${BOOST_ROOT}/include
CACHE INTERNAL "Path to internally built Boost include directories"
FORCE
)
set(
BOOST_LIBRARYDIR ${BOOST_ROOT}/lib
CACHE INTERNAL "Path to internally built Boost library directories"
FORCE
)

# Unset internal variables
unset(_toolset)
unset(_b2_needed_components)
unset(_build_byproducts)
unset(_b2_select_libraries)
unset(_boostrap_select_libraries)
endif()

*******************************************************************************
src/CMakeLists.txt
*******************************************************************************
# TODO: Properly export target
cmake_minimum_required(VERSION 3.5)
project(dummy_core VERSION ${DUMMY_APP_PROJECT_VERSION} LANGUAGES CXX)

set(DUMMY_APP_SRC main.cpp)

add_executable(dummy ${DUMMY_APP_SRC})
set_target_properties(dummy PROPERTIES CXX_STANDARD ${CMAKE_CXX_STANDARD}
CXX_EXTENSIONS ${CMAKE_CXX_EXTENSIONS}
CXX_STANDARD_REQUIRED ${CMAKE_CXX_STANDARD_REQUIRED})
# Boost is used for CTF parsing
find_package(Boost ${BOOST_MINIMUM_REQUIRED} COMPONENTS
${BOOST_COMPONENTS_REQUIRED} REQUIRED)

# Includes
target_include_directories(dummy PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
${BOOST_INCLUDEDIR})

# Libs
target_link_libraries(dummy PUBLIC Boost::boost ${Boost_LIBRARIES})

# Install both targets
install(TARGETS dummy DESTINATION bin)

*******************************************************************************
src/main.cpp
*******************************************************************************
#include <iostream>
#include <boost/regex.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

int main(){
// Header only boost library
boost::property_tree::ptree pt;
boost::property_tree::read_json("data.json", pt);

// regular boost lib
std::string line;
boost::regex pat( "^Subject: (Re: |Aw: )*(.*)" );
while (std::cin)
{
std::getline(std::cin, line);
boost::smatch matches;
if (boost::regex_match(line, matches, pat))
std::cout << matches[2] << std::endl;
}
}

As you can see, linking to a an actual library using this approach is
not a problem. However, using property_tree, which is a header only
lib, fails to compile. By reading
https://cmake.org/cmake/help/v3.12/module/FindBoost.html, I learned
that there is a Boost::boost target to link agains for the header-only
libs, but doing

target_link_libraries(dummy PUBLIC Boost::boost ${Boost_LIBRARIES} Boost::boost)

didnt help either. Looking CMakeCache.txt of dummy_core project, I can see
Boost_INCLUDE_DIR:PATH=(...)/small_superbuild/build/stage/boost/include,
which does not include
(...)/small_superbuild/build/subprojects/Source/boost_external/libs/property_tree/include/

Any idea what is happening?

 --
Thiago


More information about the CMake mailing list