2023-12-28

OpenSSL 与 Flutter 项目集成指南

写 C/C++ 项目,一半以上的时间都在解决跨平台编译的问题,以前是同一套代码要在Windows 和 macOS 及 Linux 下跑,现在是同一套代码要在 Android 和 iOS 上跑。各种层出不穷的解决方案,Solutions,真是令人眼花缭乱。可道是,你以为你花了大部分精力学习了 C/C++,结果是你还需要花大量的精力去学习和折腾 Makefile,CMake,NDK,Ninja。

最近学习了一下 Flutter,要和团队配合写代码,而我主要负责底层的逻辑和算法,这又跑不掉要使用 C/C++ 和 Flutter 打交道。Flutter 看起来一切都那么美好,命令行编译,运行,代码修改马上就在界面上反映出修改后的内容。不过在你添加了 ffi 工程后看到 CMakeFiles.txt别高兴得太早,这仅仅是为 Android 平台准备的,而 iOS 平台请使用 iOS 目录下的 CocoaPods 来进行配置。嗯?跨平台的解决方案?

OpenSSL 已经被全世界广泛应用,于是其配置脚本变得非常的复杂,而要支持不同的 CPU,它也是用尽了全力。但是我们仍然要费尽心思把 OpenSSL 编译到 Android 和 iOS 平台上。

其实苹果原来直接内嵌了 OpenSSL。但是,几次 OpenSSL 升级后,苹果认为其 ABI 不稳定,接口在新的版本中又变来变去,于是苹果干脆自行提供了一套 Crypto 套件来解决加密问题,这也导致若你要写跨平台的 App,需要把加密部分的代码按照不同平台写两遍,或者自行把 OpenSSL 编译到苹果平台上。

Android

既然有 CMakeLists.txt,那么我是否可以承受意使用 CMake 的 ExternalProject 功能来下载 OpenSSL 并完成我的编译需求呢?

答案当然是肯定的,但是,这并不代表着你可以无所畏惧地像写原生系统那样编写 CMake 的脚本。下面的一些问题是值得注意的:

  • CMake 在 Android 下需要使用到 Ninja,但是对于 ExternalProject 中定义的目标 Ninja却无法被正常识别,然后会导致脚本不知道目标文件该如何生成而报错。解决办法是在声明ExternalProject_Add 时,添加参数BUILD_BYPRODUCTS来指定该 Project 会生成的文件。
1
2
3
4
5
6
7
8
9
include(ExternalProject)
ExternalProject_Add(openssl
URL https://www.openssl.org/source/openssl-3.2.0.tar.gz
PREFIX ${OPENSSL_COMPILE_DIR}
CONFIGURE_COMMAND ./Configure no-apps --prefix=${OPENSSL_INSTALL_DIR} ${ANDROID_TARGET_ABI} -static -fPIC
BUILD_IN_SOURCE TRUE
BUILD_BYPRODUCTS ${OPENSSL_CRYPTO_LINKING_LIB}
BUILD_BYPRODUCTS ${OPENSSL_SSL_LINKING_LIB}
)
  • Android 编译 NDK 的工程时,有不同的 Target 来针对各 CPU 指令集,不同的指令集需要在配置 OpenSSL 时,带入不同的参数以对应当前的编译目标指定集,否则编译出来的OpenSSL 的库将无法被正确链接到你的 ffi 工程中。但是,并不是所有的指令集我们都需要,于是可以在 ffi 工程目录下的 android 文件夹中修改 build.gradle 配置来限制我们需要的 ABI。例如,下方就限制了只需要arm64-v8ax86_64
1
2
3
4
5
6
defaultConfig {
minSdkVersion 24
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
  • Android 的 gradle 在编译时,可能会同时开启多个进程来同时编译多个不同 ABI 的Target,于是你需要在 CMakeFiles.txt 中根据当前的 Target 来设定不同的编译目标路径,以防止不同的目标混到同一个路径中导致莫明其妙的问题。
1
2
3
4
5
if (ANDROID_ABI MATCHES "^x86_64")
set(ANDROID_TARGET_ABI "android-x86_64")
elseif(ANDROID_ABI MATCHES "^arm64")
set(ANDROID_TARGET_ABI "android-arm64")
endif()

iOS

安装 Xcode 后就拥有了编译出 iOS 需要的移动芯片的编译器。然后使用 CommonCrypto 来进行加解密工作。是的,它看起来就是那么简单,你不需要额外的做什么工作,就可以在 iOS 平台上拥有与 OpenSSL 相当的加密/解密的函数和功能。

另外,如果要将 Flutter 的项目运行到 iOS 设备上,你需要手动打开 iOS 目录下的Xcode 工程文件,在设置中选择正确的开发者账户的内容,然后使用flutter run ...的时候,会自动对安装到 iOS 设备上的包进行签名。

示例

示例使用一个空的 Flutter 工程,然后在该工程目录下使用下方命令来创建 ffi 工程。

1
flutter create --platforms=android,ios --template=plugin_ffi kernel

工程同时放到了 Github 上做参考,点击下方的链接进行访问。

放在 Github 上的 Demo 工程

MATTHEW
桂ICP备17005075号