端到端ASR神器 WeNet系列 前沿讲解之CMake
【摘要】 端到端ASR神器 WeNet系列之前沿知识 CMake语法
Wenet是出门问问语音团队联合西工大语音实验室开源的一款面向工业落地应用的语音识别工具包。与ESPNet、K2不同,WeNet主要特点是小而精。它不仅采用了现阶段最陷阱的网络设计Conformer,还用到了U2结构实现流式与非流式框架的统一。此外,还有一套C++ Runtime推理代码,囊括了websocket和grpc两种部署方案,可快速帮助工程师们工程化部署。本系列文章将从模型训练、网络结构设计、C++推理三方面介绍。为了更好的在wenet基础上部署自己的模型,本篇文章将先介绍一下CMake的基本语法,并附上实战例题。
语法基础
-
add_library
- 该指令的主要作用就是将指定的源文件生成链接文件(如.a或.so),然后添加到工程中去。相当于g++ -C.
- 语法:
add_library(\<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [source1] [source2] [...])
- 实例:
add_library(utils STATIC utils/utils.cc)
- 说明: name表示库文件的名字,该库文件会根据命令里列出的源文件来创建。而STATIC、SHARED和MODULE的作用是指定生成的库文件的类型。STATIC库是目标文件的归档文件,在链接其它目标的时候使用。SHARED库会被动态链接(动态链接库),在运行时会被加载。MODULE库是一种不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数。
-
link_directories
- 主要是指定要链接的库文件的路径,该指令有时候不一定需要。因为find_package和find_library指令可以得到库文件的绝对路径。不过你自己写的动态库文件放在自己新建的目录下时,可以用该指令指定该目录的路径以便工程能够找到。
- 语法:
link_directories(lib)
- 说明: link_directories只是告诉CMake这个位置可能有lib,类似GCC里的-L的概念
-
target_link_libraries
- 将目标文件与库文件进行链接
- 语法:
target_link_libraries(<target> [item1] [item2] [...] [[debug|optimized|general] <item>] ...)
- 说明: 该指令中的target是指 通过add_executable()和add_library()指令生成已经创建的目标文件。而[item]表示库文件没有后缀的名字。默认情况下,库依赖项是传递的。当这个目标链接到另一个目标时,链接到这个目标的库也会出现在另一个目标的连接线上。
-
link_directories和target_link_libraries的区别:
- link_libraries用在add_executable之前,target_link_libraries用在add_executable之后
- link_libraries已被废弃了,建议使用target_link_libraries替代
-
include_directories
- 将指定目录添加到编译器的头文件搜索路径之下,指定的目录被解释成当前源码路径的相对路径。
- 语法:
include_directories ([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
- 实例:
include_directories( ${PROJECT_SOURCE_DIR} # 添加源代码的路径 ${CMAKE_SOURCE_DIR} # 添加makelist所在路径 sub) # 添加一些自定义头文件地址
- 说明:在每次调用
include_directories
命令时使用AFTER
或BEFORE
选项来指定是添加到列表的前面或者后面。如果使用SYSTEM
选项,会把指定目录当成系统的搜索目录。该命令作用范围只在当前的CMakeLists.txt。
-
add_dependencies
- target 依赖的其他 target,确保在编译本 target 之前,其他的 target 已经被构建。
- 说明:一般来说用不到。用到的情况就是两个targets有依赖关系(通过target_link_libraries解决)并且依赖库也是通过编译源码产生的。这时候一句add_dependencies可以在直接编译上层target时,自动检查下层依赖库是否已经生成。没有的话先编译下层依赖库,然后再编译上层target,最后link depend target。
-
FetchContent
- FetchContent提供ExternalProject相同的功能,在配置步骤之前下载依赖项。
- 使用技巧
// 参考代码 https://github.com/bewagner/fetchContent_example/blob/master/CMakeLists.txt cmake_minimum_required(VERSION 3.14) project(fetchContent_example CXX) include(FetchContent) # 加入FetchContent模块 # 然后用FetchContent_Declare()来注册依赖项 # 本例是从GitHub下载。也可以指定本地目录 FetchContent_Declare( DocTest GIT_REPOSITORY "https://github.com/onqtam/doctest" GIT_TAG "932a2ca50666138256dae56fbb16db3b1cae133a" ) FetchContent_Declare( Range-v3 GIT_REPOSITORY "https://github.com/ericniebler/range-v3" GIT_TAG "4d6a463bca51bc316f9b565edd94e82388206093" # 接下来调用FetchContent_MakeAvailable()。 该调用可确保cmake下载我们的依赖项,并添加其目录 FetchContent_MakeAvailable(DocTest Range-v3) add_executable(${PROJECT_NAME} src/main.cpp) # PROJECT_NAME=fetchContent_example target_link_libraries(${PROJECT_NAME} doctest range-v3)
-
get_filename_component
- 用法:
add_subdirectory
(source_dir [binary_dir] [EXCLUDE_FROM_ALL
])。添加一个子目录并构建该子目录 - 说明:指定的source_dir必须包含
CMakeLists.txt
文件和代码文件。binary_dir表示的是编译结果的输出路径。EXCLUDE_FROM_ALL
是指子目录下的目标不会被父目录下的目标文件包含进去,父目录的CMakeLists.txt
不会构建子目录的目标文件,必须在子目录下显式去构建。
- 用法:
实战例题
实战1:熟悉include_directories
、add_library
、add_executable
、target_link_libraries
和link_libraries
- 目录结构如下,其中demo.cc是主函数,util.h和util.cc分别提供接口和方法。
- CMakeLists.txt的内容
cmake_minimum_required(VERSION 3.18.2)
project(hello_word)
include_directories(
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/inc)
add_executable(demo demo.cc)
- 执行结果如下图
- 错误原因:没有为util.cc生成lib库
- 改进之后的CMakeLists.txt:add_library会生成静态库libutil.a,然后再生成可执行文件demo
cmake_minimum_required(VERSION 3.18.2)
project(hello_word)
include_directories(
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/inc)
add_library(util STATIC src/util.cc)
add_executable(demo demo.cc)
- 修改之后的输出。提示仍然找不到printList方法
- 继续修改
cmake_minimum_required(VERSION 3.18.2)
project(hello_word)
include_directories(
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/inc)
add_library(util STATIC src/util.cc)
add_executable(demo demo.cc)
target_link_libraries(demo PUBLIC util) # 新修改的一行
- 最终结果:
- 也可以通过SHARED生成动态库
cmake_minimum_required(VERSION 3.18.2)
project(hello_word)
include_directories(
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/inc)
add_library(util SHARED src/util.cc)
add_executable(demo demo.cc)
target_link_libraries(demo PUBLIC util)
- 除了使用target_link_libraries,还可以使用link_libraries:
- 如果我们需要把demo也打包成静态库:
实战2:了解add_subdirectory的用法 - 目录结构:子目录
sub
下的test.cpp
定义了一个函数test()
,将输入参数打印出来,相应的头文件test.h
则对test()
进行声明,CMakelists.txt
则将sub
下的源文件编译成库文件。
// sub/test.cpp
#include "test.h"
#include <iostream>
void test(std::string str)
{
std::cout << str << std::endl;
}
// sub/test.h
#include <string>
void test(std::string str);
# sub/CMakeLists.txt
cmake_minimum_required(VERSION 3.10.2)
project(sub)
add_library(sub test.cpp)
# 父目录下的CMakeLists.txt
cmake_minimum_required(VERSION 3.10.2)
project(test)
include_directories(sub)
add_subdirectory(sub output) # 把编译的二进制文件存入output文件夹中
add_executable(test main.cpp)
target_link_libraries(test sub)
# 父目录下的main.cpp
#include "test.h"
#include <iostream>
int main(int argc, char** argv)
{
std::cout << "In main..." << std::endl;
test("hello, world!");
return 0;
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)