基于 Cmake 构建自己的工程——以Hi3519DV500为例
现在,作为一个海思音视频芯片开发的初学者,每次你都要打开 VSCode,进入长长的海思 SDK 路径,每次查看 mpp 文件夹下的各种 sample 时,查找引用时,都要忍受着各种乱七八糟的路径。
然后,当你想创建一个自己的工程时,一般来说,你会有三条路可以选:
- 直接在 mpp 下再开一个文件夹,秉着”拿来主义“的原则,仿照各种 sample 里的 makefile,直接开写。这么做前期确实省事,但是,你还是要忍受着各种乱七八糟的路径。
- 重新开一个文件夹,把各种海思专有的依赖库,头文件,第三方组件库,各种源码等分门别类地复制过去,然后开始写又臭又长的 Makefile。
- 同样开一个文件夹,把各种海思专有的依赖库,头文件,第三方组件库,各种源码等分门别类地复制过去,但是这次,你决定写一种叫做 CmakeLists.txt 的文件。
你一定受够了又臭又长的Makefile吧?————反正我是受够了。
1. 什么是 Cmake?
平时,我们编写一个 C/C++ 项目的时候,不同的源文件,头文件,第三方依赖库什么的,都可以通过 Makefile 来管理。
不过,一旦项目开始大起来,编写 Makefile 就成了一件非常麻烦的事情。
CMake 是一个开源的跨平台自动化建构系统,用来管理软件建置的程序,通过使用简单的配置文件 CMakeLists.txt,自动生成不同平台的构建文件。
CMake 本身不是构建工具,而是生成构建系统的工具,它生成的构建系统可以使用不同的编译器和工具链。
大名鼎鼎的 OpenCV,就是用 Cmake 作为构建工具的。
换句话说,我们平时是从 makefile 开始,执行 make 编译一个工程文件。
但是,CMakeLists.txt 替我们”简化“了这些 makefile,由这个 CMakeLists.txt 出发,去生成 makefile,再去通过 make 编译。
2. Cmake 安装(以 Ubuntu 18.04 下编译安装 3.16.5 为例)
sudo apt install -y build-essential libssl-dev
wget https://cmake.org/files/v3.16/cmake-3.16.5.tar.gz
tar -xzf cmake-3.16.5.tar.gz && cd cmake-3.16.5
./bootstrap --prefix=/usr/local
make -j8
sudo make install
将 export PATH=/usr/local/bin:$PATH 加入到 ~/.bashrc 中
然后 source ~/.bashrc 即可
之后输入 cmake --version 检查是否成功安装
3. 关于语法的简单介绍
毕竟我们这个不是专门的 Cmake 教程(笔者在这方面也绝非专业人士),这里只简明扼要的介绍笔者认为比较重要的一些编写 CmakeLists.txt 的语法。
指定 CMake 的最低版本要求:
cmake_minimum_required(VERSION 3.16)
定义项目的名称和使用的编程语言:
project(MyProject LANGUAGES C CXX)
# 这里项目名称为 MyProject,用了C和C++两种语言
指定要生成的可执行文件和其源文件:
add_executable(${PROJECT_NAME} main.c other_file.cpp)
# ${PROJECT_NAME} 是一个固定变量,由前面的 project 语句所定义
当源文件太多的时候,同时还在同一文件夹下,你可以定义一个变量,将他们储存:
aux_source_directory(${CMAKE_SOURCE_DIR}/source SRCLIST}
# 这里 ${CMAKE_SOURCE_DIR} 指你创建的 CmakeLists.txt 所在目录
# 这个 source 文件夹下所有可作为源代码的文件,都被存到了 SRCLIST 这个自定义变量
但是,在不同文件夹下,就不能存在一个变量里了吗?答案是否定的:
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/ SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/audio/ SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/serial/ SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/svp_npu/ SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/video_to_png/ SRCLIST)
这样,指定要生成的可执行文件和其对应源文件的时候,就可以这么写了:
add_executable(${PROJECT_NAME} ${SRCLIST})
或者有其他源文件变量的时候,也可以这样指定多个变量:
add_executable(${PROJECT_NAME} ${SRCLIST} ${SDK_COMMON_SRCLIST})
当然,一个项目怎么可能只有源文件,没有头文件呢?
include_directories(
"${CMAKE_SOURCE_DIR}/include"
"${CMAKE_SOURCE_DIR}/3rdparty/include"
)
# 指定了项目所有头文件的搜索路径,属于全局性质
而且,我们常常会遇到需要链接第三方库的时候:
link_directories(
${CMAKE_SOURCE_DIR}/3rdparty/lib/curl
${CMAKE_SOURCE_DIR}/3rdparty/lib/openssl
${CMAKE_SOURCE_DIR}/3rdparty/lib/opencv
)
# 指定了所有项目库文件的搜索路径
对于库来说,光指定路径可不行,还得知道要具体链接哪些才行:
set(SYSTEM_LINK_LIB
-lopencv_world
-lpthread
-lrt
-ldl
-lm
-lcurl
-lssl
-lcrypto
)
# 对 Makefile 稍有了解的都知道这个 -lxxx 意味着什么吧?
# 这样,就这么创建了一个自定义变量 SYSTEM_LINK_LIB,这个变量里储存了要链接的库
一些特殊的库,可以这样指定库的文件名:
set(MPI_LIBS
libss_mpi.a
libss_mpi_sysbind.a
libss_mpi_sysmem.a
libss_mpi_ive.a
)
现在,我们开始链接吧:
target_link_libraries(${PROJECT_NAME} ${MPI_LIBS} ${SYSTEM_LINK_LIB})
# 链接目标文件与其他库
我们编译好的可执行文件,总要有个去处吧?
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/output)
# 默认可执行文件输出变量,是一个固定变量
还有些其他的语法,以及一些现代 Cmake 语法,这里就不做赘述了,有兴趣的自行查阅。
4. 创建自己的项目
首先,创建一个空的项目文件夹————随你的个人喜好起名。
然后,像这样在这个文件夹下创建几个空文件夹,以及 CmakeLists.txt:
hi3519dv500_sdk/ # 各种海思专有的依赖库与对应的头文件,第三方库也一股脑放在这里
# 也有一些 mpp 相关的中间层代码,以及 isp、audio 相关的一些源码和头文件
include/ # 第三方库对应的头文件
output/ # 可执行文件输出的目录
source/ # 储存了源码(和对应的头文件)
CmakeLists.txt
hi3519dv500_sdk/ 下可以是这样:

