文章目录
1. 前述
OK,开始写第二篇的内容了。这篇博客主要能写一下:
- 如何给一些三方库按照
xmake
方式进行封装,供调用 - 如何按照
xmake
构建rknn_engine
2. 三方库的封装
这个三方库的封装,主要分为如下两类:
- 类似
opencv
这种在xmake
的xrepo
中已经包含的库 - 类似
rknnrt
这种纯粹的三方库,完全需要我们进行处理的库
2.1 xrepo
中的库
比如Eigen
和opencv
库,我们可以直接使用xrepo
中的库,比如直接:
add_requires("opencv", "eigen3")
在使用xmake
的时候,可以直接通过网络下载依赖库,然后进行处理。但是,一般我们在rk3588上都会通过apt
安装了libopencv-dev
这些,所以我还是建议直接使用系统库,避免库的版本问题,所以就是如之前博客说的:
add_requires("cmake::OpenCV", {alias = "opencv", system = true})
add_requires("cmake::Eigen3", {alias = "eigen3", system = true})
但是,还有一个问题,如果我们不是在rk3588上进行编译,而是通过交叉编译呢?使用交叉编译的时候,xmake
能够自动调用交叉编译器进行opencv
的编译,然后得到一个可被调用的库,但是!opencv
这种库很有可能编译失败,依赖的ffmpeg
等等库太多了,那么有没有优雅的解决方法呢?
其实有现成的
aarch64
的opencv
库,我们可以把这个库直接拿下来,看做是rknnrt
一样处理!
2.2 xrepo
之外的库
使用rk3588,调用其npu,必然要用rk官方的rknnrt
和librga
库,然而xrepo
中并不收纳这些库,所以我们就需要自己构建。当我们拿到头文件和库文件之后,我们就可以按照如下的目录结构进行处理:
./xmake_repo
└── packages
├── e
│ └── eigen3
│ ├── include
│ │ └── Eigen
│ │ ├── Eigen的各种头文件
│ └── xmake.lua
├── l
│ └── librga
│ ├── cross
│ ├── linux
│ │ └── arm64
│ │ ├── include
│ │ └── lib
│ └── xmake.lua
├── o
│ └── opencv
│ ├── cross
│ ├── linux
│ │ └── arm64
│ │ ├── bin
│ │ ├── include
│ │ ├── lib
│ │ └── share
│ └── xmake.lua
├── r
│ └── rknnrt
│ ├── cross
│ ├── linux
│ │ └── arm64
│ │ ├── include
│ │ └── lib
│ └── xmake.lua
└── s
└── spdlog
├── include
│ └── spdlog
│ ├── sdplog的各种头文件
└── xmake.lua
当然细心的我们发现了,我并不是每个库的目录结构都一样,而且有的目录下面还有一个cross
,这个其实是一个软链接,都是对应链接同目录的linux
目录。但是有一个非常相同,每个目录下都有一个xmake.lua
,这将是保证xmake
能够找到库的核心文件。
这里我就以三个比较代表性的说明一下。
2.2.1 opencv
这个opencv
主要就是在交叉编译的时候使用的,如果在rk3588平台上直接编译,则不使用我们自己处理的这个,所以这里的cross
是我们最终使用的文件(虽然最终还是软链接到了linux
)……
那么xmake.lua
可以这样写:
package("opencv")
set_description("The opencv package")
add_configs("shared", {description = "Build shared library.", default = true, type = "boolean", readonly = true})
on_load(function (package)
package:set("installdir", path.join(os.scriptdir(), package:plat(), package:arch()))
end)
on_fetch(function (package)
local result = {}
local libfiledir = (package:config("shared") and package:is_plat("windows", "mingw")) and "bin" or "lib"
result.links = {"opencv_core", "opencv_video", "opencv_videoio"}
result.linkdirs = package:installdir("lib")
result.includedirs = package:installdir("include/opencv4")
result.libfiles = {
path.join(package:installdir(libfiledir), "libopencv_core.so"),
path.join(package:installdir(libfiledir), "libopencv_video.so"),
path.join(package:installdir(libfiledir), "libopencv_videoio.so")
}
return result
end)
package
描述了包名,务必和文件夹同名
on_load()
函数的具体含义和使用参考xmake
的官方文档,我们这里只是定义了包的安装目录
on_fetch()
函数的具体含义和使用参考xmake
的官方文档,但是里面定义了头文件目录、库文件目录,和将要链接的库。注意的是这里我只写了链接
libopencv_core.so
、libopencv_video.so
和libopencv_videoio.so
,是因为只用到了这些,如果你要用到更多,那就在对应添加一下即可。
2.2.2 rknnrt
因为这是使用rk平台必须使用的库,所以不管是本机编译还是交叉编译,都要使用到,所以cross
和linux
都是将要使用到的。所以对应的xmake.lua
:
package("rknnrt")
set_description("The rknnrt package")
add_configs("shared", {description = "Build shared library.", default = true, type = "boolean", readonly = true})
on_load(function (package)
-- 官方提供的库,也只有一个版本,就不区分 release 还是 debug 了
-- package:set("installdir", path.join(os.scriptdir(), package:plat(), package:arch(), package:mode()))
package:set("installdir", path.join(os.scriptdir(), package:plat(), package:arch()))
end)
on_fetch(function (package)
local result = {}
local libfiledir = (package:config("shared") and package:is_plat("windows", "mingw")) and "bin" or "lib"
result.links = "rknnrt"
result.linkdirs = package:installdir("lib")
result.includedirs = package:installdir("include")
result.libfiles = path.join(package:installdir(libfiledir), "librknnrt.so")
return result
end)
2.2.3 spdlog
spdlog
和Eigen
都是纯头文件库,所以不管是aarch64
还是x86
,都是使用同样一套文件。为了简单,我还是同意都放在本地的repo
之中。这里给一下spdlog
的xmake.lua
示例:
package("spdlog")
set_description("Fast C++ logging library.")
add_configs("library", {headeronly = true})
on_load(function (package)
package:set("installdir", os.scriptdir())
end)
on_fetch(function (package)
local result = {}
result.includedirs = package:installdir("include")
return result
end)
OK,特别的地方相信聪明的我们肯定注意到了。我就不指出来了!
3. rknn_engine
库
三方库我们都已经通过自己写的xmake.lua
加入到xmake
的repo
中了,rknn_engine
的目录结构:
./rknn_engine/
├── demo
├── include
├── README.md
├── src
├── xmake.lua
└── xmake_repo
那就直接给出xmake.lua
的内容:
set_project("rknn_engine")
set_version("1.0.1", {soname = true})
set_xmakever("2.3.1")
set_languages("cxx17", "c17")
set_warnings("all")
option("local_repo")
set_default("./xmake_repo")
set_showmenu(true)
set_description("local_repo for xmake")
option_end()
add_repositories("local-repo $(local_repo)")
add_requires("spdlog", "rknnrt", "librga")
if is_plat("cross") then
add_requires("opencv", "eigen3")
else
add_requires("cmake::OpenCV", {alias = "opencv", system = true})
add_requires("cmake::Eigen3", {alias = "eigen3", system = true})
end
target("rknn_engine")
-- set_kind("$(kind)")
set_kind("shared")
-- set_basename("rknn_engine")
-- 添加公共依赖
add_includedirs("include", {public = true})
add_headerfiles("include/(**.h)")
add_files("src/**.cpp")
add_packages("spdlog", "rknnrt", "librga", "opencv", "eigen3")
add_rules("utils.install.pkgconfig_importfiles")
add_rules("utils.install.cmake_importfiles")
-- 交叉编译命令
-- xmake f -p cross --cross=aarch64-linux-gnu-
-- 或者
-- xmake f -p cross --sdk=/path/to/cross/compiler
注意到这里的几个地方:
option(“local_repo”)
这个是加一个编译选项,可以配置本地
repo
的地址add_repositories(“local-repo $(local_repo)”)
添加一个本地
repo
的地址if is_plat(“cross”) then
这个则是按照编译的平台进行处理,可以对应
xmake f -p cross
中的这个-p
选项,具体有哪些取值也还是参考xmake
的官方文档add_rules(“utils.install.pkgconfig_importfiles”)
add_rules(“utils.install.cmake_importfiles”)
这个是增加最终安装的规则,可以增加
pkgconfig
和cmake
的东西
OK,这篇博客就写这些了。