大模型应用
在 Dart/Flutter 中用 Native…
当需要把 C/C++ 源码编译并打包为 Dart 的 Code Assets,或下载预编译动态库并做完整性校验时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Dart/Flutter 中用 Na通用
解决思路
- 在包根目录的 hook/ 目录下放置 hook/build.dart(编译/打包阶段)和可选的 hook/link.dart(链接/树摇阶段),所有文件路径解析必须平台无关,使用 Platform.script.resolve() 或 Uri 解析,禁止硬编码绝对路径、shell 脚本或系统命令变量。
- 使用 package:native_toolchain_c 的程序化 API(如 CBuilder、CLibrary)执行编译工具链,禁止通过 shell 调用原始 gcc、clang 或 msvc。
- 为每个手写和生成的文件(绑定、辅助代码、钩子)添加目标包的版权与许可头。
- 若使用编译器树摇,必须用 FFIgen 生成的 record use 映射把 Dart 方法名(如 Method.name)映射回原始 C 符号名,映射文件放在 lib/src/third_party/ 下且必须使用 .g.dart 扩展名(如 sqlite3.record_use_mapping.g.dart)。
- 若采用预编译下载模式:下载的二进制必须与预配置的 MD5 或 SHA-256 哈希查找表比对以校验完整性、防止篡改;同时通过 local_build 等标志提供本地编译回退以支持离线开发者。
- 通过 dart pub add code_assets hooks native_toolchain_c record_use dev:ffigen 添加依赖,或在 pubspec.yaml 中声明 code_assets、hooks、native_toolchain_c、record_use 及 dev 依赖 ffigen。
- 在 lib/src/c_library.dart 中定义目标 C 库的编译元数据(如 CLibrary 的 name、assetName、sources),让 build 和 link 钩子共享同一份资产、名称与源码的唯一事实来源。
- 在 hook/build.dart 中用 build(args, callback) 与 CLibrary.build 构建动态库(.so/.dylib/.dll)到钩子目标目录;在 Windows 上通过 defines 传入 SQLITE_API: __declspec(dllexport) 确保 C 函数显式导出。
- 在 hook/link.dart 中用 link(arguments, callback) 与 cLibrary.link 配合 LinkerOptions.treeshake,将 input.recordedUses?.calls.keys.cast<Method>() 经 recordUseMapping 映射为 C 符号名传入 symbolsToKeep,实现死代码消除。
- 运行 dart test 触发 build 与 link 钩子生命周期。
- 选择集成方式:本地编译+树摇适合源码随包分发且追求最大体积优化;预编译下载适合本地编译慢/复杂或想免除开发者主机工具链要求。
- 本地编译前确保主机已装编译器:macOS 用 xcode-select --install 安装 Xcode Command Line Tools;Linux 用 sudo apt install build-essential 安装 GCC 或 Clang;Windows 安装 Visual Studio Installer 并勾选 Desktop development with C++ 工作负载。
- 用程序化 FFIgen 脚本(如 tool/ffigen.dart)生成 FFI 绑定到 lib/src/third_party/sqlite3.g.dart,并生成查找元数据映射到 lib/src/third_party/sqlite3.record_use_mapping.g.dart。
- 预编译下载模式中,在 lib/src/hook_helpers/hashes.dart 按平台文件名定义 MD5 哈希表(如 libnative_add_macos_arm64.dylib、libnative_add_linux_x64.so、native_add_windows_x64.dll)。
- 在 lib/src/hook_helpers/download.dart 中实现下载与校验:用 targetOS.dylibFileName(...) 生成文件名,用 HttpClient(findProxy = HttpClient.findProxyFromEnvironment)请求 GitHub Releases 等 URL,非 200 抛错,将响应 pipe 到输出目录文件,并用 md5.convert(await file.readAsBytes()).toString() 计算哈希。
- 在预编译下载的 hook/build.dart 中:读取 input.userDefines['local_build'](默认 false);为 true 时用 CBuilder.library 本地编译;否则下载文件、比对 assetHashes 中期望哈希,不匹配则抛安全异常,匹配后用 output.assets.code.add(CodeAsset(package: input.packageName, name: ..., linkMode: DynamicLoadingBundled(), file: file.uri)) 注册资产。
- 验证:运行 dart test 确认编译/链接无异常;检查 .dart_tool/resources/ 或目标目录生成对应平台的 .dylib/.so/.dll。
- 验证树摇:运行 dart build cli bin/main.dart,在构建目录用 nm -gU(macOS)、nm -D(Linux)或 dumpbin /EXPORTS(Windows MSVC 命令提示符)检查导出符号,确认只保留显式保留的入口函数(如 sqlite3_libversion),无未引用符号;若应用未调用任何原生方法,确认 link 钩子日志输出 Skipping linking as no symbols are to be kept. 且不生成库文件。
- 验证离线合规:在 pubspec.yaml(或工作区根)配置 hooks.user_defines.<your_package_name>.local_build: true,断网或沙箱离线后运行 dart test,确认使用主机编译器成功编译且不发起网络下载。
适用边界 · 注意事项
- 主机未安装任何受支持的 C 编译器(Xcode CLT、GCC/Clang、MSVC)时,本地编译方法会抛编译执行异常,应改用预编译下载方法。
- 需要完全离线且无法提供预编译二进制或本地编译器时,预编译下载模式不可用。
- 仅需纯 Dart 逻辑、不涉及 C/C++ 互操作时,无需引入 Native Assets 钩子。