其中:
audio/ 由 source/mpp/cbb/audio 复制过来,
common/ 由 source/mpp/sample/common 复制过来,是一些与 mpp 相关的中间层代码
isp/ 由 source/mpp/cbb/isp 复制过来,
lib/ 包括了所有的库文件,无论是第三方库还是海思的专有库,其中,海思的专有库从 source/out/lib 复制过来。
其他的第三方库根据需要自行选择。
这里还是建议第三方库单独出一个 3rdparty/ 目录,笔者这里纯粹是偷懒了
svp_acl_include/ 从 source/out/include 复制过来,全部都是 mpp、svp_npu 以及系统控制等方面相关。
include/ 里存放的都是第三方库对应的头文件,这个具体视情况而定,不做赘述。

注:这里
EasyRTSPServer/笔者实际并没有用到
source/ 里放的都是源码了,不过笔者还是将这里划为了两部分:

svp_common/ 部分从 mpp/sample/svp/common 复制过来,而 workfile/ 则是用户代码。
然后是 CmakeLists.txt 部分:
cmake_minimum_required(VERSION 3.10)
project(ChessRobot_App_Main LANGUAGES C CXX)
include(${CMAKE_SOURCE_DIR}/hi3519dv500.cmake)
# 这里就像C语言的头文件一样,引用了另一个文件
include_directories(
${CMAKE_SOURCE_DIR}/${PLATFORM}/common
${CMAKE_SOURCE_DIR}/${PLATFORM}/svp_acl_include
${CMAKE_SOURCE_DIR}/include/curl
${CMAKE_SOURCE_DIR}/include/openssl
${CMAKE_SOURCE_DIR}/include
# ${CMAKE_SOURCE_DIR}/include/EasyRTSPServer
${CMAKE_SOURCE_DIR}/${PLATFORM}/isp/include
${CMAKE_SOURCE_DIR}/${PLATFORM}/isp/user/3a/include
${CMAKE_SOURCE_DIR}/${PLATFORM}/audio/adp/include
${CMAKE_SOURCE_DIR}/source/svp_common
${CMAKE_SOURCE_DIR}/source/workfile/audio
${CMAKE_SOURCE_DIR}/source/workfile/serial
${CMAKE_SOURCE_DIR}/source/workfile/svp_npu
${CMAKE_SOURCE_DIR}/source/workfile/video_to_png
${CMAKE_SOURCE_DIR}/source/workfile/api_task
${CMAKE_SOURCE_DIR}/source/workfile/web_service
)
link_directories(
${CMAKE_SOURCE_DIR}/${PLATFORM}/lib/
${CMAKE_SOURCE_DIR}/${PLATFORM}/lib/libcurl
${CMAKE_SOURCE_DIR}/${PLATFORM}/lib/libopenssl
${CMAKE_SOURCE_DIR}/${PLATFORM}/lib/libopencv
# ${CMAKE_SOURCE_DIR}/${PLATFORM}/lib/EasyRTSPServer
)
aux_source_directory(${CMAKE_SOURCE_DIR}/${PLATFORM}/common/ SDK_COMMON_SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/${PLATFORM}/audio/adp/src/ SDK_COMMON_SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/ SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/audio/ SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/serial/ SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/svp_npu/ SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/video_to_png SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/web_service SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/workfile/api_task SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/source/svp_common/ SRCLIST)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/output)
add_executable(${PROJECT_NAME} ${SRCLIST} ${SDK_COMMON_SRCLIST})
target_link_libraries(${PROJECT_NAME} ${MPI_LIBS} ${SYSTEM_LINK_LIB})
读者肯定注意到了 include(${CMAKE_SOURCE_DIR}/hi3519dv500.cmake) 这一句,那么这个引用的文件里是什么呢?
set(PLATFORM hi3519dv500_sdk)
set(CMAKE_C_COMPILER aarch64-v01c01-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-v01c01-linux-gnu-g++)
set(CMAKE_STRIP aarch64-v01c01-linux-gnu-strip)
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_SYSTEM_NAME Linux)
set(MPI_LIBS
libss_mpi.a
libss_mpi_sysbind.a
libss_mpi_sysmem.a
libss_mpi_ive.a
libss_mpi_ae.a
libss_mpi_thermo.a
libss_mpi_isp.a
libot_mpi_isp.a
libss_mpi_awb.a
libdehaze.a
libextend_stats.a
libdrc.a
libldci.a
libbnr.a
libcalcflicker.a
libir_auto.a
libacs.a
libacs.a
libsns_os08a20.a
libsns_imx347_slave.a
libsns_os04a10.a
libsns_os04a10_slave.a
libsns_imx515.a
libsns_imx335.a
libsns_sc450ai.a
libsns_sc850sl.a
libsns_gst412c.a
libss_mpi_nuc.a
# CONFIG_OT_ISP_DUMP_DEBUG_SUPPORT
# libdump_dbg.a
# CONFIG_OT_AIISP_SUPPORT
# CONFIG_OT_AIDESTRIP_SUPPORT
libss_mpi_aidestrip.a
# CONFIG_OT_AIBNR_SUPPORT
libss_mpi_aibnr.a
# CONFIG_OT_AIDRC_SUPPORT
libss_mpi_aidrc.a
# CONFIG_OT_AI3DNR_SUPPORT
libss_mpi_ai3dnr.a
# CONFIG_OT_SVP_SUPPORT
# CONFIG_OT_SVP_SEC_SUPPORT = NO
libsvp_acl.a
libss_mpi_km.a
libprotobuf-c.a
# CONFIG_OT_AVS_LUT_SUPPORT
libss_stitch_lut.a
# CONFIG_OT_HEIF_SUPPORT
libheif.a
libfileformat.a
# CONFIG_OT_AUDIO_SUPPORT
libss_mpi_audio.a
libvoice_engine.a
libupvqe.a
libdnvqe.a
libss_mpi_audio_adp.a
libopus.a
libsecurec.a
# AAC
# libaac_dec.a
libaac_dec.so
# libaac_enc.a
libaac_enc.so
# libaac_comm.a
libaac_comm.so
# libaac_sbr_dec.a
libaac_sbr_dec.so
# libaac_sbr_enc.a
libaac_sbr_enc.so
# MP3
libmp3_dec.a
libmp3_enc.so
libmp3_lame.so
)
set(SYSTEM_LINK_LIB
-lopencv_world
-lpthread
-lrt
-ldl
-lm
-lcurl
-lssl
-lcrypto
)
add_definitions(
-DSENSOR0_TYPE=SONY_IMX335_MIPI_5M_30FPS_12BIT
-DSENSOR1_TYPE=SONY_IMX335_MIPI_5M_30FPS_12BIT
-DSENSOR2_TYPE=SONY_IMX335_MIPI_5M_30FPS_12BIT
-DSENSOR3_TYPE=SONY_IMX335_MIPI_5M_30FPS_12BIT
-DOT_ACODEC_TYPE_INNER
)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lstdc++ -mcpu=cortex-a53 -fno-aggressive-loop-optimizations -ldl -ffunction-sections -fdata-sections -O2 -fstack-protector-strong -fPIC -Wno-stringop-overflow")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE -pie -s -Wall -fsigned-char")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} ${O_FLAG} -std=c++11 -Wno-deprecated-declarations -ffunction-sections -fdata-sections -Werror -Wno-psabi -Wno-pointer-arith -Wno-int-to-pointer-cast -Wno-stringop-overflow"
)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
首先来看这部分:
set(PLATFORM hi3519dv500_sdk)
set(CMAKE_C_COMPILER aarch64-v01c01-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-v01c01-linux-gnu-g++)
set(CMAKE_STRIP aarch64-v01c01-linux-gnu-strip)
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
这里指定了编译器变量,编译平台的名称变量。
然后是
set(CMAKE_SYSTEM_PROCESSOR aarch64) #其实就是ARM64
set(CMAKE_SYSTEM_NAME Linux)
指定了系统平台,指令集。
MPI_LIBS 里的都是海思专有的一些库文件了,具体可以查阅 mpp/sample/Makefile.param 里的内容,视情况自行修改。
SYSTEM_LINK_LIB 则是一些系统库和第三方库。
然后是向程序源码添加的一些宏定义:
add_definitions(
-DSENSOR0_TYPE=SONY_IMX335_MIPI_5M_30FPS_12BIT
-DSENSOR1_TYPE=SONY_IMX335_MIPI_5M_30FPS_12BIT
-DSENSOR2_TYPE=SONY_IMX335_MIPI_5M_30FPS_12BIT
-DSENSOR3_TYPE=SONY_IMX335_MIPI_5M_30FPS_12BIT
-DOT_ACODEC_TYPE_INNER
)
前四行是指定 Sensor 型号的,最后一行指定音频编解码方式。
可以视情况添加其他的定义。
最后是这部分:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lstdc++ -mcpu=cortex-a53 -fno-aggressive-loop-optimizations -ldl -ffunction-sections -fdata-sections -O2 -fstack-protector-strong -fPIC -Wno-stringop-overflow")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE -pie -s -Wall -fsigned-char")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} ${O_FLAG} -std=c++11 -Wno-deprecated-declarations -ffunction-sections -fdata-sections -Werror -Wno-psabi -Wno-pointer-arith -Wno-int-to-pointer-cast -Wno-stringop-overflow"
)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
CMAKE_C_FLAGS 或 CMAKE_CXX_FLAGS 与 Makefile 里 C_FLAGS 作用一样,用来指定编译器选项。
最后两行指定 C++ 语言版本标准参照 C++11。
5. 构建与编译
然后,你写好了代码,想要构建之后再编译,只要在 CmakeLists.txt 所在目录执行 cmake ./ 即完成了构建。
之后,再执行 make 编译即可。
不过,像这样构建并编译结束之后,目录里会留下 CmakeFiles/ 的文件夹,目录下也会有 CmakeCache.txt、cmake_install.cmake 以及用过了的 Makefile 等”杂物“,想要对你的工程做出修改之后重新构建并编译,就要先把这些删掉。
你可以像这样写个脚本文件来处理这些事情:
#!/bin/sh
rm -r ./build/
rm ./output/ChessRobot_App_Main
rm -r ./CMakeFiles/
rm ./Makefile
rm ./CMakeCache.txt
rm cmake_install.cmake
# 之后重新构建并编译
cmake ./
make -j8

浙公网安备 33010602011771号