CMakeLists.txt编写

目的:熟悉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

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