Skip to content

Commit ec2c30f

Browse files
committed
[native_assets_cli] Add DartCApi
1 parent 7a72868 commit ec2c30f

File tree

16 files changed

+321
-0
lines changed

16 files changed

+321
-0
lines changed

.github/workflows/native.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ jobs:
111111
- run: dart pub get -C test_data/no_hook/
112112
if: ${{ matrix.package == 'native_assets_builder' }}
113113

114+
- run: dart pub get -C test_data/use_dart_api/
115+
if: ${{ matrix.package == 'native_assets_builder' }}
116+
114117
- run: dart pub get -C example/build/download_asset/
115118
if: ${{ matrix.package == 'native_assets_cli' }}
116119

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:ffi';
6+
import 'dart:io';
7+
8+
import 'package:pub_semver/pub_semver.dart';
9+
import 'package:test/test.dart';
10+
11+
import '../helpers.dart';
12+
import 'helpers.dart';
13+
14+
const Timeout longTimeout = Timeout(Duration(minutes: 5));
15+
16+
void main() async {
17+
test('use_dart_api build', timeout: longTimeout, () async {
18+
await inTempDir((tempUri) async {
19+
await copyTestProjects(targetUri: tempUri);
20+
final packageUri = tempUri.resolve('use_dart_api/');
21+
await runPubGet(
22+
workingDirectory: packageUri,
23+
logger: logger,
24+
);
25+
26+
// Assume we're run from Dart SDK and try to find the include dir.
27+
final includeDirectory = dartExecutable.resolve('../include/');
28+
final versionFile = includeDirectory.resolve('dart_version.h');
29+
final versionContents =
30+
await File(versionFile.toFilePath()).readAsString();
31+
final regex = RegExp(
32+
r'#define DART_API_DL_MAJOR_VERSION (\d+)\s*#define DART_API_DL_MINOR_VERSION (\d+)');
33+
final match = regex.firstMatch(versionContents)!;
34+
final major = int.parse(match.group(1)!);
35+
final minor = int.parse(match.group(2)!);
36+
final dartCApi = DartCApi(
37+
includeDirectory: includeDirectory,
38+
version: Version(major, minor, 0),
39+
);
40+
41+
// Run the build.
42+
final result = (await buildCodeAssets(
43+
packageUri,
44+
dartCApi: dartCApi,
45+
))!;
46+
final codeAsset = CodeAsset.fromEncoded(result.encodedAssets.single);
47+
48+
// Check that we can load the dylib and run the init.
49+
final dylib = DynamicLibrary.open(codeAsset.file!.toFilePath());
50+
final initDartApiDl = dylib.lookupFunction<IntPtr Function(Pointer<Void>),
51+
int Function(Pointer<Void>)>('InitDartApiDL');
52+
final initResult = initDartApiDl(NativeApi.initializeApiDLData);
53+
expect(initResult, 0);
54+
});
55+
});
56+
}

