问题描述
想象以下场景:项目 A 是一个共享库,它具有多个依赖项(LibA、LibB 和 LibC).项目 B 是一个依赖于项目 A 的可执行文件,因此也需要项目 A 的所有依赖项才能构建.
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.
此外,这两个项目都是使用 CMake 构建的,并且项目 A 不应需要安装(通过安装"目标)才能让项目 B 使用它,因为这可能会给开发人员带来麻烦.
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.
使用 CMake 解决这些依赖关系的最佳方法是什么?理想的解决方案应尽可能简单(尽管并不简单)并且需要最少的维护.
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.
推荐答案
简单.这是我脑海中的例子:
Easy. Here is the example from the top of my head:
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
在 components/B
中:
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
在 components/C
中:
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)
components/A
中的
CMakeLists.txt
:
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
在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.
这篇关于CMake 并查找其他项目及其依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!