Transitive Dependencies

Hello!

I have two projects. projectA and projectB. Project A depends on projectB. projectB depends on system dependencies. When I build project A, I get a bunch of linker errors.

projectB:

cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
project(projectB LANGUAGES CXX)

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
    YES
    CACHE BOOL "Export all symbols")

add_definitions(-DBOOST_LOG_DYN_LINK)
find_package(Boost 1.74.0 REQUIRED COMPONENTS log)
find_package(OpenSSL 1.1.0 REQUIRED)

find_package(PkgConfig)
pkg_check_modules(LIBSOMETHING libosomething>=0.1.0 REQUIRED)

add_library(
  ${CMAKE_PROJECT_NAME}
  include/projectB/projectB.hpp
  src/projectB.cpp)
add_library("${CMAKE_PROJECT_NAME}::${CMAKE_PROJECT_NAME}" ALIAS "${CMAKE_PROJECT_NAME}")

target_compile_features(${CMAKE_PROJECT_NAME} PUBLIC cxx_std_17)
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE -Werror)
set_target_properties(
  ${CMAKE_PROJECT_NAME}
  PROPERTIES LINKER_LANGUAGE CXX)

target_include_directories(
  ${CMAKE_PROJECT_NAME}
  PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
         $<INSTALL_INTERFACE:include>)

target_include_directories(${CMAKE_PROJECT_NAME}
                           PRIVATE lib/local-dep-1)
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE lib/local-dep-2/src)

set(TARGETS_EXPORT_NAME "${CMAKE_PROJECT_NAME}Config")
install(
  TARGETS ${CMAKE_PROJECT_NAME}
  EXPORT               "${TARGETS_EXPORT_NAME}"
  LIBRARY DESTINATION  "${CMAKE_INSTALL_LIBDIR}"
  ARCHIVE DESTINATION  "${CMAKE_INSTALL_LIBDIR}"
  RUNTIME DESTINATION  "${CMAKE_INSTALL_BINDIR}"
  INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
export(
  TARGETS ${CMAKE_PROJECT_NAME}
  NAMESPACE ${CMAKE_PROJECT_NAME}::
  FILE "${CMAKE_CURRENT_BINARY_DIR}/${TARGETS_EXPORT_NAME}.cmake"
)
install(
  EXPORT ${TARGETS_EXPORT_NAME}
  DESTINATION "${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}/cmake"
  NAMESPACE ${CMAKE_PROJECT_NAME}::
)

projectA (excerpt):

get_filename_component( PROJECTB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/projectB" ABSOLUTE )
set(projectB_DIR "${PROJECTB_PATH}/_builds/${CMAKE_BUILD_TYPE}")
find_package(projectB REQUIRED)

I then add the projectB::projectB library to the target. This results some issues:

  • I have to add include directories for lib/projectB/lib/local-dep-1 and lib/projectB/lib/local-dep-2/src to projectA.
  • projectA has linker errors with finding OpenSSL and libsomething.
  • More of a minor annoyance than an issue, but projectA will not automatically build projectB to generate projectBConfig.cmake.

Is this the right way to do this? How do I have projectA pick up on linking OpenSSL and libsomething?

In projectB, you’ve marked these include directories as PRIVATE, meaning they apply to projectB only, not to its consumers. If its consumers also need these include directories (as this implies), make them PUBLIC instead.

I can’t see anything in projectB’s CMakeList that would indicate projectB is linking against OpenSSL and libsomething. Even if you build projectB as a static library, you should include the relevant target_link_libraries() command so that CMake knows those dependencies are necessary. In the case of static libraries, they will be propagated to consumers too (since static libraries don’t link).

That is the correct behaviour when using an external package (using find_package()) — that is intended for picking up pre-supplied dependencies. If you want to build your dependencies, I believe you should look into CMake’s dependency guide and FetchContent module.