Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CMake and finding other projects and their dependencies

Tags:

cmake

Imagine the following scenario: Project A is a shared library which has several dependencies (LibA, LibB, and LibC). Project B is an executable that has a dependency on project A, and therefore requires all of Project A's dependencies also in order to build.

Additionally, both projects are built using CMake, and Project A should not need to be installed (via the 'install' target) in order for Project B to use it, as this can become a nuisance to developers.

What is the best way to solve these dependencies using CMake? The ideal solution would be as simple as possible (though no simpler) and require minimal maintenance.

like image 203
blockchaindev Avatar asked May 06 '13 12:05

blockchaindev


People also ask

What are CMake dependencies?

CMake files provided with a software package contain instructions for finding each build dependency. Some build dependencies are optional in that the build may succeed with a different feature set if the dependency is missing, and some dependencies are required.

Can CMake install dependencies?

CMake itself does not allow to install dependencies automatically.

What does Add_subdirectory do in CMake?

Add a subdirectory to the build. Adds a subdirectory to the build. The source_dir specifies the directory in which the source CMakeLists.


1 Answers

Easy. Here is the example from the top of my head:

The top level CMakeLists.txt:

cmake_minimum_required(VERSION 2.8.10)  # You can tweak some common (for all subprojects) stuff here. For example:  set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) set(CMAKE_DISABLE_SOURCE_CHANGES  ON)  if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")   message(SEND_ERROR "In-source builds are not allowed.") endif ()  set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_COLOR_MAKEFILE   ON)  # Remove 'lib' prefix for shared libraries on Windows if (WIN32)   set(CMAKE_SHARED_LIBRARY_PREFIX "") endif ()  # When done tweaking common stuff, configure the components (subprojects). # NOTE: The order matters! The most independent ones should go first. add_subdirectory(components/B) # B is a static library (depends on Boost) add_subdirectory(components/C) # C is a shared library (depends on B and external XXX) add_subdirectory(components/A) # A is a shared library (depends on C and B)  add_subdirectory(components/Executable) # Executable (depends on A and C) 

CMakeLists.txt in components/B:

cmake_minimum_required(VERSION 2.8.10)  project(B C CXX)  find_package(Boost              1.50.0              REQUIRED)  file(GLOB CPP_FILES source/*.cpp)  include_directories(${Boost_INCLUDE_DIRS})  add_library(${PROJECT_NAME} STATIC ${CPP_FILES})  # Required on Unix OS family to be able to be linked into shared libraries. set_target_properties(${PROJECT_NAME}                       PROPERTIES POSITION_INDEPENDENT_CODE ON)  target_link_libraries(${PROJECT_NAME})  # Expose B's public includes (including Boost transitively) to other # subprojects through cache variable. set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include                                  ${Boost_INCLUDE_DIRS}     CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE) 

CMakeLists.txt in components/C:

cmake_minimum_required(VERSION 2.8.10)  project(C C CXX)  find_package(XXX REQUIRED)  file(GLOB CPP_FILES source/*.cpp)  add_definitions(${XXX_DEFINITIONS})  # NOTE: Boost's includes are transitively added through B_INCLUDE_DIRS. include_directories(${B_INCLUDE_DIRS}                     ${XXX_INCLUDE_DIRS})  add_library(${PROJECT_NAME} SHARED ${CPP_FILES})  target_link_libraries(${PROJECT_NAME} B                                       ${XXX_LIBRARIES})  # Expose C's definitions (in this case only the ones of XXX transitively) # to other subprojects through cache variable. set(${PROJECT_NAME}_DEFINITIONS ${XXX_DEFINITIONS}     CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)  # Expose C's public includes (including the ones of C's dependencies transitively) # to other subprojects through cache variable. set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include                                  ${B_INCLUDE_DIRS}                                  ${XXX_INCLUDE_DIRS}     CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE) 

CMakeLists.txt in components/A:

cmake_minimum_required(VERSION 2.8.10)  project(A C CXX)  file(GLOB CPP_FILES source/*.cpp)  # XXX's definitions are transitively added through C_DEFINITIONS. add_definitions(${C_DEFINITIONS})  # NOTE: B's and Boost's includes are transitively added through C_INCLUDE_DIRS. include_directories(${C_INCLUDE_DIRS})  add_library(${PROJECT_NAME} SHARED ${CPP_FILES})  # You could need `${XXX_LIBRARIES}` here too, in case if the dependency  # of A on C is not purely transitive in terms of XXX, but A explicitly requires # some additional symbols from XXX. However, in this example, I assumed that  # this is not the case, therefore A is only linked against B and C. target_link_libraries(${PROJECT_NAME} B                                       C)  # Expose A's definitions (in this case only the ones of C transitively) # to other subprojects through cache variable. set(${PROJECT_NAME}_DEFINITIONS ${C_DEFINITIONS}     CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)  # Expose A's public includes (including the ones of A's dependencies # transitively) to other subprojects through cache variable. set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include                                  ${C_INCLUDE_DIRS}     CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE) 

CMakeLists.txt in components/Executable:

cmake_minimum_required(VERSION 2.8.10)  project(Executable C CXX)  file(GLOB CPP_FILES source/*.cpp)  add_definitions(${A_DEFINITIONS})  include_directories(${A_INCLUDE_DIRS})  add_executable(${PROJECT_NAME} ${CPP_FILES})  target_link_libraries(${PROJECT_NAME} A C) 

To make it clear, here is the corresponding source tree structure:

Root of the project ├───components │   ├───Executable │   │   ├───resource │   │   │   └───icons │   │   ├───source |   |   └───CMakeLists.txt │   ├───A │   │   ├───include │   │   │   └───A │   │   ├───source |   |   └───CMakeLists.txt │   ├───B │   │   ├───include │   │   │   └───B │   │   ├───source |   |   └───CMakeLists.txt │   └───C │       ├───include │       │   └───C │       ├───source |       └───CMakeLists.txt └───CMakeLists.txt 

There are many points where this could be tweaked/customized or changed to satisfy certain needs, but this should at least get you started.

NOTE: I've successfully employed this structure in several medium-sized and large projects.

like image 83
Alexander Shukaev Avatar answered Nov 05 '22 16:11

Alexander Shukaev