编写 CMakeLists

CMake

最近项目使用 CMake 进行自动化建构,主要是编写 CMakeLists 文件。

CMake简介

CMake 是一个跨平台的自动化建构系统,使用一个名为 CMakeLists.txt 的文件来描述构建过程,可以产生标准的构建文件,如 Unix 的 Makefile 或Windows Visual C++ 的 projects/workspaces。文件 CMakeLists.txt 需要手工编写,CMake 提供了比 autoconfig 更简洁的语法。

在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:

  1. 编写 CmakeLists.txt
  2. 执行命令cmake PATH或者ccmake PATH生成 Makefile ( PATH 是 CMakeLists.txt 所在的目录 )
  3. 使用 make 命令进行编译

CMakeLists

官方文档

最全的肯定还是官方文档:CMake Documentation

基本语法

  1. 变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名
  2. 指令(参数 1 参数 2…) 参数使用括弧括起,参数之间使用空格或分号分开
  3. 指令是大小写无关,参数和变量是大小写相关的

常用命令

命令 说明
cmake_minimum_required 指定 CMake 最低版本
project 指定项目的名称
add_executable 生成可执行程序,可指定多个文件,空格分隔
add_subdirectory 可以将指定的文件夹加到build任务列表中
add_library 生成库文件
target_link_libraries 指定链接目标文件时需要链接的外部库
link_directories 动态链接库或静态链接库的搜索路径
include_directories 指定编译过程中编译器搜索头文件的路径
add_definitions 添加编译参数
set 设置变量
add_dependencies 设置依赖项

文件操作

获取目录下所有文件

变量THIRD_PARTY_LIBRARIES中将包含几个目录的所有文件:

1
2
3
4
5
SET(THIRD_PARTY_PATH ${PROJECT_SOURCE_DIR}/3rdparty)
FILE(GLOB THIRD_PARTY_LIBRARIES
${THIRD_PARTY_PATH}/lib1/lib/*
${THIRD_PARTY_PATH}/lib2/lib/*
${THIRD_PARTY_PATH}/lib3/lib/*)

拷贝文件

方法一:

1
#FILE(COPY ${THIRD_PARTY_LIBRARIES} DESTINATION ${LIBRARY_OUTPUT_PATH})

但只会在第一次 Build 时拷贝,若目标更新不会重新拷贝。

方法二:

1
2
3
4
5
6
7
8
ADD_CUSTOM_TARGET(copy_file ALL
COMMAND
${CMAKE_COMMAND} -E make_directory ${LIBRARY_OUTPUT_PATH} ${EXECUTABLE_OUTPUT_PATH}/script
COMMAND
${CMAKE_COMMAND} -E copy_if_different ${THIRD_PARTY_LIBRARIES} ${LIBRARY_OUTPUT_PATH}
COMMAND
${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/script ${EXECUTABLE_OUTPUT_PATH}/script
)

make_directory:创建目录
copy_if_different:拷贝文件
copy_directory:拷贝目录下所有文件

该方法每次 Build 都会进行拷贝。根据官方文档ALL选项默认都会构建,但是我在 CLion 底下失败了,所以使用了 add_dependencies 将这个 Custom Target 添加为目标的依赖。

多项目整编

  1. 将项目依赖的库、头文件路径配置为变量,若下层项目有依赖关系,可直接使用变量进行添加库、头文件路径
  2. 使用link_directories添加各项目依赖(项目之间也有可能出现依赖)
  3. 使用add_subdirectory添加各项目

例如,根 CMakeLists 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
SET(DATABASE_SOURCE_DIR ${PROJECT_SOURCE_DIR}/database)
SET(DATABASE_INCLUDE_DIR
${DATABASE_SOURCE_DIR}/include
${DATABASE_SOURCE_DIR}/database/include)

SET(CONFIG_INCLUDE_DIR
${PROJECT_SOURCE_DIR}/config/include)

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/app)
SET(LIBRARY_OUTPUT_PATH ${EXECUTABLE_OUTPUT_PATH}/lib)

LINK_DIRECTORIES(${LIBRARY_OUTPUT_PATH})

FILE(GLOB THIRD_PARTY_LIBRARIES
${THIRD_PARTY_PATH}/lib1/lib/*
${THIRD_PARTY_PATH}/lib2/lib/*
${THIRD_PARTY_PATH}/lib3/lib/*)
#FILE(COPY ${THIRD_PARTY_LIBRARIES} DESTINATION ${LIBRARY_OUTPUT_PATH})

#FILE(COPY ${OUTCALL_SERVER_SCRIPTS} DESTINATION ${EXECUTABLE_OUTPUT_PATH}/script)

ADD_CUSTOM_TARGET(copy_file ALL
COMMAND
${CMAKE_COMMAND} -E make_directory ${LIBRARY_OUTPUT_PATH} ${EXECUTABLE_OUTPUT_PATH}/script
COMMAND
${CMAKE_COMMAND} -E copy_if_different ${THIRD_PARTY_LIBRARIES} ${LIBRARY_OUTPUT_PATH}
COMMAND
${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/script ${EXECUTABLE_OUTPUT_PATH}/script
)

ADD_SUBDIRECTORY(config)
ADD_SUBDIRECTORY(database)

INCLUDE_DIRECTORIES(
${CONFIG_INCLUDE_DIR}
${DATABASE_INCLUDE_DIR}
${THIRD_PARTY_PATH}/lib1/include
${THIRD_PARTY_PATH}/lib2/include
)

add_executable(app main.cpp)
target_link_libraries(app datasource config)

ADD_DEPENDENCIES(config copy_file)
ADD_DEPENDENCIES(datasource copy_file)

注意点:

  • ADD_SUBDIRECTORY之前配置的变量,在底层项目才会生效。所以根项目相关的配置可以放在ADD_DEPENDENCIES之后。
Cotin Yang wechat
欢迎订阅我的微信公众号 CotinDev
小小地鼓励一下吧~😘