Handling of Dependecies on a CMake Project with multiple subprojects

Viewed 213

I have the following Structure of my Project:

    ├── CMakeLists.txt
    ├── libA
    │   ├── CMakeLists.txt
    │   ├── include
    │   │   └── libA
    │   │       └── my_liba.h
    │   ├── src
    │   │   └── my_liba.cpp
    │   └── test
    ├── libB
    │   ├── CMakeLists.txt
    │   ├── include
    │   │   └── libB
    │   │       └── my_libb.h
    │   ├── src
    │   │   └── my_libb.cpp
    │   └── test
    └── runner
        ├── CMakeLists.txt
        └── src
            └── main.cpp

I would like to achieve the following:

  • Be able to build libA and libB and Runner seperately
  • Be able to build all together.

For the dependency: libB depends on libA and Runner needs libB. How do i have to configure libB and Runner?

These are the current CMakeLists.txt Files:

libA/CMakeLists.txt:

cmake_minimum_required(VERSION 3.14)
project (MyLibA)

add_library(${PROJECT_NAME} src/my_liba.cpp)
add_library(Example::LibA ALIAS ${PROJECT_NAME})

target_include_directories( ${PROJECT_NAME}
    PUBLIC ${PROJECT_SOURCE_DIR}/include
)

libB/CMakeLists.txt:

cmake_minimum_required(VERSION 3.14)
project (MyLibB)

add_library(${PROJECT_NAME} src/my_libb.cpp)
add_library(Example::LibB ALIAS ${PROJECT_NAME})

target_link_libraries(${PROJECT_NAME} PRIVATE Example::LibA)

target_include_directories( ${PROJECT_NAME}
    PUBLIC ${PROJECT_SOURCE_DIR}/include
)

Runner/CMakeLists.txt

project(runner)

add_executable(${PROJECT_NAME} src/main.cpp)

target_link_libraries(${PROJECT_NAME}
    Example::LibB
)

CMakeLists.txt:

cmake_minimum_required(VERSION 3.14)

project(Example)

add_subdirectory(libA)
add_subdirectory(libB)
add_subdirectory(runner)

With the current configuration i can build everything out of the lib directory. I could also build libA from inside the libA Directory. But i could not build libB or runner from there folders, which was to be expected, since they do not know where to find the dependencies.

How do i have to change my CMakeFiles to get this to work?

2 Answers

If you want to be able to build libB using only the libB build script you need a way of making libB able to find the libA dependency.

From your question I assume that you actually want to build libA as part of the libB building process (you don't want to install libA before starting the libB build.

This looks weird, but you could do this in libB/CMakeLists.txt:

add_subdirectory(../libA)

The problem is that doing this will break the root build script. This can be fixed by checking if the libA target already exists:

if (NOT TARGET libA)
    add_subdirectory(../libA)
endif (NOT TARGET libA)

In this way, you either have a libA target because the root build script added the libA subdirectory, or you add it yourself.

add_subdirectory(../libA) looks weird to me, and probably some people with more CMake experience will consider it bad style, but I don't see another option here, given your setup. Other things may break if you do this, see this question.

As a side note, you may want to add some dependencies between your targets:

add_dependencies(libB libA)

In this way, when you build libB, if libA is not built, a libA build will be triggered before the libB build starts.

add_subdirectory allows you to pass any directory. If you don't pass a subdirectory though, you need to pass a second parameter specifying the binary directory use for this "subtree" of the sources. of course you need to make sure not to create circles.

This allows you to add the source directories of the projects a target depends on resulting in the following inclusions of source dirs:

  • libB/CMakeLists.txt includes libA
  • runner/CMakeLists.txt includes libB
  • CMakeLists.txt either includes only runner or is removed entirely (you could simply use runner as source dir instead)

A convenient location to use for the binary dirs would be subdirectories of the binary dir.

Changes to your CMakeLists.txt files

libA/CMakeLists.txt

remains unmodifies

libB/CMakeLists.txt

Create a directory to use as source dir and add the add_subdirectory

set(LIB_A_BINARY_DIR "${CMAKE_BINARY_DIR}/subproject_build_dirs/libA")
file(MAKE_DIRECTORY ${LIB_A_BINARY_DIR})
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../libA" ${LIB_A_BINARY_DIR})

runner/CMakeLists.txt

Similar to the changes above, but also add cmake_minimum_required to avoid cmake from producing warnings, if you want to use runner as the source dir.

cmake_minimum_required(VERSION 3.14)

set(LIB_B_BINARY_DIR "${CMAKE_BINARY_DIR}/subproject_build_dirs/libB")
file(MAKE_DIRECTORY ${LIB_B_BINARY_DIR})
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../libB" ${LIB_B_BINARY_DIR})

CMakeLists.txt

If you want to keep this file instead of using runner/CMakeLists.txt directly it should be changed to the following

cmake_minimum_required(VERSION 3.14)

project(Example)

add_subdirectory(runner)
Related