1、背景
前面文章《基于Flutter实现跨平台离线大模型对话应用》中介绍了基于Flutter实现跨平台的离线模型对话应用,跑通了Android、iOS、Mac平台,在编译Windows平台时由于不熟悉Windows平台编译器,踩了一些坑,最终搞了一个晚上加一个上午成功搞定。
2、Windows编译器介绍
传统的编译器通常分为三个部分,前端(frontEnd),优化器(Optimizer)和后端(backEnd)。在编译过程中:
- 前端主要负责词法和语法分析,将源代码转化为抽象语法树;
- 优化器则是在前端的基础上,对得到的中间代码进行优化,使代码更加高效;
- 后端则是将已经优化的中间代码转化为针对各自平台的机器代码。
作为一个移动端开发,日常用到最多的C++编译器是GCC、Clang。
- GCC(GNU Compiler Collection,GNU 编译器套装),是一套由 GNU 开发的编程语言编译器。GCC 原名为 GNU C 语言编译器,因为它原本只能处理 C语言。GCC 快速演进,变得可处理 C++、Fortran、Pascal、Objective-C、Java 以及 Ada 等他语言。
- Clang是以 LLVM(Low Level Virtual Machine,底层虚拟机)为后端的一款高效易用,并且与IDE 结合很好的编译前端。Clang 只支持C,C++ 和 Objective-C 三种语言。
虽然Windows上也有Cygwin、MinGW、TDM-GCC等GCC移植版本,但我们还是选微软官方的MSVC系列编译器。它与Visual Studio集成发布,微软自己的编译器,VS是一个基本完整的开发工具集,它包括了整个软件生命周期中所需要的大部分工具,如UML工具、代码管控工具、集成开发环境(IDE)等等。所写的目标代码适用于微软支持的所有平台,包括Microsoft Windows、Windows Mobile、Windows CE、.NET Framework、.NET Compact Framework和Microsoft Silverlight 及Windows Phone。
3、Windows平台开发Flutter桌面应用
3.1 环境安装
Flutter 2.10 开始,对Windows 的支持已进入 stable 渠道。要开发Windows应用除了安装Flutter SDK,还需要安装微软提供的Visual Studio22或者是Visual Studio 2022生成工具。在安装时要选择”使用C++的桌面开发“,它包含了所有默认组件(必要的C++工具链和Windows SDK的头文件)。
3.2 配置C++编译脚本
直接在windows目录下创建CMakeLists.txt文件,在CMakeLists.txt中直接添加编译信息:
cmake_minimum_required(VERSION 3.10)
# 项目名称
set(PROJECT_NAME "libNativeAdd")
project(${PROJECT_NAME} LANGUAGES CXX)
# 源文件
add_library(${PROJECT_NAME} SHARED
"./native_add.cpp"
)
# 动态库的输出目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<$<CONFIG:DEBUG>:Debug>$<$<CONFIG:RELEASE>:Release>")
# 安装动态库的目标目录
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
# 安装动态库,到执行目录
install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${PROJECT_NAME}.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime)
也可以添加子目录方式添加对应模块:
add_subdirectory("../libs/native_add" native_add)
3.3 遇到问题
- 项目里面有几个
.cpp文件还有一个.c文件,这样配置后运行flutter run,一直报错,提示连接C文件里的函数失败,查看了编译时中间文件,发现C文件没有被执行编译,最后发现是CMakelist.txt中指定了project(${PROJECT_NAME} LANGUAGES CXX)编译语言导致C文件没有执行编译,其他平台都没问题,Windows上报错。 - 运行时FFI查找C函数时提示找不到,将编译出的DLL文件放到指定路径,通过指定路径加载也同样失败,而且debug查看,动态库已经成功加载,就是找不到函数。向外暴露的函数通过
#define EXPORT_API __attribute__ ((visibility ("default")))定义的EXPORT修饰:EXPORT_API void* init(const char* path);,windows编译时EXPORT_API修饰报错,以为这个修饰没有用,直接删除掉了,导致找不到函数。最后通过:
#ifdef WIN32
#define EXPORT_API extern "C" __declspec(dllexport)
#else
#define EXPORT_API extern "C" __attribute__((visibility("default"))) __attribute__((used))
#endif
来支持不同平台接口。
3.4 应用打包
我们一般安装windows程序都提供了一个安装包,我们执行安装,然后释放资源和可执行文件,同时应用程序发布到微软商店前必须要打包。有效的格式是 .msix、 .msixbundle、 .msixupload、 .appx、 .appxbundle、 .appxupload 和 .xap。Flutter提供了打包msix程序的工具。MSIX是微软提供的新的Windows应用程序包格式,它提供了一种现代的打包格式和安装程序。这种格式既可以用于将应用程序发布到Windows上的Microsoft Store,也可以直接分发应用程序安装程序。
Flutter提供了MSIX pub打包方式。打包时需要配置.pfx证书:
- 请下载OpenSSL工具包来生成证书。
- 进入OpenSSL的安装路径,例如
C:\Program Files\ OpenSSL- win64 \bin。为方便使用我们设置一个环境变量,这样可以从任何地方访问OpenSSL。 - 生成私钥:
Openssl genrsa -out mykeyname.key 2048。 - 使用私钥生成证书签名请求(CSR)文件:
Openssl req -new -key mykeyname.key -out mycsrname.csr - 使用私钥和CSR文件生成已签名的CRT文件:
openssl x509 -in mycsrname.csr -out mycrtname.crt -req -signkey mykeyname.key -days 10000 - 使用私钥和CRT文件生成。pfx文件:
openssl pkcs12 -export -out CERTIFICATE.pfx -inkey mykeyname.key -in mycrtname.crt - 在安装应用程序的机器上安装.pfx证书,配置作为受信任的根证书颁发机构。
Flutter可执行文件.exe可以在我们的项目build\windows\runner<build mode>下找到。除了.exe可执行文件,您还需要以下文件:
- 统一目录上所有的
.dll文件 - data目录
- 拷贝依赖文件:
msvcp140.dll,vcruntime140.dll,vcruntime140_1.dll
最终目录结构:
Release
│ flutter_windows.dll
│ msvcp140.dll
│ my_app.exe
│ vcruntime140.dll
│ vcruntime140_1.dll
│
└───data
│ │ app.so
│ │ icudtl.dat
...
3.5 Flutter插件
我们代码里面写死了加载.dll文件路径,打包后解压安装后加载路径不好找,Flutter提供了插件支持,我们只需要创建Native插件即可,不用再考虑加载路径的问题。
flutter create --template=plugin_ffi hello创建插件;pubspec.yaml配置 FFI plugins:
plugin:
platforms:
some_platform:
ffiPlugin: true
这样在插件里面实现本地代码,应用工程中调用插件代码即可。
参考:
- GitHub - gaoshang212/flutter_native_demo
- zhuanlan.zhihu.com/p/458488070
- Flutter Packages 的开发和提交 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter
4、性能优化
运行时效果不是很理想,使用的thinkpad,2.8G主频8核,24G内存,SSD,推理速度不是很快,开启编译优化:
target_compile_options(model_lib PRIVATE
"$<$<CONFIG:RELEASE>:-O3>"
"$<$<CONFIG:DEBUG>:-O3>"
)
并没有很快的速度提升,具体优化还得持续研究。
5、总结
本文介绍了Windows上的MSVC C++编译器以及Flutter创建桌面应用的步骤及应用程序打包方式,并分享了FFI集成本地代码遇到的问题和解决办法。