CMakeLists 简单使用笔记
CMake
Created 2020.02.20 by William Yu; Last modified: 2021.03.31-V1.0.4
Contact: windmillyucong@163.com
Copyleft! 2021 William Yu. Some rights reserved.
References
- 《CMake Practice》
- https://cmake.org/cmake/help/latest/index.html
1.常用指令总结
设定cmake版本
1
cmake_minimum_required(VERSION 3.3)
project()
设定project名称,一个项目每个独立的模块都可以设置自己的project name
set()
指定自定义的变量,如
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
#### 创建一个 Library ####
project(module_lib)
# list of all the source files (headers and .ccs)
set(PROJECT_LIBRARY_SRCS # 添加库的所有源文件
"impl/a_function.cc"
"impl/b_function.cc"
"impl/c_function.cc"
)
# Include all the target headers. # 添加库的头文件目录
set(PROJECT_LIBRARY_HDRS
${PROJECT_SOURCE_DIR}/include
)
# Include all the depending libraries. # 添加库会用到的其他三方库
set(PROJECT_LIBRARY_LIBS
eigen
opencv
glog
)
# 使用刚刚定义的变量,创建一个共享库
# Create a shared library from this module.
user_add_library(${PROJECT_NAME}
"${PROJECT_LIBRARY_HDRS}" "${PROJECT_LIBRARY_SRCS}" "${PROJECT_LIBRARY_LIBS}")
# 函数的具体实现
# adds a named library in build targets, with included headers, sources and linked libraries.
function(USER_ADD_LIBRARY lib_name hdrs srcs link_libs)
# Add library build target
add_library(${lib_name} SHARED ${srcs})
# Target includes
target_include_directories(${lib_name} PRIVATE ${hdrs}
SYSTEM INTERFACE ${hdrs})
# Link libraries
target_link_libraries(${lib_name} PRIVATE ${link_libs})
endfunction()
message()
类似于print
1
2
3
4
5
General messages
message([<mode>] "message text" ...)
Reporting checks
message(<checkState> "message text" ...)
-
普通消息的输出
-
1
FATAL_ERROR
CMake Error, stop processing and generation.
-
1
SEND_ERROR
CMake Error, continue processing, but skip generation.
-
1
WARNING
CMake Warning, continue processing.
-
检查报告
-
1
STATUS
简单报告
-
1 2 3 4 5 6 7 8
CHECK_START Record a concise message about the check about to be performed. CHECK_PASS Record a successful result for a check. CHECK_FAIL Record an unsuccessful result for a check.
更详细的信息报告,将STATUS细分成了三类
-
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
message(CHECK_START "Finding my things") list(APPEND CMAKE_MESSAGE_INDENT " ") unset(missingComponents) message(CHECK_START "Finding partA") # ... do check, assume we find A message(CHECK_PASS "found") message(CHECK_START "Finding partB") # ... do check, assume we don't find B list(APPEND missingComponents B) message(CHECK_FAIL "not found") list(POP_BACK CMAKE_MESSAGE_INDENT) if(missingComponents) message(CHECK_FAIL "missing components: ${missingComponents}") else() message(CHECK_PASS "all components found") endif()
-
include()
添加
add_subdirectory()
添加子目录下的CMakeLists.txt
add_executable()
编译可执行文件
include_directories()
添加头文件路径
target_link_libraries()
链接库到目标文件
add_library()
编译库
function()
- https://cmake.org/cmake/help/latest/command/function.html?highlight=function
自定义函数
1
2
3
function(<name> [<arg1> ...])
<commands>
endfunction()
list()
1
list(APPEND LIBS ${TRIFO_PROJECT_LIBRARY_LIBS})
添加东西进列表
2.常用环境变量与关键字总结
目录
1
2
3
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR
这三个变量指代的内容是一致的,如果是 in source 编译,指得就是工程顶层目录。如果是 out-of-source 编译,指的是工程编译发生的目录
工程顶层目录
1
2
3
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR
这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。 也就是在 in source 编译时,他跟 CMAKE_BINARY_DIR 等变量一致。
当前目录
1
2
3
4
5
6
CMAKE_CURRRENT_BINARY_DIR
如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,
如果是 out-of-source 编译,他指的是 target 编译目录。
使用我们上面提到的 ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。
使用 SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。
cmak模块路径
CMAKE_MODULE_PATH
这个变量用来定义自己的 cmake 模块所在的路径。
如果你的工程比较复杂,有可能会自己编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理
CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设置一下。
比如
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
这时候你就可以通过 INCLUDE 指令来调用自己的模块了。
输出的可执行程序与库文件的路径
1
EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH
1
2
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/bin)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/lib)
系统信息
1
2
3
4
5
6
7
CMAKE_MAJOR_VERSION CMAKE 主版本号,比如 2.4.6 中的 2
CMAKE_MINOR_VERSION CMAKE 次版本号,比如 2.4.6 中的 4
CMAKE_PATCH_VERSION CMAKE 补丁等级,比如 2.4.6 中的 6
CMAKE_SYSTEM 系统名称,比如 Linux-2.6.22
CMAKE_SYSTEM_NAME 不包含版本的系统名,比如 Linux
CMAKE_SYSTEM_VERSION 系统版本,比如 2.6.22
CMAKE_SYSTEM_PROCESSOR 处理器名称,比如 i686
3.库的生成与使用
code example:
- 3.create_lib
- 4.use_lib
库的生成需要头文件.h和源代码.cpp
1
2
3
4
5
6
7
8
include_directories(${PROJECT_SOURCE_DIR}/include/) # 添加头文件搜索路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin/) # 指定编译生成的可执行程序的位置
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib/) # 指定编译生成的库的输出位置 ./build/lib
set(LIBTEST_SRC a_function.cc) # 指定库的源码
add_library(test_shared SHARED ${LIBTEST_SRC}) # 编译共享库libtest_shared.so
add_library(test_static STATIC ${LIBTEST_SRC}) # 编译静态库libtest_static.a
库的使用需要载入头文件和lib
1
2
3
4
5
6
7
8
9
10
11
12
13
14
include_directories(${PROJECT_SOURCE_DIR}/include/) # 添加头文件搜索路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin/) # 指定编译生成的可执行程序的位置
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib/) # 指定编译生成的库的输出位置 ./build/lib
set(LIBTEST_SRC a_function.cc) # 指定库的源码
add_library(test_shared SHARED ${LIBTEST_SRC}) # 编译共享库libtest_shared.so
add_library(test_static STATIC ${LIBTEST_SRC}) # 编译静态库libtest_static.a
add_executable(main main.cpp) # 编译二进制目标文件
target_link_libraries(main test_shared) # 将共享库链接到二进制文件
add_executable(main_static main.cpp) # 编译二进制目标文件
target_link_libraries(main_static test_static) # 将静态库链接到二进制文件
4.载入三方库
4.1. 直接载入
code example
- 5.find_sys_lib
若清楚库的头文件和lib所在位置,直接include进来即可
1
2
include_directories(/usr/include) # 包含库的头文件
target_link_libraries(目标文件名 库名XXX) # 查找动态库 libXXX.so 并链接
4.2. 查询载入,使用命令 find_package
若不清楚库头文件和lib的位置,可以使用查询载入
- 两种搜索模式
- Module模式
- Config模式
- 默认采取Module模式
- Module 没有找到库,才会采取Config模式
4.2.1. Module模式
code example
- 5.find_sys_lib
基本原理
-
查找 FindXXX.cmake 文件
-
查找的路径:CMAKE_MODULE_PATH 默认
/usr/share/cmake-3.5/Modules/
-
FindXXX.cmake 文件内部通常提供这几个变量:
1 2 3
<name>_FOUND # 查找标志,找到为TRUE <name>_INCLUDE_DIR or <name>_INCLUDES # 库的头文件 <name>_LIBRARY or <name>_LIBRARIES # 库
补充笔记:cmake内置的模块信息
-
cmake内置了很多模块的信息
-
查看CMake支持的模块
1
cmake --help-module-list
-
cmake 内置模块信息搜索路径为
1 2
/usr/share/cmake/Modules/ /usr/share/cmake-3.5/Modules/
里面有大量内置模块的Findxxx.Cmake
引入外部库的命令
以curl 库为例:
1
2
3
4
5
6
7
FIND_PACKAGE(CURL)
IF(CURL_FOUND)
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE(CURL_FOUND)
MESSAGE(FATAL_ERROR "CURL library not found")
ENDIF(CURL_FOUND)
补充:FindXXX.cmake文件究竟长啥样子呢?
code example:
- 6.user_lib_with_Fingcmake
如果我是库的开发人员,应该如何为我的库编写FindXXX.cmake 文件?
很简单,提供给调用者三个参数即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 简单的Findxxx.cmake文件 提供三个参数:
# - 头文件路径
# - 库文件路径
# - XXX_FOUND标志位
find_path(HELLO_INCLUDE_DIR # 确认头文件
a_function.h
${PROJECT_SOURCE_DIR}/include/)
message(".h dir: ${HELLO_INCLUDE_DIR}")
find_library(HELLO_LIBRARY # 确认库文件
libtest_shared.so
${PROJECT_SOURCE_DIR}/lib/)
message("lib dir: ${HELLO_LIBRARY}")
if(HELLO_INCLUDE_DIR AND HELLO_LIBRARY) # 设置xxx_FOUND
set(HELLO_FOUND TRUE)
endif()
4.2.2. Config模式
基本原理
-
查找 XXXConfig.cmake 文件
-
搜索路径:
1
cd /usr/local/lib/cmake/
-
XXXConfig.cmake文件提供:
1 2
XXX_INCLUDE_DIRS XXX_LIBRARIES
-
如果XXX_DIR路径下找不到XXXConfig.cmake文件,则会去/usr/local/lib/cmake/XXX/中查找XXXConfig.cmake文件
-
通常安装库的时候会拷贝一份xxxConfig.cmake文件到系统目录中
-
和4.2.1.Module模式差不多,只是文件写法不一样,文件放置的位置也不一样
-
个人感觉没有Module模式常用
引入外部库的命令
-
如果xxx没有安装在系统目录,无法自动找到xxxConfig.cmake,可以手动引入
1
set(xxx_DIR <real_path_to_dir_of_xxxConfig.cmake>) # 添加xxxConfig.cmake所在路径
-
然后再FIND_PACKAGE即可
5. 命令行参数
1
-D<键>=<值> -D<键>=<值>
e.g.:
1
cmake .. -DLIBRARY_TYPE=SHARED -DCMAKE_BUILD_TYPE=Release
CMakeLists.txt里面接收命令行参数
- 判断相等的写法:MATCHES
1
2
3
4
5
6
7
8
9
if(LIBRARY_TYPE MATCHES "SHARED")
add_library(test_shared SHARED ${LIBTEST_SRC}) # 编译共享库libtest_shared.so
add_executable(main main.cpp) # 编译二进制目标文件
target_link_libraries(main test_shared) # 将共享库链接到二进制文件
elseif(LIBRARY_TYPE MATCHES "STATIC")
add_library(test_static STATIC ${LIBTEST_SRC}) # 编译静态库libtest_static.a
add_executable(main_static main.cpp) # 编译二进制目标文件
target_link_libraries(main_static test_static) # 将静态库链接到二进制文件
endif()
6.其他
设置c++ 版本
1
2
3
4
# 下面这三行是设置编译的时候采用c++14标准,默认是11标准
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
查看编译速度
1
2
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time")
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CMAKE_COMMAND} -E time")
编译速度优化
Contact
Feel free to contact me windmillyucong@163.com anytime for anything.