dart-setup-ffi-assets
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCompiling C Code into Code Assets with Native Assets Hooks
使用Native Assets钩子将C代码编译为Code Assets
Integrate and automate the compilation and packaging of native C/C++ source code into Code Assets under Dart's overarching Native Assets feature using build and link hooks.
通过构建和链接钩子,在Dart的Native Assets功能框架下,集成并自动化原生C/C++源代码到Code Assets的编译与打包流程。
Contents
目录
Introduction
简介
Under Dart's Native Assets feature, packages can package native code (like C/C++ libraries) as Code Assets and bundle them automatically during standard development cycles (e.g., , , , and ). The packaging of Code Assets is driven by two programmatic hook scripts placed inside a package's folder:
dart rundart testdart buildflutter runhook/- : Compiles local C sources to machine code or bundles prebuilt native binaries as code assets for a specific host/target architecture.
hook/build.dart - : Links built code assets, applying advanced tree-shaking optimizations to strip unused native symbols and compress the runtime binary size.
hook/link.dart
在Dart的Native Assets功能下,包可以将原生代码(如C/C++库)打包为Code Assets,并在标准开发周期(例如、、和)中自动捆绑。Code Assets的打包由放置在包的文件夹中的两个程序化钩子脚本驱动:
dart rundart testdart buildflutter runhook/- :将本地C源代码编译为机器码,或将预构建原生二进制文件打包为特定主机/目标架构的Code Assets。
hook/build.dart - :链接已构建的Code Assets,应用高级摇树优化以剥离未使用的原生符号,压缩运行时二进制文件大小。
hook/link.dart
Constraints
约束条件
[!IMPORTANT] Keep all file resolving platform-independent. Never hardcode absolute target paths, shell scripts, or system command variables. Always useorPlatform.script.resolve()-based resolution to ensure scripts are fully portable.Uri
- Hook Locations: Compiling and packaging hooks must reside strictly inside the directory at the package's root:
hook/- (Build execution phase)
hook/build.dart - (Optional packaging/linking/tree-shaking phase)
hook/link.dart
- Compile Toolchain Standard: Use the programmatic APIs from (e.g.
package:native_toolchain_candCBuilder) to run compile toolchains. Never invoke rawCLibrary,gcc, orclangvia shell commands.msvc - Preamble & License Headers: Every handcrafted and generated source file (including bindings, helpers, and hooks) must strictly contain the target package's copyright and licensing header.
- Tree Shaking Mapping: If utilizing compiler tree-shaking, you must map the target Dart method names (e.g. ) back to their raw native C symbol names using a record use mapping generated by FFIgen. The mapping file must reside under
Method.nameand strictly use thelib/src/third_party/extension (e.g.,.g.dart).sqlite3.record_use_mapping.g.dart - Integrity Safeguards for Precompiled Libraries: If adopting the dynamic download pattern:
- Cryptographic Verification: Downloaded prebuilt binaries must be checked against preconfigured lookup tables containing MD5 or SHA-256 hashes to guarantee binary integrity and prevent tampering.
- Graceful Recovery: Support offline developers by providing fallbacks (such as local compiler execution via flags like ).
local_build
[!IMPORTANT] 确保所有文件解析与平台无关。切勿硬编码绝对目标路径、Shell脚本或系统命令变量。始终使用或基于Platform.script.resolve()的解析方式,确保脚本完全可移植。Uri
- 钩子位置:编译和打包钩子必须严格放置在包根目录的目录下:
hook/- (构建执行阶段)
hook/build.dart - (可选的打包/链接/摇树阶段)
hook/link.dart
- 编译工具链标准:使用提供的程序化API(如
package:native_toolchain_c和CBuilder)来运行编译工具链。切勿通过Shell命令直接调用原始的CLibrary、gcc或clang。msvc - 序言与许可证头:所有手工编写和生成的源代码文件(包括绑定、辅助工具和钩子)必须严格包含目标包的版权和许可证头。
- 摇树映射:如果使用编译器摇树优化,必须通过FFigen生成的使用记录映射,将目标Dart方法名(如)映射回对应的原生C符号名。映射文件必须存放在
Method.name目录下,且严格使用lib/src/third_party/扩展名(例如.g.dart)。sqlite3.record_use_mapping.g.dart - 预编译库的完整性保障:如果采用动态下载模式:
- 加密验证:下载的预构建二进制文件必须与包含MD5或SHA-256哈希的预配置查找表进行校验,以保证二进制文件的完整性并防止篡改。
- 优雅降级:为离线开发者提供回退方案(例如通过标志启用本地编译)。
local_build
Native Interop Packages
原生互操作包
Programmatic build and link hooks for Code Assets leverage three specialized native interop packages:
| Dependency | Purpose | Key API Abstractions |
|---|---|---|
| Main orchestrator defining execution bounds. | |
| Detects local compilers (MSVC, Xcode/Clang, GCC) and executes build toolchains. | |
| Models code metadata records passed to dynamic loaders. | |
Code Assets的程序化构建和链接钩子依赖三个专门的原生互操作包:
| 依赖包 | 用途 | 核心API抽象 |
|---|---|---|
| 主协调器,定义执行边界。 | |
| 检测本地编译器(MSVC、Xcode/Clang、GCC)并执行构建工具链。 | |
| 定义传递给动态加载器的代码元数据记录。 | |
Step-by-Step Workflow
分步工作流
Step 1: Add Dependencies
步骤1:添加依赖
Add Code Assets hook and toolchain dependencies to your package. You must fetch these dependencies directly from pub.dev.
You can add it automatically using the CLI:
bash
dart pub add code_assets hooks native_toolchain_c record_use dev:ffigenOr manually declare them in your target package's :
pubspec.yamlyaml
dependencies:
code_assets: ^1.0.0
hooks: ^0.1.0
native_toolchain_c: ^0.1.0
record_use: ^0.6.0
dev_dependencies:
ffigen: ^20.1.1将Code Assets钩子和工具链依赖添加到你的包中。必须直接从pub.dev获取这些依赖。
你可以使用CLI自动添加:
bash
dart pub add code_assets hooks native_toolchain_c record_use dev:ffigen或者在目标包的中手动声明:
pubspec.yamlyaml
dependencies:
code_assets: ^1.0.0
hooks: ^0.1.0
native_toolchain_c: ^0.1.0
record_use: ^0.6.0
dev_dependencies:
ffigen: ^20.1.1Step 2: Define C Specifications
步骤2:定义C规范
Define your target C library compilation metadata inside . This lets both the build and link hooks share a single source of truth for assets, names, and sources.
lib/src/c_library.dart在中定义目标C库的编译元数据。这样构建和链接钩子可以共享资产、名称和源代码的单一数据源。
lib/src/c_library.dartStep 3: Implement Build and Link Hook Scripts
步骤3:实现构建与链接钩子脚本
Write the compilation orchestration script inside and the dead-code elimination logic inside .
hook/build.darthook/link.dart在中编写编译编排脚本,在中编写死码消除逻辑。
hook/build.darthook/link.dartStep 4: Run the Hook Cycle
步骤4:运行钩子周期
Running standard test suites dynamically launches the build and link hook lifecycle in the background:
bash
dart test运行标准测试套件会在后台自动启动构建和链接钩子的生命周期:
bash
dart testChoosing an Integration Approach
选择集成方案
There are two primary methods for integrating and delivering C/C++ native assets in Dart. Select the one that matches your project requirements:
| Aspect | Method 1: Local Compilation & Tree-Shaking | Method 2: Precompiled Downloads |
|---|---|---|
| Primary Use Case | When C/C++ source code is included directly in the package and you want maximum size optimization. | When compiling locally is slow/complex, or when avoiding developer host toolchain requirements. |
| Host Toolchain Requirements | Requires pre-installed platform C compiler (Xcode tools, MSVC, GCC). | Zero compiler setup required on developer/user machines. |
| Binary Optimization | Premium. Unused symbols are completely tree-shaken, decreasing library size. | Standard. Standard compiled binaries are shipped as-is. |
| Offline Setup | Fully compliant. Works completely offline. | Requires network access to download libraries, with offline fallback. |
在Dart中集成和交付C/C++原生资产有两种主要方法。请选择符合项目需求的方案:
| 维度 | 方法1:本地编译与摇树优化 | 方法2:预编译二进制下载 |
|---|---|---|
| 主要适用场景 | 当C/C++源代码直接包含在包中,且需要最大化尺寸优化时。 | 当本地编译缓慢/复杂,或希望避免开发者主机工具链要求时。 |
| 主机工具链要求 | 需要预先安装平台C编译器(Xcode工具、MSVC、GCC)。 | 开发者/用户机器无需任何编译器配置。 |
| 二进制优化 | 高级优化。未使用的符号会被完全摇树剥离,减小库体积。 | 标准优化。直接交付标准编译的二进制文件。 |
| 离线支持 | 完全兼容。可完全离线工作。 | 需要网络下载库文件,提供离线回退方案。 |
Method 1: Local Compilation with Linker Tree-Shaking (Recommended)
方法1:本地编译与链接器摇树优化(推荐)
In this approach, the build hook invokes local toolchains (GCC, Clang, MSVC) to compile source files directly. The link hook subsequently filters output symbols utilizing compiler options, retaining only target methods invoked in user code. This represents the standard, robust SQLite pattern under .
pkgs/code_assets/example/sqlite此方案中,构建钩子调用本地工具链(GCC、Clang、MSVC)直接编译源代码。链接钩子随后利用编译器选项过滤输出符号,仅保留用户代码中调用的目标方法。这是下的标准、可靠的SQLite模式。
pkgs/code_assets/example/sqlitePrerequisite Host Compiler Toolchains
前置主机编译器工具链
Since delegates actual dynamic compilation to the host operating system's default toolchain, the development machine must have one of the following compiler packages pre-installed:
package:native_toolchain_c- macOS: Xcode Command Line Tools. Install via:
bash
xcode-select --install - Linux: GCC or Clang. Install via:
bash
sudo apt install build-essential - Windows: MSVC (Microsoft Visual C++). Install the Visual Studio Installer and select the Desktop development with C++ workload.
Note: If no compatible toolchain is discovered on the host path, the build hook script will throw a compilation execution exception. Ensure to specify compiler constraints or adopt Method 2 if toolchains cannot be guaranteed.
由于将实际的动态编译委托给主机操作系统的默认工具链,开发机器必须预先安装以下编译器包之一:
package:native_toolchain_c- macOS:Xcode命令行工具。通过以下命令安装:
bash
xcode-select --install - Linux:GCC或Clang。通过以下命令安装:
bash
sudo apt install build-essential - Windows:MSVC(Microsoft Visual C++)。安装Visual Studio安装程序并选择使用C++的桌面开发工作负载。
注意:如果在主机路径中未发现兼容的工具链,构建钩子脚本将抛出编译执行异常。如果无法保证工具链可用,请指定编译器约束或采用方法2。
C Source and Bindings Setup
C源代码与绑定配置
Assume a C source defining simple math functions at with its entry point header at :
third_party/sqlite/sqlite3.cthird_party/sqlite/sqlite3.hc
#ifndef SQLITE3_H_
#define SQLITE3_H_
const char *sqlite3_libversion(void);
#endif // SQLITE3_H_We utilize a programmatic FFIgen script () to create FFI bindings in , enabling recorded usage tracking and producing the lookup metadata map in :
tool/ffigen.dartlib/src/third_party/sqlite3.g.dartlib/src/third_party/sqlite3.record_use_mapping.g.dartdart
// AUTO-GENERATED FILE - DO NOT MODIFY.
// Generated via ffigen.
const recordUseMapping = {
'sqlite3_libversion': 'sqlite3_libversion',
};假设在中有一个定义简单数学函数的C源代码,其入口头文件位于:
third_party/sqlite/sqlite3.cthird_party/sqlite/sqlite3.hc
#ifndef SQLITE3_H_
#define SQLITE3_H_
const char *sqlite3_libversion(void);
#endif // SQLITE3_H_我们使用程序化FFigen脚本()在中创建FFI绑定,启用使用记录跟踪,并在中生成查找元数据映射:
tool/ffigen.dartlib/src/third_party/sqlite3.g.dartlib/src/third_party/sqlite3.record_use_mapping.g.dartdart
// AUTO-GENERATED FILE - DO NOT MODIFY.
// Generated via ffigen.
const recordUseMapping = {
'sqlite3_libversion': 'sqlite3_libversion',
};Defining the C Library Build Spec
定义C库构建规范
Define the centralized library specification in :
lib/src/c_library.dartdart
import 'package:native_toolchain_c/native_toolchain_c.dart';
/// The C build specification for the sqlite library.
final cLibrary = CLibrary(
name: 'sqlite3',
assetName: 'src/third_party/sqlite3.g.dart',
sources: ['third_party/sqlite/sqlite3.c'],
);在中定义集中式库规范:
lib/src/c_library.dartdart
import 'package:native_toolchain_c/native_toolchain_c.dart';
/// The C build specification for the sqlite library.
final cLibrary = CLibrary(
name: 'sqlite3',
assetName: 'src/third_party/sqlite3.g.dart',
sources: ['third_party/sqlite/sqlite3.c'],
);Implementing hook/build.dart
hook/build.dart实现hook/build.dart
hook/build.dartImplement using . This builds the library to a dynamic library (e.g. , , or ) inside the hook's target directory:
hook/build.dartCLibrary.build.so.dylib.dlldart
import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';
import 'package:sqlite/src/c_library.dart';
void main(List<String> args) async {
await build(args, (input, output) async {
if (input.config.buildCodeAssets) {
await cLibrary.build(
input: input,
output: output,
defines: {
if (input.config.code.targetOS == OS.windows)
// Ensure C functions are explicitly exported in the Windows DLL
'SQLITE_API': '__declspec(dllexport)',
},
);
}
});
}使用实现。这会将库编译为动态库(例如、或)并存储在钩子的目标目录中:
CLibrary.buildhook/build.dart.so.dylib.dlldart
import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';
import 'package:sqlite/src/c_library.dart';
void main(List<String> args) async {
await build(args, (input, output) async {
if (input.config.buildCodeAssets) {
await cLibrary.build(
input: input,
output: output,
defines: {
if (input.config.code.targetOS == OS.windows)
// Ensure C functions are explicitly exported in the Windows DLL
'SQLITE_API': '__declspec(dllexport)',
},
);
}
});
}Implementing hook/link.dart
hook/link.dart实现hook/link.dart
hook/link.dartImplement the link optimization phase in . This utilizes compiler tree-shaking options () to compile a minimized, dead-code-eliminated binary based on symbol usage records:
hook/link.dartLinkerOptions.treeshakedart
import 'package:hooks/hooks.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';
import 'package:record_use/record_use.dart';
import 'package:sqlite/src/c_library.dart';
import 'package:sqlite/src/third_party/sqlite3.record_use_mapping.g.dart';
void main(List<String> arguments) async {
await link(arguments, (input, output) async {
await cLibrary.link(
input: input,
output: output,
linkerOptions: LinkerOptions.treeshake(
// Map Dart Method references back to raw C symbol names
symbolsToKeep: input.recordedUses?.calls.keys.cast<Method>().map(
(e) => recordUseMapping[e.name]!,
),
),
);
});
}在中实现链接优化阶段。这利用编译器摇树选项(),基于符号使用记录编译一个最小化、消除死码的二进制文件:
hook/link.dartLinkerOptions.treeshakedart
import 'package:hooks/hooks.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';
import 'package:record_use/record_use.dart';
import 'package:sqlite/src/c_library.dart';
import 'package:sqlite/src/third_party/sqlite3.record_use_mapping.g.dart';
void main(List<String> arguments) async {
await link(arguments, (input, output) async {
await cLibrary.link(
input: input,
output: output,
linkerOptions: LinkerOptions.treeshake(
// Map Dart Method references back to raw C symbol names
symbolsToKeep: input.recordedUses?.calls.keys.cast<Method>().map(
(e) => recordUseMapping[e.name]!,
),
),
);
});
}Method 2: Downloading Precompiled Dynamic Libraries
方法2:下载预编译动态库
An alternative approach compiles binaries beforehand on a central build machine, archives them, and downloads the target binary during the build hook execution. This matches the paradigm demonstrated in the hook package.
download_asset另一种方案是预先在中央构建机器上编译二进制文件,归档后在构建钩子执行期间下载目标二进制文件。这与钩子包展示的范式一致。
download_assetWhy Download Precompiled Binaries?
为什么选择预编译二进制下载?
- Host Constraints: Compiling large C/C++ libraries locally requires a complete compiler setup (GCC, Xcode/SDKs, Visual Studio) that the end-developer's host machine may not possess.
- Compile Speed: Precompiled downloads execute in milliseconds compared to potentially long multi-minute compilation processes.
- Platform Bridging: Allows cross-compiling constraints to be avoided if host architectures are limited.
- 主机约束:本地编译大型C/C++库需要完整的编译器配置(GCC、Xcode/SDKs、Visual Studio),而终端开发者的主机机器可能不具备这些条件。
- 编译速度:预编译下载只需数毫秒,相比之下本地编译可能需要数分钟。
- 平台桥接:如果主机架构有限,可以避免交叉编译约束。
Implementing Precompiled Dynamic Downloads
实现预编译动态库下载
We configure our build hook to detect local compiler flags (e.g. ). If not specified, the hook utilizes to pull down platform-specific libraries, calculates the MD5 hash to confirm download safety against a configured hashes lookup table, and registers the binary file as a :
local_buildHttpClientCodeAsset我们配置构建钩子以检测本地编译器标志(例如)。如果未指定该标志,钩子将使用拉取特定平台的库,计算MD5哈希以与配置的哈希查找表确认下载安全性,并将二进制文件注册为:
local_buildHttpClientCodeAsset1. Defining Target Hashes (lib/src/hook_helpers/hashes.dart
)
lib/src/hook_helpers/hashes.dart1. 定义目标哈希(lib/src/hook_helpers/hashes.dart
)
lib/src/hook_helpers/hashes.dartDefine target MD5 hash checks per platform file in your package sources:
dart
const assetHashes = {
'libnative_add_macos_arm64.dylib': '4a88f50438a98402db2dbd47b59eb412',
'libnative_add_linux_x64.so': '9f5e15043aa98402dcdbbd47b59ea520',
'native_add_windows_x64.dll': 'a881e5043ba98402acdebd47b59fa321',
};在包源代码中为每个平台文件定义目标MD5哈希校验:
dart
const assetHashes = {
'libnative_add_macos_arm64.dylib': '4a88f50438a98402db2dbd47b59eb412',
'libnative_add_linux_x64.so': '9f5e15043aa98402dcdbbd47b59ea520',
'native_add_windows_x64.dll': 'a881e5043ba98402acdebd47b59fa321',
};2. Hook Downloader Helper (lib/src/hook_helpers/download.dart
)
lib/src/hook_helpers/download.dart2. 钩子下载器辅助工具(lib/src/hook_helpers/download.dart
)
lib/src/hook_helpers/download.dartImplement the downloading and integrity check logic using dynamic target filename matching:
dart
import 'dart:io';
import 'package:code_assets/code_assets.dart';
import 'package:crypto/crypto.dart';
const version = '1.0.0';
Uri downloadUri(String target) => Uri.parse(
'https://github.com/my-org/my-native-repo/releases/download/$version/$target',
);
Future<File> downloadAsset(
OS targetOS,
Architecture targetArchitecture,
Directory outputDir,
) async {
final fileName = targetOS.dylibFileName('native_add_${targetOS.name}_${targetArchitecture.name}');
final uri = downloadUri(fileName);
final client = HttpClient()..findProxy = HttpClient.findProxyFromEnvironment;
final request = await client.getUrl(uri);
final response = await request.close();
if (response.statusCode != 200) {
throw ArgumentError('Download target $uri failed: Code ${response.statusCode}');
}
final targetFile = File.fromUri(outputDir.uri.resolve(fileName));
await targetFile.create(recursive: true);
await response.pipe(targetFile.openWrite());
return targetFile;
}
Future<String> hashAsset(File file) async {
return md5.convert(await file.readAsBytes()).toString();
}使用动态目标文件名匹配实现下载和完整性检查逻辑:
dart
import 'dart:io';
import 'package:code_assets/code_assets.dart';
import 'package:crypto/crypto.dart';
const version = '1.0.0';
Uri downloadUri(String target) => Uri.parse(
'https://github.com/my-org/my-native-repo/releases/download/$version/$target',
);
Future<File> downloadAsset(
OS targetOS,
Architecture targetArchitecture,
Directory outputDir,
) async {
final fileName = targetOS.dylibFileName('native_add_${targetOS.name}_${targetArchitecture.name}');
final uri = downloadUri(fileName);
final client = HttpClient()..findProxy = HttpClient.findProxyFromEnvironment;
final request = await client.getUrl(uri);
final response = await request.close();
if (response.statusCode != 200) {
throw ArgumentError('Download target $uri failed: Code ${response.statusCode}');
}
final targetFile = File.fromUri(outputDir.uri.resolve(fileName));
await targetFile.create(recursive: true);
await response.pipe(targetFile.openWrite());
return targetFile;
}
Future<String> hashAsset(File file) async {
return md5.convert(await file.readAsBytes()).toString();
}3. Implementing hook/build.dart
hook/build.dart3. 实现hook/build.dart
hook/build.dartWrite the final download build hook incorporating local compilation fallback:
dart
import 'dart:io';
import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';
import 'package:my_download_package/src/hook_helpers/hashes.dart';
import 'package:my_download_package/src/hook_helpers/download.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';
void main(List<String> args) async {
await build(args, (input, output) async {
final localBuild = input.userDefines['local_build'] as bool? ?? false;
if (localBuild) {
final name = 'native_add_${input.config.code.targetOS.name}_${input.config.code.targetArchitecture.name}';
final builder = CBuilder.library(
name: name,
assetName: 'native_add.dart',
sources: ['src/native_add.c'],
);
await builder.run(input: input, output: output);
} else {
final targetOS = input.config.code.targetOS;
final targetArch = input.config.code.targetArchitecture;
final outputDir = Directory.fromUri(input.outputDirectory);
final file = await downloadAsset(targetOS, targetArch, outputDir);
final fileHash = await hashAsset(file);
final expectedFileName = targetOS.dylibFileName('native_add_${targetOS.name}_${targetArch.name}');
final expectedHash = assetHashes[expectedFileName];
if (fileHash != expectedHash) {
throw Exception(
'Security Mismatch: File $expectedFileName hash verification failed! '
'Found hash: $fileHash, expected: $expectedHash.'
);
}
output.assets.code.add(
CodeAsset(
package: input.packageName,
name: 'native_add.dart',
linkMode: DynamicLoadingBundled(),
file: file.uri,
),
);
}
});
}编写包含本地编译回退的最终下载构建钩子:
dart
import 'dart:io';
import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';
import 'package:my_download_package/src/hook_helpers/hashes.dart';
import 'package:my_download_package/src/hook_helpers/download.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';
void main(List<String> args) async {
await build(args, (input, output) async {
final localBuild = input.userDefines['local_build'] as bool? ?? false;
if (localBuild) {
final name = 'native_add_${input.config.code.targetOS.name}_${input.config.code.targetArchitecture.name}';
final builder = CBuilder.library(
name: name,
assetName: 'native_add.dart',
sources: ['src/native_add.c'],
);
await builder.run(input: input, output: output);
} else {
final targetOS = input.config.code.targetOS;
final targetArch = input.config.code.targetArchitecture;
final outputDir = Directory.fromUri(input.outputDirectory);
final file = await downloadAsset(targetOS, targetArch, outputDir);
final fileHash = await hashAsset(file);
final expectedFileName = targetOS.dylibFileName('native_add_${targetOS.name}_${targetArch.name}');
final expectedHash = assetHashes[expectedFileName];
if (fileHash != expectedHash) {
throw Exception(
'Security Mismatch: File $expectedFileName hash verification failed! '
'Found hash: $fileHash, expected: $expectedHash.'
);
}
output.assets.code.add(
CodeAsset(
package: input.packageName,
name: 'native_add.dart',
linkMode: DynamicLoadingBundled(),
file: file.uri,
),
);
}
});
}Verification Checklist
验证检查清单
Before declaring a build or link hook implementation complete, always perform the following checks:
在宣布构建或链接钩子实现完成之前,请务必执行以下检查:
1. Local Execution Sandbox
1. 本地执行沙箱
Run unit tests and confirm the native assets compile/link process completes with no runtime or build tool exceptions:
bash
dart test运行单元测试,确认原生资产的编译/链接过程完成且无运行时或构建工具异常:
bash
dart test2. Verify Target Outputs
2. 验证目标输出
Navigate to your package target directory and verify that dynamic binary assets are created for the host system:
- macOS: Verify or target directories contain
.dart_tool/resources/files..dylib - Linux: Verify or target directories contain
.dart_tool/resources/files..so - Windows: Verify or target directories contain
.dart_tool/resources/files..dll
导航到包目标目录,确认主机系统的动态二进制资产已创建:
- macOS:验证或目标目录中包含
.dart_tool/resources/文件。.dylib - Linux:验证或目标目录中包含
.dart_tool/resources/文件。.so - Windows:验证或目标目录中包含
.dart_tool/resources/文件。.dll
3. Verify Tree-Shaking Stripping
3. 验证摇树剥离效果
To ensure the link hook is actually stripping unused native symbols and compressing binary packaging, perform the following validation:
- Compile a production bundle of the CLI/app:
bash
dart build cli bin/main.dart - Navigate to the compiled build directory containing the dynamic library.
- Query the exported dynamic symbol tables:
- macOS:
bash
nm -gU build/cli/lib/libsqlite3.dylib - Linux:
bash
nm -D build/cli/lib/libsqlite3.so - Windows (using MSVC Developer Command Prompt):
cmd
dumpbin /EXPORTS build\cli\lib\sqlite3.dll
- macOS:
- Confirm Target Exports: Verify that the command outputs only the explicitly kept entry point functions (e.g. ) and does not output any unreferenced/stripped symbols.
sqlite3_libversion - No Bundle Scenario: If the application does not import or invoke any methods from the native library:
- Verify that the link hook logs:
Skipping linking as no symbols are to be kept. - Verify that no library was built/placed in the production bundle (the /
.dylib/.sofile is not generated, saving bundle size)..dll
- Verify that the link hook logs:
为确保链接钩子确实在剥离未使用的原生符号并压缩二进制包大小,请执行以下验证:
- 编译CLI/应用的生产包:
bash
dart build cli bin/main.dart - 导航到包含动态库的编译构建目录。
- 查询导出的动态符号表:
- macOS:
bash
nm -gU build/cli/lib/libsqlite3.dylib - Linux:
bash
nm -D build/cli/lib/libsqlite3.so - Windows(使用MSVC开发者命令提示符):
cmd
dumpbin /EXPORTS build\cli\lib\sqlite3.dll
- macOS:
- 确认目标导出:验证命令输出仅包含明确保留的入口点函数(例如),不包含任何未引用/已剥离的符号。
sqlite3_libversion - 无捆绑场景:如果应用未导入或调用原生库的任何方法:
- 验证链接钩子日志显示:
Skipping linking as no symbols are to be kept. - 验证生产包中未生成/放置库文件(/
.dylib/.so文件未生成,节省包体积)。.dll
- 验证链接钩子日志显示:
4. Verify Offline Compliance (User Defines)
4. 验证离线兼容性(用户定义)
Confirm offline compliance is fully active and the download fallback executes perfectly offline:
- Configure the define for your package in the package's
local_build: true(or the workspace rootpubspec.yaml):pubspec.yamlyamlhooks: user_defines: <your_package_name>: local_build: true - Disable the machine's network adapter or run in a sandboxed offline shell.
- Launch unit tests:
bash
dart test - Verify the test suite successfully compiles local source files using host compilers, has no compile errors, and never attempts network download requests.
确认离线兼容性完全生效,且下载回退方案可在完全离线环境中正常执行:
- 在包的(或工作区根目录的
pubspec.yaml)中配置pubspec.yaml定义:local_build: trueyamlhooks: user_defines: <your_package_name>: local_build: true - 禁用机器的网络适配器或在沙箱化的离线Shell中运行。
- 启动单元测试:
bash
dart test - 验证测试套件成功使用主机编译器编译本地源代码,无编译错误,且从未尝试网络下载请求。