目的:熟悉CMakeLists.txt的编写,规范化编程
说明:此内容为总结爱编程的大丙的CMake保姆及教程,特别感谢爱编程的大丙,原博客地址:https://subingwen.cn/cmake/CMake-primer/#2-7-%E6%97%A5%E5%BF%97
命令解析
cmake_minimum_required
指定使用的cmake的最低版本
示例:
cmake_minimum_required(VERSION 3.10)
project
定义工程名称,并可以指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
用法:
project(< PROJECT-NAME >
[VERSION < major >[.< minor >[.< patch >[.< tweak >]]]]
[DESCRIPTION < project-description-string >]
[HOMEPAGE_URL < url-string >]
[LANGUAGES < language-name >…])
示例:
project(project_test)
add_executable
添加一个可执行文件
用法:
add_executable(可执行程序名 源文件名称)
- 可执行程序名和project中的项目名没有任何关系
- 源文件名称可以有多个,用空格或者分号隔开
示例:
add_executable(main main.c threadpool.c)
target_compile_features
用于指定某个目标编译时所需的编译器特性
用法:
target_compile_features(< target > <PRIVATE|PUBLIC|INTERFACE> < feature > […])
- < target >:必须时已经定义的执行文件或库
- <PRIVATE|PUBLIC|INTERFACE>:特性的传递性
- < feature >:具体的特性标签
示例:
add_library(my_lib lib.cpp)
# 设为 PUBLIC:意味着“我用 C++17 开发,你也得用 C++17 才能调我”
target_compile_features(my_lib PUBLIC cxx_std_17)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE my_lib)
set
定义或修改变量,存储路径、文件名、编译选项或开关状态等
用法:
set(< variable > < value >… [PARENT_SCOPE])
- < variable >:变量的名称(通常约定使用大写,如OPENCVLIB)
- < value >:变量的值。可以时一个或多个,如果是多个值,它们会链接成一个列表(用空格或分号分隔)
- PARENT_SCOPE:可选参数。如果设置了此项,变量的作用域将提升到父级作用域(通常用于子目录向主目录传递变量)
示例:
set(SRC_LIST main.cpp threadpool.cpp)
在使用定义的变量的时候通过${}引用,${SRC_LIST} == main.cpp tool.cpp
aux_source_directory
查找某个路径下的所有源文件
用法:
aux_source_directory(< dir > < variable >)
- dir:要搜索的目录
- variable:将从dir目录下搜索到的源文件列表存储到该变量中
示例:
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
file
用法:
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
- GLOB:将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中
- GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中
示例:
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CAMKE_CURRENT_SOURCE_DIR}/include/*.h)
include_directories
查找头文件,告诉编译器从哪里寻找头文件(旧版),在现代cmake中不再建议使用,使用下面的 targrt_include_directorise 是更好的选择
用法:
include_directories(headpath)
示例:
include_directories(${PROJECT_SOURCE_DIR}/include)
targrt_include_directorise
用于为指定的编译目标添加头文件包含目录,他取代了旧式的指令include_directories
用法:
target_include_directories(< target > [SYSTEM] [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1…]
[<INTERFACE|PUBLIC|PRIVATE> [items2…] …])
优势:
- 精确绑定:头文件路径直接绑定在目标上,而不是全局生效
- 依赖传递:通过 INTERFACE|PUBLIC|PRIVATE 关键字,可以控制这些路径是否会传递给链接该库的其他目标
示例:
add_library(math_lib math.cpp)
# 为库设置包含路径
target_include_directories(math_lib PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include"
)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE math_lib)
# 结果:由于使用了 PUBLIC,app 在编译时会自动包含 math_lib 的 include 目录
add_library
制作动态库和静态库
制作静态库
用法:
add_library(库名称 STATIC 源文件1 [源文件2]…)
生成的静态库名称由三部分组成:lib + 库名称 + .a ,在这里只需要给出库名称即可,另外两部分会在生成该文件的时候自动填充
示例:
add_library(calc STATIC ${SRC_LIST})
制作动态库
用法:
add_library(库名称 SHARED 源文件1 [源文件2]…)
生成的动态库名称由三部分组成:lib + 库名称 + .so ,在这里只需要给出库名称即可,另外两部分会在生成该文件的时候自动填充
示例:
add_library(calc SHARED ${SRC_LIST})
特别说明:
设置动态库的生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/include)
在指定静态库生成的路径的时候不能使用EXECUTABLE_OUTPUT_PATH 宏,而应该使用LIBRARY_OUTPUT_PATH,这个宏对应静态库文件和动态库文件都适用
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/include)
link_libraries
链接库文件,系统提供的动态库或自己制作的动态库或静态库文件
link_libraries
链接静态库
用法:
link_libraries(< static lib > [< static lib >…])
- 参数:指定出要链接的静态库的名字,可以是全名libxxx.a或者是直接xxx
target_link_libraries
链接动态库
用法:
target_link_libraries(
< target >
< PEIVATE|PUBLIC|INTERFACE> < item >…
[< PEIVATE|PUBLIC|INTERFACE> < item >…]…)
- target:指定要加载的库文件的名字
- 可能是一个源文件
- 可能是一个动态库/静态库文件
- 可能是一个可执行文件
- PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC
- 如果各个动态库之间没有依赖关系,无需做任何设置,三者没有区别,在单cmake文件下,无需指定,使用默认的PUBLIC即可
- 只有在动态库的链接具有传递性的时候才要设置,如果动态库A链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了库B、C,并且可以使用动态库B、C中定义的方法。
- PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。
- PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调用了什么库。
- INTERFACE:在interface后面引入的库不会被连接到前面的target中,只会导出符号
特别提醒
动态库的链接和静态库是完全不同的:
- 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了
- 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存
所以,在cmake中指定要链接的动态库的时候,应该将命令写道生成了可执行文件之后。
示例:
add_exectuable(main ${SRC_LIST})
target_link_libraries(app pthread) # 要保证这里的链接动态库在生成可执行文件之后
link_directories
如果该静态库不是系统提供的(自己制作或者使用第三方提供的静态库)可能会出现静态库找不到的情况,此时可以将静态库的路径也指定出来
用法:
link_directories(< lib path >)
示例:
link_directories(${PROJECT_SOURCE_DIR}/lib)
链接库总结
target_link_libraries
用于指定一个目标(如可执行文件或库)在编译时需要连接那些库,它支持指定库的名称、路径以及链接库的顺序
用法:
target_link_libraries(target_name (target_name [item1 [item2 […]]] [<debug|optimized|general> < lib1 > [< lib2 > […]]])
优点:
- 更精确的控制目标的链接库
- 可以指定库的不同连接条件(如调试版本、发布版本)
- 支持多个目标和多个库之间的复杂关系
- 更加灵活和易于维护
示例:
add_executable(my_exectuable main.cpp)
target_link_libraries(my_executable PRIVATE my_dynamic_library)
link_libraries
用于设置全局连接库,这些库会连接到之后定义的所有目标上,他会影像所有的目标,适用于全局设置,但不如前面精确
用法:
link_libraries(lib1 lib2 […])
缺点:
- 缺乏针对具体目标的控制,不适合复杂的项目结构
- 容易导致以外的依赖关系,因为他对所有目标都生效
- 一旦设置,全局影响可能导致难以追踪的链接问题
示例:
link_libraries(my_static_library)
add_executable(my_executable main.cpp)
所以,target_link_libaries是更好的链接库的方式,他能更精确的控制和管理链接库的依赖
message
输出日志,主要使用在调试阶段
用法:
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] “message to display” …)
- (无):重要信息
- STATUS:非重要信息
- WARNING:CMake警告,会继续执行
- AUTHOR_WARNING:CMake警告(dev),会继续执行
- SEND_ERROR:CMake错误,继续执行,但是会跳过生成的步骤
- FATAL_ERROR:CMake错误,终止所有处理过程
示例:
message(STATUS "source path: ${PROJECT_SOURCE_DIR}") # 输出一般日志信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")# 输出警告信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")# 输出错误信息
set
对字符串进行拼接
用法:
set(变量名1 ${变量名1} ${变量名2} …)
将从第二个参数开始往后所有的字符串进行拼接,最后将结果存储到第一个参数中,如果第一个参数中原来有数据会对原数据进行覆盖
示例:
cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 拼接
set(SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}")
list
APPEND 拼接
用法:
list(APPEND < list > [< element >…])
list也可以对字符串进行拼接,但是他的功能比set更强大,字符串拼接只是它的其中一个功能,所以需要在它第一个参数的位置指定出我们要做的操作
APPEND:表示进行数据追加,后边的参数和set一样
示例:
cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}")
REMOVE_ITEM 移除
用法:
list(REMOVE_ITEM < list > < value > [< value >…])
示例:
file(GLOB SRC_1 ${PROJECT_SORCE_DIR}/*.cpp)
#移除main.cpp
list(REMOVE_ITEM SRC_1 ${PROJECT_SOURCE_DIR}/main.cpp)
注意:通过file命令搜索源文件的时候得到的是文件的绝对路径(在list中每个文件对应的路径都是一个item,并且都是绝对路径),那么在移除的时候也要将该文件的绝对路径指定出来才可以,否则移除操作不会成功
add_definitions
自定义宏 (全局)
用法:
add_definitions(-D宏名称)
示例:
add_definitions(-DDEBUG)
target_compile_definitions
自定义宏(局部),可以控制只在特定的目标(或链接该库的生成)中生效
set_target_properties
用于设置一个或多个目标的特定属性,是定制化构建流程最直接的方式
用法:
set_target_properties(target1 target2 …
PROPERTIES prop1 value1
prop2 value2 …)
- target1/2:必须是已经通过add_exectuable 或 add_library 定义的目标名称
- PROPERTIES:关键字,标志着属性名-值对列表的开始
可以修改生成文件的名称、存放路径及后缀,还可以为动态库设置版本号和兼容版本
常用属性:
- OUTPUT_NAME:更改生成的二进制文件名
- RUNTIME_OUTPUT_DIRECTORY:指定可执行文件的生成目录
- DEBUG_POSTFIX:为调试版生成的文件添加后缀
示例:
add_exectuable(my_app main.cpp)
# 设置 my_app 的属性
set_target_properties(my_app PROPERTIES)
OUTPUT_NAME "FinalGame" # 生成的文件名为FinalGame
CXX_STANDARD 17 # 该目标使用的 C++17标准
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" # 放到 bin 目录下
find_package
用于查找并加载外部项目的配置,它通过查找系统中已有的 .cmake 文件,将第三方库的信息导入到当前工程中
用法:
find_package(< PackageName > [version] [EXACT] [QUIET] [MODULE] [REQUIRED] [[COMPONENTS] [components…]] [OPTIONAL_COMPONENTS components…] [NO_POLICY_SCOPE])
- PackageName:要被查找的包名(区分大小写,如OpenCV、Qt5…)
- REQUIRED:表示该包是必须的。如果找不到,CMake会直接报错并停止配置
- COMPONENTS:指定包中的特定组件(例如Qt中的Widgets、Network)
导入目标:它会生成像OpenCV::OpenCV 这样的目标名,可以像使用自己的库一样链接它
标准化变量:自动定义一下变量
<PackageName>_FOUND:是否找到库<PackageName>_INCLUDE_DIRS:头文件路径<PackageName>_LIBRARIES:库文件路径
示例:
# 1. 查找 OpenCV,要求必须找到,且版本至少为 4.0
find_package(OpenCV 4.0 REQUIRED)
# 2. 如果找到了,OpenCV 会提供其头文件和库信息
if(OpenCV_FOUND)
message(STATUS "OpenCV found: ${OpenCV_VERSION}")
add_executable(my_app main.cpp)
# 3. 现代写法:直接链接“导入目标”
# 这会自动处理头文件路径和链接库,你甚至不需要 target_include_directories
target_link_libraries(my_app PRIVATE opencv_java4) # 或者 OpenCV_LIBRARIES
endif()
宏
PROJECT_SOURCE_DIR
使用cmake命令后面紧跟的目录,一般是工程的根目录
PROJECT_BINARY_DIR
执行cmake命令的目录
CMAKE_CURRENT_SOURCE_DIR
表示当前访问的CMakeLists.txt文件所在的路径
CMAKE_CURRENT_BINARY_DIR
target编译目录
EXECUTABLE_OUTPUT_PATH
指定可执行程序输出的路径
示例:
set(OUTPUT_PATH /home/kzzx/cmake_test)
set(EXECUTABLE_OUTPUT_PATH ${OUTPUT_PATH}/bin)
LIBRARY_OUTPUT_PATH
重新定义目标链接库文件的存放位置
PROJECT_NAME
返回通过PROJECT指令定义的项目名称
CMAKE_BINARY_DIR
项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径
CMAKE_CXX_STANDARD
指定c++标准
示例:
set(CMAKE_CXX_STANDARD 11) #-std=c++11
示例
在实际的项目编译过程中,一般不会直接在根目录下cmake,而是会创建一个build文件夹,在build文件夹下cmake .. 然后再make
cmake_minimum_required(VERSION 3.10)
project(rtsp_yolo11_display CXX)
set(CMAKE_BUILD_TYPE Debug)
# Set C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
# Utils directory
set(UTILS_DIR /app/cdev_demo/bpu/utils)
set(UTILS_SRC_DIR ${UTILS_DIR}/src)
# Collect utils source files (support both .cc and .cpp)
file(GLOB UTILS_SOURCES CONFIGURE_DEPENDS
${UTILS_SRC_DIR}/*.cc
${UTILS_SRC_DIR}/*.cpp
)
# Add macro definition
add_definitions(-D__STDC_CONSTANT_MACROS)
# Include directories
include_directories(
/usr/hobot/include
/usr/include
/app/cdev_demo/bpu/utils/inc
/usr/include/hobot/dnn
/usr/include/hobot
yolo/inc/
)
# Link directories
link_directories(/usr/hobot/lib)
# Find FFmpeg libraries (automatically adds dependencies and include paths)
find_package(PkgConfig REQUIRED)
find_package(OpenCV REQUIRED)
find_package(OpenMP REQUIRED)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui)
include_directories(${OpenCV_INCLUDE_DIRS})
pkg_check_modules(FFMPEG REQUIRED IMPORTED_TARGET
libavformat
libavcodec
libavutil
)
# Link libraries
SET(LINK_libs dnn hbucp gflags fmt spcdev OpenMP::OpenMP_CXX ${OpenCV_LIBS})
# Add executable
add_executable(rtsp_yolo11_display main.cc
yolo/src/ultralytics_yolo11.cc
${UTILS_SOURCES}
mainwindow.cpp
mainwindow.h
)
# Link required libraries
target_link_libraries(rtsp_yolo11_display
spcdev
PkgConfig::FFMPEG
${LINK_libs}
Qt5::Widgets
Qt5::Gui
Qt5::Core
gflags
)
cd build -> cmake .. -> make -j4










