东拼西凑把功能都实现了,回头看看CMakelist乱七八糟。把自己作为小白,整理下CMakelist的知识点并且优化一下本身的代码做一个记录。
目录
2. pkg-config 的 CFLAGS 写入 CMAKE_C_FLAGS 是冗余且错误的
3.删掉: ${GST_LIBRARIES} hv ${OpenCV_INCLUDE_DIRS}
1. ${GST_LIBRARIES} 没有被保留,是因为:
3. OpenCV_INCLUDE_DIRS 被误写到 target_link_libraries:
1.重复 pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)。
2.strEqualIgnoreCase函数根本没必要,可以使用cmake的add_compile_definitions。
1.为什么项目中会有多个 CMakeLists.txt(“大 CMakeLists” 和“小 CMakeLists”)
2. "小" CMakeLists.txt:子模块或子目录构建文件
2.为什么大小的 CMakeLists.txt 都用 pkg_check_modules?能不能写一起
一、子目录CMakelist.txt
1.原代码:
# configure a header file to pass some of the CMake settings to the source code
# 配置一个头文件,将部分 CMake 设置传入源码中
configure_file (
"${PROJECT_SOURCE_DIR}/src/versionConfig.h.in"
"${PROJECT_BINARY_DIR}/versionConfig.h"
)
# 设置 C 编译选项:无优化(-O0)、调试信息(-ggdb)、开启所有警告(-Wall)、保留帧指针(方便调试)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -ggdb -Wall -fno-omit-frame-pointer")
# 添加各个库对应的编译宏(来自 pkg-config 的 CFLAGS),确保头文件能正确识别
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GLIB_CFLAGS} ${GSTREAMER_CFLAGS} ${GSTREAMER_SDP_CFLAGS} ${GSTREAMER_WEBRTC_CFLAGS} ${JSON_GLIB_CFLAGS} ${LIBSOUP_CFLAGS}")
# 源文件列表,后续将用于创建静态库(add_library)
set(SOURCES
base/relay.cpp
base/panTilt.cpp
base/utils.cpp
base/video.cpp
base/sentinel.cpp
base/websocketlog.cpp
base/preset.cpp
base/database.cpp
base/preview.cpp
base/httpApi.cpp
base/ota.cpp
base/deviceInit.cpp
base/crashHandler.cpp
base/bird_man.cpp
router.cpp
)
# 编译可执行程序,主函数在 main.cpp,辅助源文件 handler.cpp
add_executable(${PROJECT_NAME} main.cpp handler.cpp)
# strip the excecutable file when CMAKE_BUILD_TYPE=release
# 当构建类型为 Release 时,strip 可执行文件(去除符号表以减小体积)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELEASE "-s")
# target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_lib ${CRYPTO_LIB})
# 将之前的源码编译为一个静态库,方便多个目标共享
add_library(${PROJECT_NAME}_lib ${SOURCES})
# 设置需要链接的库变量(方便统一管理)
set(EXTRA_LIBS
${GST_LIBRARIES}
${GST_RTSP_SERVER_LIBRARIES}
${GLIB_LIBRARIES}
${GSTREAMER_LIBRARIES}
${GSTREAMER_SDP_LIBRARIES}
${GSTREAMER_WEBRTC_LIBRARIES}
${JSON_GLIB_LIBRARIES}
${SOUP_LIBRARIES}
${OpenCV_LIBS}
${HV_LIB}
${HV_HCCORE}
${HV_HCNET}
)
# 将这些库链接到静态库中
target_link_libraries(${PROJECT_NAME}_lib
PUBLIC
sqlite3
pthread
curl
dl
crypto # sha256
${EXTRA_LIBS}
${GLIB_LIBRARIES}
${GSTREAMER_LIBRARIES}
${GSTREAMER_SDP_LIBRARIES}
${GSTREAMER_WEBRTC_LIBRARIES}
${JSON_GLIB_LIBRARIES}
${SOUP_LIBRARIES}
${HV_LIB}
${HV_HCCORE}
${HV_HCNET}
cudart nvdsgst_meta nvds_meta nvds_yml_parser curl hv ${OpenCV_INCLUDE_DIRS} /usr/lib/aarch64-linux-gnu
)
# 最终将库链接到主可执行程序 Laserbird
target_link_libraries(Laserbird
${PROJECT_NAME}_lib
${GSTREAMER_LIBRARIES} cudart nvdsgst_meta nvds_meta nvds_yml_parser curl hv ${OpenCV_INCLUDE_DIRS} /usr/lib/aarch64-linux-gnu
${GSTREAMER_LIBRARIES} ${GSTREAMER_APP_LIBRARIES}
)
2.理解:
CMAKE_C_FLAGS
:
CMAKE_C_FLAGS
是一个 CMake 内置变量,用于设置 C语言编译时的全局编译选项(flags)。
当在 CMake 中编译 .c
文件(C语言源文件)时,CMake 会将 CMAKE_C_FLAGS
中的内容作为参数传递给编译器(如 gcc
或 clang
),例如:
set(CMAKE_C_FLAGS "-O2 -Wall")
表示:所有用 C 编写的源文件在编译时都会加上 -O2 -Wall
。
3.可优化项/问题:
1. 手动设置 CMAKE_C_FLAGS
是低级用法
-
容易引发覆盖默认配置的行为。
-
和
target_compile_options()
、target_compile_definitions()
冲突。 -
会污染所有 target 的编译选项,难以维护。
推荐使用:
target_compile_options(${PROJECT_NAME} PRIVATE -O0 -ggdb -Wall -fno-omit-frame-pointer)
2. pkg-config
的 CFLAGS
写入 CMAKE_C_FLAGS
是冗余且错误的
CMake 本来就已经通过 pkg_check_modules()
得到了包含路径变量:
-
GLIB_INCLUDE_DIRS
-
GLIB_LIBRARY_DIRS
直接通过 include_directories()
和 link_directories()
来处理就够了。
不需要手动拼 -I/path
到 CMAKE_C_FLAGS
。
3.删掉: ${GST_LIBRARIES} hv ${OpenCV_INCLUDE_DIRS}
1. ${GST_LIBRARIES}
没有被保留,是因为:
在CMake 中,使用了:
pkg_check_modules(GST REQUIRED gstreamer-1.0)
这个会生成以下变量:
-
GST_LIBRARIES
-
GST_INCLUDE_DIRS
-
GST_CFLAGS
但在 EXTRA_LIBS
中,已经有了:
set(EXTRA_LIBS ${GST_LIBRARIES} ... )
并且最终 target_link_libraries(Laserbird)
已经包含了:
target_link_libraries(Laserbird Laserbird_lib ${EXTRA_LIBS} ... )
所以 GST_LIBRARIES
实际上是已经被链接进来了,就不需要单独再写一次(否则会重复)。
2. hv
不写,是因为已经用了变量 ${HV_LIB}
:
之前有写:
find_library(HV_LIB hv)
然后在 EXTRA_LIBS
和 target_link_libraries
里面都已经写了:
${HV_LIB}
所以再写一次 hv
是重复的,甚至可能引发链接冲突。
统一用变量 ${HV_LIB}
更清晰、可控,万一未来换路径也方便。
3. OpenCV_INCLUDE_DIRS
被误写到 target_link_libraries
:
target_link_libraries(Laserbird ... ${OpenCV_INCLUDE_DIRS} )
应该是:
target_link_libraries(Laserbird ... ${OpenCV_LIBS} )
因为:
-
OpenCV_INCLUDE_DIRS
是用于include_directories()
加头文件路径的; -
而
OpenCV_LIBS
才是链接 OpenCV 所需的库文件路径。
4.set(EXTRA_LIBS ...)
不用写
set(EXTRA_LIBS ...)
是为了方便统一管理多个库路径和名称,但不是必须的。可以不写 set(EXTRA_LIBS ...)
,直接把这些库展开写进 target_link_libraries(...)
,功能上没有区别。
set(EXTRA_LIBS a b c)等价于之后在 target_link_libraries()
中写:
target_link_libraries(my_target
a b c
)
#写set这样
set(EXTRA_LIBS
pthread
curl
dl
crypto
${OpenCV_LIBS}
)
target_link_libraries(${PROJECT_NAME}_lib
${EXTRA_LIBS}
)
# 不写就这样
target_link_libraries(${PROJECT_NAME}_lib
pthread
curl
dl
crypto
${OpenCV_LIBS}
)
问题总结
问题 | 描述 | 优化建议 |
---|---|---|
重复链接库 | 多次链接同一个库,比如 curl , hv , OpenCV_INCLUDE_DIRS (误用) | 只链接一次,整理到一个 target_link_libraries() 里 |
变量误用 | OpenCV_INCLUDE_DIRS 用于头文件,但把它当库用了 | 应使用 OpenCV_LIBS |
直接使用路径作为库 | /usr/lib/aarch64-linux-gnu 被直接放入 link 中 | 应通过 link_directories() 管理路径,或更好的是用 find_library() |
CMake 风格不统一 | 有些地方使用旧语法(比如 add_definitions ),不推荐 | 使用 target_compile_definitions 替代 |
CMake 可维护性差 | 所有逻辑都堆在一起 | 拆分成主 CMakeLists.txt 和子模块 src/CMakeLists.txt |
宏设置堆叠复杂 | 比如 CMAKE_C_FLAGS 多次 set | 合并设置逻辑,保持整洁 |
4.优化后代码:
# configure a header file to pass some of the CMake settings to the source code
# 配置一个头文件,将部分 CMake 设置传入源码中
configure_file (
"${PROJECT_SOURCE_DIR}/src/versionConfig.h.in"
"${PROJECT_BINARY_DIR}/versionConfig.h"
)
# # 设置 C 编译选项:无优化(-O0)、调试信息(-ggdb)、开启所有警告(-Wall)、保留帧指针(方便调试)
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -ggdb -Wall -fno-omit-frame-pointer")
# # 添加各个库对应的编译宏(来自 pkg-config 的 CFLAGS),确保头文件能正确识别
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GLIB_CFLAGS} ${GSTREAMER_CFLAGS} ${GSTREAMER_SDP_CFLAGS} ${GSTREAMER_WEBRTC_CFLAGS} ${JSON_GLIB_CFLAGS} ${LIBSOUP_CFLAGS}")
# # 给目标添加调试相关编译选项(只影响这个 target)
# target_compile_options(${PROJECT_NAME}_lib PRIVATE -O0 -ggdb -Wall -fno-omit-frame-pointer)
# target_compile_options(${PROJECT_NAME} PRIVATE -O0 -ggdb -Wall -fno-omit-frame-pointer)
# 源文件列表,后续将用于创建静态库(add_library)
set(SOURCES
base/relay.cpp
base/panTilt.cpp
base/utils.cpp
base/video.cpp
base/sentinel.cpp
base/websocketlog.cpp
base/preset.cpp
base/database.cpp
base/preview.cpp
base/httpApi.cpp
base/ota.cpp
base/deviceInit.cpp
base/crashHandler.cpp
base/bird_man.cpp
router.cpp
)
# target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_lib ${CRYPTO_LIB})
# 将之前的源码编译为一个静态库,方便多个目标共享
add_library(${PROJECT_NAME}_lib ${SOURCES})
# 目标库链接
target_link_libraries(${PROJECT_NAME}_lib
PUBLIC
sqlite3 pthread curl dl
crypto # sha256
${OpenCV_LIBS}
${GLIB_LIBRARIES}
${GSTREAMER_LIBRARIES}
${GSTREAMER_APP_LIBRARIES}
${GSTREAMER_SDP_LIBRARIES}
${GSTREAMER_WEBRTC_LIBRARIES}
${GST_RTSP_SERVER_LIBRARIES}
${JSON_GLIB_LIBRARIES}
${SOUP_LIBRARIES}
${HV_LIB}
${HV_HCCORE}
${HV_HCNET}
cudart
nvdsgst_meta
nvds_meta
nvds_yml_parser
)
# 编译可执行程序,主函数在 main.cpp,辅助源文件 handler.cpp
add_executable(${PROJECT_NAME} main.cpp handler.cpp)
#把名为 ${PROJECT_NAME}_lib 的库 链接到目标 ${PROJECT_NAME} 上,并且使用的是 PRIVATE 可见性。
target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_lib)
# strip the excecutable file when CMAKE_BUILD_TYPE=release
# 当构建类型为 Release 时,strip 可执行文件(去除符号表以减小体积)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELEASE "-s")
二、根目录CMakelist.txt
1.原代码:
# 禁止 in-source 构建,防止源码目录被污染(必须单独创建 build 目录)
if(" ${CMAKE_SOURCE_DIR}" STREQUAL " ${CMAKE_BINARY_DIR}")
message(FATAL_ERROR "
FATAL: In-source builds are not allowed.
You should create a separate directory for build files.
")
endif()
# 设定 CMake 最低版本为 3.10.0。
cmake_minimum_required(VERSION 3.10.0)
# 定义项目名 Laserbird,版本为 2.2.9。
project(Laserbird VERSION 2.2.9)
# 自定义函数 strEqualIgnoreCase,用于大小写无关字符串比较。
Function(strEqualIgnoreCase isEqual str1 str2)
string(TOLOWER ${str1} first)
string(TOLOWER ${str2} second)
string(COMPARE EQUAL "${first}" "${second}" equal)
set(${isEqual} ${equal} PARENT_SCOPE)
endfunction(strEqualIgnoreCase)
# Debug by default, strip the excecutable file when release
# 若未设置构建类型,则默认启用 Debug 模式并添加宏 DEBUG。
# 若设置了构建类型但是 Debug(不区分大小写),也添加宏 DEBUG。
# 最后打印当前的构建类型。
if (NOT CMAKE_BUILD_TYPE)
add_definitions(-DDEBUG)
# set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type" FORCE)
else()
strEqualIgnoreCase(isDebug ${CMAKE_BUILD_TYPE} "debug")
if (isDebug)
add_definitions(-DDEBUG)
endif()
endif()
message("buildtype: " ${CMAKE_BUILD_TYPE})
# 添加 JWT 开关,默认开启crypto。
# 如果开启了,就定义宏 JWT,并寻找 libcrypto。
# 若找不到则提醒。
option(JWT "Whether to enable JWT" ON)
if (${JWT})
add_definitions(-DJWT)
find_library(CRYPTO_LIB crypto REQUIRED)
if(NOT CRYPTO_LIB)
message("crypto not found")
endif()
endif()
# 设置最终可执行文件输出路径为 build/bin。
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
# 设置 CUDA 架构为 7.5(即支持 Turing 架构,如 RTX 20 系列)。
# 设置 CUDA 和 C++ 的标准为 C++14。
set(CMAKE_CUDA_ARCHITECTURES 75)
set(CMAKE_CUDA_STANDARD 14)
set(CMAKE_CXX_STANDARD 14)
# 加头文件搜索路径,例如 include 文件夹、${PROJECT_BINARY_DIR} 生成的 versionConfig.h 文件等。
include_directories(include "${PROJECT_BINARY_DIR}")
include_directories(/home/comnova)
# preview include
include_directories(/usr/include/glib-2.0)
include_directories(/usr/lib/aarch64-linux-gnu/glib-2.0/include)
include_directories(/usr/include/gstreamer-1.0)
include_directories(/usr/include/libsoup-2.4)
include_directories(/usr/include/json-glib-1.0)
# include_directories(/usr/lib/aarch64-linux-gnu/pkgconfig)
# 查找 libhv;
# 若找不到就假设使用源码方式编译 libhv,并添加其子目录。
find_library(HV_LIB hv)
if(NOT HV_LIB)
set(HV_LIB hv)
message("hv not found: compile with source code")
add_subdirectory(third_party/libhv)
endif()
# 查找 SDK 所需的 HCCore 和 HCNet 库。
find_library(HV_HCCORE HCCore REQUIRED)
if(NOT HV_HCCORE)
message("HCCORE not found")
endif()
find_library(HV_HCNET hcnetsdk REQUIRED)
if(NOT HV_HCNET)
message("HCNET not found")
endif()
# 添加额外的 CMake 模块查找路径(通常用于自定义 .cmake 文件)。
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# 查找所需的包
# 使用 pkg-config 工具查找并配置 GStreamer、RTSP、WebRTC、OpenCV、GLib 等依赖库。
find_package(PkgConfig REQUIRED)
pkg_check_modules(GST REQUIRED gstreamer-1.0)
pkg_check_modules(GST_RTSP_SERVER REQUIRED gstreamer-rtsp-server-1.0)
find_package(TensorRT REQUIRED)
find_package(OpenCV REQUIRED)
pkg_check_modules(GLIB REQUIRED glib-2.0)
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
pkg_check_modules(GSTREAMER_SDP REQUIRED gstreamer-sdp-1.0)
pkg_check_modules(GSTREAMER_WEBRTC REQUIRED gstreamer-webrtc-1.0)
pkg_check_modules(JSON_GLIB REQUIRED json-glib-1.0)
pkg_check_modules(SOUP REQUIRED libsoup-2.4)
set(LIB_INSTALL_DIR /opt/nvidia/deepstream/deepstream/lib/)
set(APP_INSTALL_DIR /opt/nvidia/deepstream/deepstream/bin/)
set(APP_INSTALL_ROOT /opt/nvidia/deepstream/deepstream)
# appsink appsrc
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
pkg_check_modules(GSTREAMER_APP REQUIRED gstreamer-app-1.0)
# 添加 appsrc/appsink 的头文件路径;
# 添加链接库路径。
include_directories(${GSTREAMER_APP_INCLUDE_DIRS})
link_directories(${GSTREAMER_LIBRARY_DIRS} ${GSTREAMER_APP_LIBRARY_DIRS})
# 包含头文件路径
include_directories(
${GLIB_INCLUDE_DIRS}
${GSTREAMER_SDP_INCLUDE_DIRS}
${GSTREAMER_WEBRTC_INCLUDE_DIRS}
${JSON_GLIB_INCLUDE_DIRS}
${SOUP_INCLUDE_DIRS}
${OpenCV_INCLUDE_DIRS}
/usr/local/cuda/include
${GSTREAMER_INCLUDE_DIRS}
${GST_RTSP_SERVER_INCLUDE_DIRS}
${APP_INSTALL_ROOT}/sources/includes
/usr/lib/aarch64-linux-gnu/
/home/comnova/Downloads/LFL/bird_man/includes
/home/comnova/Downloads/LFL/bird_man/includes/libhv/include
/home/comnova/bird_man/includes/libhv/include/hv
)
# 链接库路径
# 合并了各个依赖库和项目内部路径,供头文件查找使用。
link_directories(
${GLIB_LIBRARY_DIRS}
${GSTREAMER_LIBRARY_DIRS}
${GSTREAMER_SDP_LIBRARY_DIRS}
${GSTREAMER_WEBRTC_LIBRARY_DIRS}
${JSON_GLIB_LIBRARY_DIRS}
${SOUP_LIBRARY_DIRS}
${LIB_INSTALL_DIR}
${GST_RTSP_SERVER_LIBRARY_DIRS}
/usr/local/cuda/lib64
)
# 进入 src 子目录编译代码,src/CMakeLists.txt 必须存在。
add_subdirectory(src)
# 设置编译器选项
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GLIB_CFLAGS_OTHER} ${GSTREAMER_CFLAGS_OTHER} ${GSTREAMER_SDP_CFLAGS_OTHER} ${GSTREAMER_WEBRTC_CFLAGS_OTHER} ${JSON_GLIB_CFLAGS_OTHER} ${SOUP_CFLAGS_OTHER}")
2.理解:
-
防止 in-source 构建,保护源码。
-
明确设置最低 CMake 版本和项目版本。
-
使用自定义函数做大小写无关比较,逻辑清晰。
-
设置 Debug 默认选项,且根据不同构建类型设置宏。
-
使用
option
控制 JWT 功能,灵活配置。 -
利用
pkg_check_modules
查找依赖包,系统化管理依赖。 -
明确设置 CUDA 架构和语言标准。
-
清晰的
include_directories
和link_directories
。 -
处理第三方库 hv 的查找和源码编译。
-
组织结构清晰,利用子目录组织源代码。
3.可优化项:
1.重复 pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
。
2.strEqualIgnoreCase函数根本没必要,可以使用cmake的add_compile_definitions。
3.很多没有必要的,写死的自定义头文件路径
其他网关里面没有,会导致编译报错
/home/comnova/Downloads/LFL/bird_man/includes
/home/comnova/Downloads/LFL/bird_man/includes/libhv/include
/home/comnova/bird_man/includes/libhv/include/hv
4.有链接cuda库的话,最好告诉cmake
project(Laserbird VERSION 2.2.9 LANGUAGES C CXX CUDA)
4.优化后代码:
# 禁止 in-source 构建,防止源码目录被污染(必须单独创建 build 目录)
if(" ${CMAKE_SOURCE_DIR}" STREQUAL " ${CMAKE_BINARY_DIR}")
message(FATAL_ERROR "
FATAL: In-source builds are not allowed.
You should create a separate directory for build files.
")
endif()
# 设定 CMake 最低版本为 3.10.0。
cmake_minimum_required(VERSION 3.10.0)
# 定义项目名 Laserbird,版本为 2.2.9。有链接cuda库最好加上
project(Laserbird VERSION 2.2.9 LANGUAGES C CXX CUDA)
# 若未设置构建类型,则默认启用 Debug 模式并添加宏 DEBUG。
# 使用add_compile_definitions
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE)
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" build_type_lower)
if(build_type_lower STREQUAL "debug")
add_compile_definitions(DEBUG)
endif()
# 添加 JWT 开关,默认开启。如果开启了,就定义宏 JWT,并寻找 libcrypto。
option(JWT "Enable JWT support" ON)
if(JWT)
add_compile_definitions(JWT)
endif()
# 查找 libcrypto(JWT需要)
if(JWT)
find_library(CRYPTO_LIB crypto REQUIRED)
if(NOT CRYPTO_LIB)
message(WARNING "libcrypto not found, JWT might not work properly")
endif()
endif()
# 添加额外的 CMake 模块查找路径(通常用于自定义 .cmake 文件)。
# 用于查找TensorRT
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# 使用 pkg-config 工具查找并配置 GStreamer、RTSP、WebRTC、OpenCV、GLib 等依赖库。
find_package(PkgConfig REQUIRED)
pkg_check_modules(GST REQUIRED gstreamer-1.0)
pkg_check_modules(GST_RTSP_SERVER REQUIRED gstreamer-rtsp-server-1.0)
pkg_check_modules(GLIB REQUIRED glib-2.0)
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
pkg_check_modules(GSTREAMER_SDP REQUIRED gstreamer-sdp-1.0)
pkg_check_modules(GSTREAMER_WEBRTC REQUIRED gstreamer-webrtc-1.0)
pkg_check_modules(JSON_GLIB REQUIRED json-glib-1.0)
pkg_check_modules(SOUP REQUIRED libsoup-2.4)
# appsink appsrc
pkg_check_modules(GSTREAMER_APP REQUIRED gstreamer-app-1.0)
find_package(TensorRT REQUIRED)
find_package(OpenCV REQUIRED)
# 查找 libhv;若找不到就假设使用源码方式编译 libhv,并添加其子目录。
find_library(HV_LIB hv)
if(NOT HV_LIB)
set(HV_LIB hv)
message("hv not found: compile with source code")
add_subdirectory(third_party/libhv)
endif()
# 查找 SDK 所需的 HCCore 和 HCNet 库。
find_library(HV_HCCORE HCCore REQUIRED)
if(NOT HV_HCCORE)
message("HCCORE not found")
endif()
find_library(HV_HCNET hcnetsdk REQUIRED)
if(NOT HV_HCNET)
message("HCNET not found")
endif()
# 设置 CUDA 架构为 7.5(即支持 Turing 架构,如 RTX 20 系列),CUDA 和 C++ 的标准为 C++14。
set(CMAKE_CUDA_ARCHITECTURES 75)
set(CMAKE_CUDA_STANDARD 14)
set(CMAKE_CXX_STANDARD 14)
# 设置最终可执行文件输出路径为 build/bin。
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
# -------------------------------
# 没有使用这些变量的地方,cmake不会自动使用它们,除非显式调用
set(LIB_INSTALL_DIR /opt/nvidia/deepstream/deepstream/lib/)
set(APP_INSTALL_ROOT /opt/nvidia/deepstream/deepstream/sources/includes)
# 包含头文件路径
include_directories(
${GLIB_INCLUDE_DIRS}
${GSTREAMER_SDP_INCLUDE_DIRS}
${GSTREAMER_WEBRTC_INCLUDE_DIRS}
${JSON_GLIB_INCLUDE_DIRS}
${SOUP_INCLUDE_DIRS}
${OpenCV_INCLUDE_DIRS}
/usr/local/cuda/include
${GSTREAMER_INCLUDE_DIRS}
${GST_RTSP_SERVER_INCLUDE_DIRS}
${GSTREAMER_APP_INCLUDE_DIRS}
include "${PROJECT_BINARY_DIR}"
/home/comnova
# preview include
/usr/include/glib-2.0
/usr/lib/aarch64-linux-gnu/glib-2.0/include
/usr/include/gstreamer-1.0
/usr/include/libsoup-2.4
/usr/include/json-glib-1.0
${APP_INSTALL_ROOT}
)
# 统一链接库目录
set(COMMON_LINK_DIRS
${GLIB_LIBRARY_DIRS}
${GSTREAMER_LIBRARY_DIRS}
${GSTREAMER_SDP_LIBRARY_DIRS}
${GSTREAMER_WEBRTC_LIBRARY_DIRS}
${JSON_GLIB_LIBRARY_DIRS}
${SOUP_LIBRARY_DIRS}
${LIB_INSTALL_DIR}
${GST_RTSP_SERVER_LIBRARY_DIRS}
${GSTREAMER_APP_LIBRARY_DIRS}
/usr/local/cuda/lib64
)
# 将链接库路径加入全局(也可改为target_link_directories绑定具体目标)
link_directories(${COMMON_LINK_DIRS})
# 进入 src 子目录编译代码,src/CMakeLists.txt 必须存在。
add_subdirectory(src)
优点:
-
禁止 in-source 构建:防止污染源码。
-
编译类型控制:自动设置为 Debug 并定义宏,调试友好。
-
选项配置(如 JWT):支持可选组件,方便扩展。
-
依赖项查找清晰:利用
pkg_check_modules
管理依赖,清楚易改。 -
头文件和库路径配置全面:考虑到 DeepStream、GLib、GStreamer 等,覆盖全面。
-
src 子目录管理:模块化项目结构,推荐实践。
-
兼顾 CUDA 支持:未来如需集成加速,非常方便。
三、疑问点:
1.为什么项目中会有多个 CMakeLists.txt(“大 CMakeLists” 和“小 CMakeLists”)
1. "大" CMakeLists.txt:顶层构建文件
-
位于项目根目录(通常是项目的
Laserbird/
根目录)。 -
作用是整体配置整个工程(项目名、构建类型、全局依赖库、头文件目录等)。
-
它负责调用子目录构建,例如:
add_subdirectory(src)
2. "小" CMakeLists.txt:子模块或子目录构建文件
-
位于
src/
、src/base/
等子目录中。 -
专注于构建该子模块的目标,例如编译
.cpp
源文件、设置自己的依赖。 -
由“大的”CMakeLists 用
add_subdirectory()
加入。示例:
src/CMakeLists.txt
里可能会写:
add_library(${PROJECT_NAME}_lib
base/relay.cpp
base/utils.cpp
...
)
原因 | 说明 |
---|---|
模块化管理 | 每个模块(如 src/base)负责自己的构建逻辑,避免顶层文件过于臃肿。 |
可维护性强 | 项目增大后,逻辑拆分清晰,团队协作更容易。 |
可重用性 | 子模块可以单独构建/调试,甚至复用到别的项目。 |
递归构建 | CMake 会递归读取并构建每个 add_subdirectory() 加入的模块。 |
类型 | 路径 | 作用 |
---|---|---|
大 CMakeLists | 项目根目录 | 设置全局变量、项目名称、依赖、入口点等 |
小 CMakeLists | 子模块目录(如 src/ ) | 设置子模块源文件、编译规则、依赖、输出等 |
2.为什么大小的 CMakeLists.txt 都用 pkg_check_modules
?能不能写一起
-
顶层 CMakeLists.txt:用于总体管理项目(设置版本、子模块、全局变量等);
-
子模块 CMakeLists.txt(例如在
src/
、third_party/
中):每个模块需要自己依赖某些库,所以也用pkg_check_modules
。
能不能写在一起?
-
能,但不推荐所有都写在顶层。因为有些模块可能不需要全部依赖库,把所有
pkg_check_modules
写到顶层会导致不必要的依赖增加。 -
模块化设计推荐:
-
顶层只查找全局共享的库(如 glib);
-
每个子模块根据需要调用
pkg_check_modules
处理自身依赖(如 json-glib、opencv、gstreamer); -
再通过
target_include_directories()
和target_link_libraries()
精确添加依赖。
-