pkgs/native_assets_builder/test/build_runner/helpers.dart

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Future<BuildResult?> buildCodeAssets(
5252
Uri packageUri, {
5353
String? runPackageName,
5454
List<String>? capturedLogs,
55+
DartCApi? dartCApi,
5556
}) =>
5657
build(
5758
packageUri,
@@ -63,6 +64,7 @@ Future<BuildResult?> buildCodeAssets(
6364
buildValidator: validateCodeAssetBuildOutput,
6465
applicationAssetValidator: validateCodeAssetInApplication,
6566
runPackageName: runPackageName,
67+
dartCApi: dartCApi,
6668
);
6769

6870
Future<BuildResult?> build(
@@ -84,6 +86,7 @@ Future<BuildResult?> build(
8486
bool linkingEnabled = false,
8587
required List<String> buildAssetTypes,
8688
Map<String, String>? hookEnvironment,
89+
DartCApi? dartCApi,
8790
}) async {
8891
final targetOS = target?.os ?? OS.current;
8992
final runPackageName_ =
@@ -122,6 +125,7 @@ Future<BuildResult?> build(
122125
android: targetOS == OS.android
123126
? AndroidCodeConfig(targetNdkApi: targetAndroidNdkApi!)
124127
: null,
128+
dartCApi: dartCApi,
125129
);
126130
}
127131
return inputBuilder;

pkgs/native_assets_builder/test_data/manifest.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@
164164
- use_all_api/hook/build.dart
165165
- use_all_api/hook/link.dart
166166
- use_all_api/pubspec.yaml
167+
- use_dart_api/ffigen.yaml
168+
- use_dart_api/hook/build.dart
169+
- use_dart_api/lib/src/use_dart_api_bindings_generated.dart
170+
- use_dart_api/lib/use_dart_api.dart
171+
- use_dart_api/pubspec.yaml
172+
- use_dart_api/src/use_dart_api.c
173+
- use_dart_api/src/use_dart_api.h
174+
- use_dart_api/test/use_dart_api_test.dart
167175
- wrong_build_output/hook/build.dart
168176
- wrong_build_output/pubspec.yaml
169177
- wrong_build_output_2/hook/build.dart
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
An example that uses the [C API of the Dart VM].
2+
3+
The example shows how to pass an object from the Dart heap to native code and
4+
hold on to it via a `PersistentHandle`. For more documentation about handles,
5+
and the other C API features refer to the documentation in the header files.
6+
7+
## Usage
8+
9+
Run tests with `dart --enable-experiment=native-assets test`.
10+
11+
[C API of the Dart VM]: https://github.com/dart-lang/sdk/tree/main/runtime/include
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Run with `flutter pub run ffigen --config ffigen.yaml`.
2+
name: NativeAddBindings
3+
description: |
4+
Bindings for `src/use_dart_api.h`.
5+
6+
Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
7+
output: 'lib/src/use_dart_api_bindings_generated.dart'
8+
headers:
9+
entry-points:
10+
- 'src/use_dart_api.h'
11+
include-directives:
12+
- 'src/use_dart_api.h'
13+
preamble: |
14+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
15+
// for details. All rights reserved. Use of this source code is governed by a
16+
// BSD-style license that can be found in the LICENSE file.
17+
comments:
18+
style: any
19+
length: full
20+
ffi-native:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:logging/logging.dart';
6+
import 'package:native_assets_cli/code_assets_builder.dart';
7+
import 'package:native_assets_cli/native_assets_cli.dart';
8+
import 'package:native_toolchain_c/native_toolchain_c.dart';
9+
10+
void main(List<String> arguments) async {
11+
await build(arguments, (input, output) async {
12+
final dartCApi = input.config.code.dartCApi;
13+
if (dartCApi == null) {
14+
throw UnsupportedError(
15+
'This doesn\'t work with access to the Dart C API!',
16+
);
17+
}
18+
19+
final packageName = input.packageName;
20+
final cbuilder = CBuilder.library(
21+
name: packageName,
22+
assetName: 'src/${packageName}_bindings_generated.dart',
23+
sources: [
24+
'src/$packageName.c',
25+
dartCApi.dartApiDlC.toFilePath(),
26+
],
27+
includes: [
28+
dartCApi.includeDirectory.toFilePath(),
29+
],
30+
);
31+
await cbuilder.run(
32+
input: input,
33+
output: output,
34+
logger: Logger('')
35+
..level = Level.ALL
36+
..onRecord.listen((record) {
37+
print('${record.level.name}: ${record.time}: ${record.message}');
38+
}),
39+
);
40+
});
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// AUTO GENERATED FILE, DO NOT EDIT.
6+
//
7+
// Generated by `package:ffigen`.
8+
// ignore_for_file: type=lint
9+
import 'dart:ffi' as ffi;
10+
11+
@ffi.Native<ffi.Int32 Function(ffi.Int32, ffi.Int32)>(symbol: 'add')
12+
external int add(
13+
int a,
14+
int b,
15+
);
16+
17+
@ffi.Native<ffi.IntPtr Function(ffi.Pointer<ffi.Void>)>(symbol: 'InitDartApiDL')
18+
external int InitDartApiDL(
19+
ffi.Pointer<ffi.Void> data,
20+
);
21+
22+
@ffi.Native<ffi.Pointer<ffi.Void> Function(ffi.Handle)>(
23+
symbol: 'NewPersistentHandle')
24+
external ffi.Pointer<ffi.Void> NewPersistentHandle(
25+
Object non_persistent_handle,
26+
);
27+
28+
@ffi.Native<ffi.Handle Function(ffi.Pointer<ffi.Void>)>(
29+
symbol: 'HandleFromPersistent')
30+
external Object HandleFromPersistent(
31+
ffi.Pointer<ffi.Void> persistent_handle,
32+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
export 'src/use_dart_api_bindings_generated.dart';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: use_dart_api
2+
description: Uses some functions from `dart_api_dl.h`.
3+
version: 0.1.0
4+
5+
publish_to: none
6+
7+
environment:
8+
sdk: '>=3.3.0 <4.0.0'
9+
10+
dependencies:
11+
logging: ^1.1.1
12+
# native_assets_cli: ^0.11.0
13+
native_assets_cli:
14+
path: ../../../native_assets_cli/
15+
# native_toolchain_c: ^0.8.0
16+
native_toolchain_c:
17+
path: ../../../native_toolchain_c/
18+
19+
dev_dependencies:
20+
ffigen: ^10.0.0
21+
lints: ^3.0.0
22+
test: ^1.23.1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#include "use_dart_api.h"
6+
7+
int32_t add(int32_t a, int32_t b) { return a + b; }
8+
9+
intptr_t InitDartApiDL(void *data) { return Dart_InitializeApiDL(data); }
10+
11+
void *NewPersistentHandle(Dart_Handle non_persistent_handle) {
12+
return Dart_NewPersistentHandle_DL(non_persistent_handle);
13+
}
14+
15+
Dart_Handle HandleFromPersistent(void *persistent_handle) {
16+
return Dart_HandleFromPersistent_DL(persistent_handle);
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#include <stdint.h>
6+
7+
#include "dart_api_dl.h"
8+
9+
#if _WIN32
10+
#define MYLIB_EXPORT __declspec(dllexport)
11+
#else
12+
#define MYLIB_EXPORT
13+
#endif
14+
15+
MYLIB_EXPORT int32_t add(int32_t a, int32_t b);
16+
17+
MYLIB_EXPORT intptr_t InitDartApiDL(void *data);
18+
19+
MYLIB_EXPORT void *NewPersistentHandle(Dart_Handle non_persistent_handle);
20+
21+
MYLIB_EXPORT Dart_Handle HandleFromPersistent(void *persistent_handle);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:ffi';
6+
7+
import 'package:test/test.dart';
8+
import 'package:use_dart_api/use_dart_api.dart';
9+
10+
void main() {
11+
InitDartApiDL(NativeApi.initializeApiDLData);
12+
13+
test('use dart_api_dl.h', () {
14+
const x = 42;
15+
final persistentHandle = NewPersistentHandle(x);
16+
HandleFromPersistent(persistentHandle);
17+
});
18+
}

pkgs/native_assets_cli/lib/code_assets.dart

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export 'src/code_assets/config.dart'
2424
CodeAssetLinkOutputBuilder,
2525
CodeAssetLinkOutputBuilderAdd,
2626
CodeConfig,
27+
DartCApi,
2728
IOSCodeConfig,
2829
MacOSCodeConfig;
2930
export 'src/code_assets/ios_sdk.dart' show IOSSdk;

pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart

+3
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ final class CodeAsset {
184184
}..sortOnKey());
185185

186186
static const String type = 'native_code';
187+
188+
@override
189+
String toString() => 'CodeAsset(${encode().encoding})';
187190
}
188191

189192
extension OSLibraryNaming on OS {

0 commit comments

Comments
 (0)