Loading...
Loading...
Guides agents in compiling and packaging C/C++ source code into dynamic or static libraries (Code Assets) using Dart's Native Assets hook system (via hook/build.dart and hook/link.dart utilizing package:hooks and package:native_toolchain_c). Use when a user asks to: 'setup native assets', 'compile C/C++ source code', 'bundle dynamic libraries', 'build native C code', 'link native assets', 'implement build.dart or link.dart hooks', or 'integrate C/C++ interop in Dart/Flutter'. Helps agents avoid manual toolchain orchestration and configures secure hash-validated binary downloads or advanced linker tree-shaking with package:record_use mapping.
npx skill4agent add dart-lang/skills dart-setup-ffi-assetsdart rundart testdart buildflutter runhook/hook/build.darthook/link.dart[!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/hook/build.darthook/link.dartpackage:native_toolchain_cCBuilderCLibrarygccclangmsvcMethod.namelib/src/third_party/.g.dartsqlite3.record_use_mapping.g.dartlocal_build| 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. | |
dart pub add code_assets hooks native_toolchain_c record_use dev:ffigenpubspec.yamldependencies:
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.1lib/src/c_library.darthook/build.darthook/link.dartdart test| 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. |
pkgs/code_assets/example/sqlitepackage:native_toolchain_cxcode-select --installsudo apt install build-essentialthird_party/sqlite/sqlite3.cthird_party/sqlite/sqlite3.h#ifndef SQLITE3_H_
#define SQLITE3_H_
const char *sqlite3_libversion(void);
#endif // SQLITE3_H_tool/ffigen.dartlib/src/third_party/sqlite3.g.dartlib/src/third_party/sqlite3.record_use_mapping.g.dart// AUTO-GENERATED FILE - DO NOT MODIFY.
// Generated via ffigen.
const recordUseMapping = {
'sqlite3_libversion': 'sqlite3_libversion',
};lib/src/c_library.dartimport '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'],
);hook/build.darthook/build.dartCLibrary.build.so.dylib.dllimport '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)',
},
);
}
});
}hook/link.darthook/link.dartLinkerOptions.treeshakeimport '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]!,
),
),
);
});
}download_assetlocal_buildHttpClientCodeAssetlib/src/hook_helpers/hashes.dartconst assetHashes = {
'libnative_add_macos_arm64.dylib': '4a88f50438a98402db2dbd47b59eb412',
'libnative_add_linux_x64.so': '9f5e15043aa98402dcdbbd47b59ea520',
'native_add_windows_x64.dll': 'a881e5043ba98402acdebd47b59fa321',
};lib/src/hook_helpers/download.dartimport '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();
}hook/build.dartimport '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 test.dart_tool/resources/.dylib.dart_tool/resources/.so.dart_tool/resources/.dlldart build cli bin/main.dartnm -gU build/cli/lib/libsqlite3.dylibnm -D build/cli/lib/libsqlite3.sodumpbin /EXPORTS build\cli\lib\sqlite3.dllsqlite3_libversionSkipping linking as no symbols are to be kept..dylib.so.dlllocal_build: truepubspec.yamlpubspec.yamlhooks:
user_defines:
<your_package_name>:
local_build: truedart test