nikitablack
nikitablack

Reputation: 4663

Multiple sub-projects depending on the same library

I have a project main_project. It depends on two libraries - libA and libB, which in turn depend on lib_common each.

Graphically project's structure looks like this:

main_project
   |
   +-libA
   |   |
   |   +-CMakeLists.txt
   |
   +-libB
   |   |
   |   +-CMakeLists.txt
   |
   +-lib_common
   |   |
   |   +-CMakeLists.txt
   |
   +-CMakeLists.txt

Since libA uses lib_common it's pretty natural to call add_subdirectory(lib_common) inside libA's CMakeLists.txt. But the libB also depends on lib_common. If I put it in a subdirectory again it will compile twice. And I don't want that. Also I want to keep all my subprojects clean and self-contained - that's why I don't want to put lib_common in top-level main_project's CMakeLists.txt (because lib_common have nothing with main_project).

Which options do I have?

Upvotes: 7

Views: 2916

Answers (2)

Akira
Akira

Reputation: 4473

I would add one more abstraction level to your project structure:

  • app - this is for executables and for everything that belongs to them,
  • lib - libraries for the application.

The top-level CMakeLists.txt will only know that there is two different thing, the app sub-project and the lib sub-project. An example directory structure could look like follows:

FooProject
|-- app
|   |-- CMakeLists.txt
|   `-- src
|       `-- foo.cpp
|-- CMakeLists.txt
`-- lib
    |-- CMakeLists.txt
    |-- libA
    |   |-- CMakeLists.txt
    |   |-- inc
    |   |   `-- bar.h
    |   `-- src
    |       `-- bar.cpp
    |-- libB
    |   |-- CMakeLists.txt
    |   |-- inc
    |   |   `-- baz.h
    |   `-- src
    |       `-- baz.cpp
    `-- lib_common
        |-- CMakeLists.txt
        |-- inc
        |   `-- common_tool.h
        `-- src
            `-- common_tool.cpp

The top-level CMakeLists.txt contains only the following:

cmake_minimum_required(VERSION 2.8)
project(FooProject)

add_subdirectory(lib)
add_subdirectory(app)

The app/CMakeLists.txt should know that there is a lib folder from where you can include headers and that the executable depends on the libA and the libB:

project(foo)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../lib)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src FOO_SOURCES)

add_executable(foo ${FOO_SOURCES})
add_dependencies(foo libA libB)
target_link_libraries(foo libA libB)

install(TARGETS foo RUNTIME DESTINATION bin ARCHIVE DESTINATION bin)

The lib/CMakeLists.txt exists to tie together the libA, libB and lib_common:

cmake_minimum_required(VERSION 2.8)

add_subdirectory(lib_common)
add_subdirectory(libA)
add_subdirectory(libB)

Let's say that lib_common has no dependencies, so the lib/lib_common/CMakeLists.txt won't see outside of its folder:

project(lib_common)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src LIBCOMMON_SOURCES)

add_library(lib_common ${LIBCOMMON_SOURCES})

In this example the lib/libA/CMakeLists.txt and lib/libB/CMakeLists.txt have almost identical content and only they deal with the lib_common:

project(libA)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc ${CMAKE_CURRENT_SOURCE_DIR}/..)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src LIBA_SOURCES)

add_library(libA ${LIBA_SOURCES})
add_dependencies(libA lib_common)
target_link_libraries(libA lib_common)

install(TARGETS libA LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)

In this example the lib sub-project is what you can call as clean and self-contained.

Upvotes: 5

Nibor
Nibor

Reputation: 1258

I think this a fairly standard CMake solution:

# toplevel CMakeLists.txt
add_subdirectory(libA)
add_subdirectory(libB)
add_subdirectory(lib_common)

# libA CmakeLists.txt
add_library(libA ${LIBA_SOURCES})
target_link_libraries(libA lib_common)

and similar for libB.

You do not have to call add_subdirectory(lib_common) before add_subdirectory(libA), CMake handles the dependency management automatically.

Upvotes: 2

Related Questions