持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情。你可以简单浏览一下目录,有需要的阅读,写文章不易,阅读之前请给我点个赞吧~
一、github 项目实例源码一
在本例中,我将头文件移动到每个项目包含目录下的子文件夹中,而将目标包含作为根包含文件夹。这是一个防止文件名冲突的好主意,因为你必须包括如下文件:
1.1 添加子模块目录
add_subdirectory(code)//An executable
add_subdirectory(include)//A header only library
add_subdirectory(library)//A static library
1.2 引用子项目(Sub-Project)目录
当使用project()命令创建一个项目时,CMake将自动创建许多变量,这些变量可用于引用项目的详细信息。然后,这些变量可以被其他子项目或主项目使用。例如,为可以使用的其他项目引用源目录。
${code_SOURCE_DIR}
${include_SOURCE_DIR}
${library_SOURCE_DIR}
| Variable | Info |
|---|---|
| PROJECT_NAME | 由当前 project()设置的项目名称 |
| CMAKE_PROJECT_NAME | 由 project()命令设置的第一个项目的名称,即顶级项目 |
| PROJECT_SOURCE_DIR | 当前项目的源文件目录 |
| PROJECT_BINARY_DIR | 当前项目的生成目录 |
| name_SOURCE_DIR | 名为 "name" 的项目的源目录。在本例中,创建的源目录将是 code_SOURCE_DIR, include_SOURCE_DIR, and library_SOURCE_DIR |
| name_BINARY_DIR | 名为 "name" 的项目的二进制目录 code_BINARY_DIR, include_BINARY_DIR, and library_BINARY_DIR |
1.3 仅有头文件的库
如果你有一个库是作为一个头库创建的,cmake 支持 INTERFACE 目标(target)来允许创建一个没有任何构建输出的目标。更多细节可以从这里找到。
add_library(${PROJECT_NAME} INTERFACE)
在创建目标时,还可以使用 INTERFACE 范围为该目标包含目录。INTERFACE作用域用于创建目标需求,这些需求在链接此目标的任何库中使用,但不在目标本身的编译中使用。
target_include_directories(${PROJECT_NAME}
INTERFACE
${PROJECT_SOURCE_DIR}/include
)
1.4 从子项目引用库
二、实战
以 github 上的一个大型项目为例
使用 tree 命令查看一下当前工程的目录结构
2.1 顶层的 CMakeList.txt 文件内容
# 1.
cmake_minimum_required(VERSION 3.2)
# 2.
project(cmake-project-template)
# 3.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3")
# 4.
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})
# 5.
set(DIVISIBLE_INSTALL_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
set(DIVISIBLE_INSTALL_BIN_DIR ${PROJECT_SOURCE_DIR}/bin)
set(DIVISIBLE_INSTALL_LIB_DIR ${PROJECT_SOURCE_DIR}/lib)
set(DIVISION_HEADERS_DIR ${PROJECT_SOURCE_DIR}/src/division)
# 6.
include_directories(${DIVISIBLE_INSTALL_INCLUDE_DIR})
include_directories(${DIVISION_HEADERS_DIR})
# 7.
add_subdirectory(src)
add_subdirectory(test)
2.2 主要部分
我们可以看到这里有
1. 版本号
2. 工程名
3. C++标准
4. 指定 CMake 执行install目标时,安装路径的前缀
设置 CMake 内置变量 CMAKE_INSTALL_PREFIX,这里是将CMAKE_INSTALL_PREFIX的路径设置成当前项目的源文件目录。
set(CMAKE_INSTALL_PREFIX <install_path>)
要加在project之后 比如在设置intall 安装目录之后,执行install时,可以通过DESTINATION直接指定安装目录之下的路径。这里将test安装到 /usr/local/bin目录下
set(CMAKE_INSTALL_PREFIX /usr/local)
install(TARGETS test DESTINATION bin)
5. 设置目录并存在变量中
(1). 设置include目录并存在变量中
指定实际路径为 ${PROJECT_SOURCE_DIR}/include ,当前项目的源文件目录下的 include 目录。
(2). 设置 bin 目录并存在变量中 (3). 设置 lib 目录并存在变量中 (4). 设置 src/division 目录并存在变量中 包含 .h .cpp 文件
6. 包含目录,指定目录,给预编译器做头文件搜索
7. 添加子模块目录
有子目录的
2.3 子模块的 CMakeLists.txt
每个子模块都需要有 CMakeLists.txt
2.3.1 子模块 src
# 1. 版本号
cmake_minimum_required(VERSION 3.2)
# 2. 项目名称
project(divider)
# 3. 添加子模块(含 .h .cpp)
add_subdirectory(division)
set(SOURCE_FILES main.cpp)
# 4. 指定编译构建的可执行的目标
add_executable(divider ${SOURCE_FILES})
# 5. 将子模块的库链接到当前的可执行文件
target_link_libraries(divider division)
install(TARGETS divider DESTINATION ${DIVISIBLE_INSTALL_BIN_DIR})
2.3.1.1 子模块的子模块
# 1.
cmake_minimum_required(VERSION 3.2)
# 2.
project(division C CXX)
# 3.
set(SOURCE_FILES
division.h
division.cpp
)
# 4.
add_library(division SHARED STATIC ${SOURCE_FILES})
# add_library(<name> [STATIC | SHARED | MODULE]
# [EXCLUDE_FROM_ALL]
# [<source>...])
# 添加一个库,命名为 `<name>` ,由源文件 `<source>` 构建;
# 这个 `<name>` 需要全局唯一
# The actual file name of the library built is constructed based on conventions of the native platform (such as `lib<name>.a` or `<name>.lib`).
# 5.
install(TARGETS division DESTINATION ${DIVISIBLE_INSTALL_LIB_DIR})
install(FILES division.h DESTINATION ${DIVISIBLE_INSTALL_INCLUDE_DIR})
2.3.2 子模块test
# 1.
cmake_minimum_required(VERSION 3.2)
# 2.
project(divider_tests)
# 3.
add_subdirectory(lib/googletest)
# 4.
include_directories(${DIVISION_HEADERS_DIR})
# 当前子模块需要其他模块的头文件目录,就在此处使用include_directories引用
include_directories(lib/googletest/googletest/include)
# 5.
set(SOURCE_FILES main.cpp src/divider_tests.cpp)
# 6.
add_executable(divider_tests ${SOURCE_FILES})
# 7.
target_link_libraries(divider_tests division gtest)
# 8.
install(TARGETS divider_tests DESTINATION bin)