cMake实战组织大型工程目录

1,783 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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}
VariableInfo
PROJECT_NAME由当前 project()设置的项目名称 
CMAKE_PROJECT_NAME由 project()命令设置的第一个项目的名称,即顶级项目
PROJECT_SOURCE_DIR当前项目的源文件目录
PROJECT_BINARY_DIR当前项目的生成目录
name_SOURCE_DIR名为 "name" 的项目的源目录。在本例中,创建的源目录将是 code_SOURCE_DIRinclude_SOURCE_DIR, and library_SOURCE_DIR
name_BINARY_DIR名为 "name" 的项目的二进制目录 code_BINARY_DIRinclude_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 命令查看一下当前工程的目录结构

image.png

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 .cppadd_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)