Skip to content

Commit 1371e65

Browse files
dcharkesvictorsanni
authored andcommitted
[native_assets] Add support for link hooks (flutter#148474)
This PR adds support invoking `link.dart` hooks. Link hooks can add new assets. Link hooks can transform assets sent to link hook from build hooks. This PR does not yet add support for getting tree-shake information in the link hooks. This is pending on defining the `resources.json` format (dart-lang/sdk#55494). Issue: * flutter#146263 ## Implementation considerations The build hooks could be run before Dart compilation and the link hooks after Dart compilation. (This is how it's done in Dart standalone.) However, due to the way the `Target`s are set up, this would require two targets and serializing and deserializing the `BuildResult` in between these. This would lead to more code but no benefits. Currently there is nothing that mandates running build hooks before Dart compilation. ## Testing * The unit tests verify that the native_assets_builder `link` and `linkDryRun` would be invoked with help of the existing fake. * The native assets integration test now also invokes an FFI call of a package that adds the asset during the link hook instead of the build hook. * In order to keep coverage of the `flutter create --template=package_ffi`, `flutter create` is still run and the extra dependency is added and an extra ffi call is added. (Open to alternative suggestions.)
1 parent 8959014 commit 1371e65

26 files changed

+1054
-421
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
migrate_working_dir/
12+
13+
# IntelliJ related
14+
*.iml
15+
*.ipr
16+
*.iws
17+
.idea/
18+
19+
# The .vscode folder contains launch configuration and tasks you configure in
20+
# VS Code which you may wish to be included in version control, so this line
21+
# is commented out by default.
22+
#.vscode/
23+
24+
# Flutter/Dart/Pub related
25+
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26+
/pubspec.lock
27+
**/doc/api/
28+
.dart_tool/
29+
.packages
30+
build/
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# link_hook
2+
3+
Test project for the native assets test to exercise adding assets during a link hook.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Run with `dart run ffigen --config ffigen.yaml`.
2+
name: LinkHookBindings
3+
description: |
4+
Bindings for `src/link_hook.h`.
5+
6+
Regenerate bindings with `dart run ffigen --config ffigen.yaml`.
7+
output: 'lib/link_hook_bindings_generated.dart'
8+
headers:
9+
entry-points:
10+
- 'src/link_hook.h'
11+
include-directives:
12+
- 'src/link_hook.h'
13+
ffi-native:
14+
preamble: |
15+
// ignore_for_file: always_specify_types
16+
// ignore_for_file: camel_case_types
17+
// ignore_for_file: non_constant_identifier_names
18+
comments:
19+
style: any
20+
length: full
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:logging/logging.dart';
6+
import 'package:native_assets_cli/native_assets_cli.dart';
7+
import 'package:native_toolchain_c/native_toolchain_c.dart';
8+
9+
10+
void main(List<String> args) async {
11+
await build(args, (BuildConfig config, BuildOutput output) async {
12+
final String packageName = config.packageName;
13+
final CBuilder cbuilder = CBuilder.library(
14+
name: packageName,
15+
assetName: 'some_asset_name_that_is_not_used',
16+
sources: <String>[
17+
'src/$packageName.c',
18+
],
19+
dartBuildFiles: <String>['hook/build.dart'],
20+
);
21+
final BuildOutput outputCatcher = BuildOutput();
22+
await cbuilder.run(
23+
buildConfig: config,
24+
buildOutput: outputCatcher,
25+
logger: Logger('')
26+
..level = Level.ALL
27+
..onRecord.listen((LogRecord record) => print(record.message)),
28+
);
29+
output.addDependencies(outputCatcher.dependencies);
30+
// Send the asset to hook/link.dart.
31+
output.addAsset(
32+
outputCatcher.assets.single,
33+
linkInPackage: 'link_hook',
34+
);
35+
});
36+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:native_assets_cli/native_assets_cli.dart';
6+
7+
void main(List<String> args) async {
8+
await link(args, (LinkConfig config, LinkOutput output) async {
9+
final NativeCodeAsset asset = config.assets.single as NativeCodeAsset;
10+
final String packageName = config.packageName;
11+
output.addAsset(NativeCodeAsset(
12+
package: packageName,
13+
// Change the asset id to something that is used.
14+
name: '${packageName}_bindings_generated.dart',
15+
linkMode: asset.linkMode,
16+
os: asset.os,
17+
architecture: asset.architecture,
18+
file: asset.file,
19+
));
20+
output.addDependency(config.packageRoot.resolve('hook/link.dart'));
21+
});
22+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'link_hook_bindings_generated.dart' as bindings;
6+
7+
/// A very short-lived native function.
8+
int difference(int a, int b) => bindings.difference(a, b);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// ignore_for_file: always_specify_types
6+
// ignore_for_file: camel_case_types
7+
// ignore_for_file: non_constant_identifier_names
8+
9+
// AUTO GENERATED FILE, DO NOT EDIT.
10+
//
11+
// Generated by `package:ffigen`.
12+
import 'dart:ffi' as ffi;
13+
14+
/// A very short-lived native function.
15+
@ffi.Native<ffi.IntPtr Function(ffi.IntPtr, ffi.IntPtr)>()
16+
external int difference(
17+
int a,
18+
int b,
19+
);
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: link_hook
2+
description: "A new Dart FFI package project."
3+
version: 0.0.1
4+
5+
environment:
6+
sdk: '>=3.5.0-154.0.dev <4.0.0'
7+
8+
dependencies:
9+
cli_config: 0.2.0
10+
logging: 1.2.0
11+
native_assets_cli: 0.6.0
12+
native_toolchain_c: 0.4.2
13+
14+
args: 2.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
15+
async: 2.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
16+
collection: 1.18.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
17+
crypto: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
18+
file: 7.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
19+
glob: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
20+
meta: 1.14.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
21+
path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
22+
pub_semver: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
23+
source_span: 1.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
24+
string_scanner: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
25+
term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
26+
typed_data: 1.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
27+
yaml: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
28+
29+
dev_dependencies:
30+
ffi: 2.1.2
31+
ffigen: 12.0.0
32+
flutter_lints: 4.0.0
33+
test: 1.25.5
34+
35+
_fe_analyzer_shared: 68.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
36+
analyzer: 6.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
37+
boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
38+
cli_util: 0.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
39+
convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
40+
coverage: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
41+
frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
42+
http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
43+
http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
44+
io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
45+
js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
46+
lints: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
47+
matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
48+
mime: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
49+
node_preamble: 2.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
50+
package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
51+
pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
52+
quiver: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
53+
shelf: 1.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
54+
shelf_packages_handler: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
55+
shelf_static: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
56+
shelf_web_socket: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
57+
source_map_stack_trace: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
58+
source_maps: 0.10.12 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
59+
stack_trace: 1.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
60+
stream_channel: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
61+
test_api: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
62+
test_core: 0.6.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
63+
vm_service: 14.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
64+
watcher: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
65+
web: 0.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
66+
web_socket_channel: 2.4.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
67+
webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
68+
yaml_edit: 2.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
69+
70+
# PUBSPEC CHECKSUM: e03f
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "link_hook.h"
6+
7+
// A very short-lived native function.
8+
//
9+
// For very short-lived functions, it is fine to call them on the main isolate.
10+
// They will block the Dart execution while running the native function, so
11+
// only do this for native functions which are guaranteed to be short-lived.
12+
FFI_PLUGIN_EXPORT intptr_t difference(intptr_t a, intptr_t b) { return a - b; }
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include <stdint.h>
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
9+
#if _WIN32
10+
#include <windows.h>
11+
#else
12+
#include <pthread.h>
13+
#include <unistd.h>
14+
#endif
15+
16+
#if _WIN32
17+
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
18+
#else
19+
#define FFI_PLUGIN_EXPORT
20+
#endif
21+
22+
FFI_PLUGIN_EXPORT intptr_t difference(intptr_t a, intptr_t b);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:link_hook/link_hook.dart';
6+
import 'package:test/test.dart';
7+
8+
void main() {
9+
test('invoke native function', () {
10+
// Tests are run in debug mode.
11+
expect(difference(24, 18), 24 - 18);
12+
});
13+
}

packages/flutter_tools/lib/src/isolated/native_assets/android/native_assets.dart

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,25 @@ Future<Iterable<KernelAsset>> dryRunNativeAssetsAndroidInternal(
5050
const OSImpl targetOS = OSImpl.android;
5151

5252
globals.logger.printTrace('Dry running native assets for $targetOS.');
53-
final DryRunResult dryRunResult = await buildRunner.dryRun(
53+
final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun(
5454
linkModePreference: LinkModePreferenceImpl.dynamic,
5555
targetOS: targetOS,
5656
workingDirectory: projectUri,
5757
includeParentEnvironment: true,
5858
);
59-
ensureNativeAssetsBuildSucceed(dryRunResult);
60-
final List<AssetImpl> nativeAssets = dryRunResult.assets;
59+
ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult);
60+
final LinkDryRunResult linkDryRunResult = await buildRunner.linkDryRun(
61+
linkModePreference: LinkModePreferenceImpl.dynamic,
62+
targetOS: targetOS,
63+
workingDirectory: projectUri,
64+
includeParentEnvironment: true,
65+
buildDryRunResult: buildDryRunResult,
66+
);
67+
ensureNativeAssetsLinkDryRunSucceed(linkDryRunResult);
68+
final List<AssetImpl> nativeAssets = <AssetImpl>[
69+
...buildDryRunResult.assets,
70+
...linkDryRunResult.assets,
71+
];
6172
ensureNoLinkModeStatic(nativeAssets);
6273
globals.logger.printTrace('Dry running native assets for $targetOS done.');
6374
final Map<AssetImpl, KernelAsset> assetTargetLocations =
@@ -97,18 +108,31 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)>
97108
final List<AssetImpl> nativeAssets = <AssetImpl>[];
98109
final Set<Uri> dependencies = <Uri>{};
99110
for (final Target target in targets) {
100-
final BuildResult result = await buildRunner.build(
111+
final BuildResult buildResult = await buildRunner.build(
112+
linkModePreference: LinkModePreferenceImpl.dynamic,
113+
target: target,
114+
buildMode: buildModeCli,
115+
workingDirectory: projectUri,
116+
includeParentEnvironment: true,
117+
cCompilerConfig: await buildRunner.ndkCCompilerConfigImpl,
118+
targetAndroidNdkApi: targetAndroidNdkApi,
119+
);
120+
ensureNativeAssetsBuildSucceed(buildResult);
121+
nativeAssets.addAll(buildResult.assets);
122+
dependencies.addAll(buildResult.dependencies);
123+
final LinkResult linkResult = await buildRunner.link(
101124
linkModePreference: LinkModePreferenceImpl.dynamic,
102125
target: target,
103126
buildMode: buildModeCli,
104127
workingDirectory: projectUri,
105128
includeParentEnvironment: true,
106129
cCompilerConfig: await buildRunner.ndkCCompilerConfigImpl,
107130
targetAndroidNdkApi: targetAndroidNdkApi,
131+
buildResult: buildResult,
108132
);
109-
ensureNativeAssetsBuildSucceed(result);
110-
nativeAssets.addAll(result.assets);
111-
dependencies.addAll(result.dependencies);
133+
ensureNativeAssetsLinkSucceed(linkResult);
134+
nativeAssets.addAll(linkResult.assets);
135+
dependencies.addAll(linkResult.dependencies);
112136
}
113137
ensureNoLinkModeStatic(nativeAssets);
114138
globals.logger.printTrace('Building native assets for $targets done.');

packages/flutter_tools/lib/src/isolated/native_assets/ios/native_assets.dart

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,25 @@ Future<Iterable<KernelAsset>> dryRunNativeAssetsIOSInternal(
4848
) async {
4949
const OSImpl targetOS = OSImpl.iOS;
5050
globals.logger.printTrace('Dry running native assets for $targetOS.');
51-
final DryRunResult dryRunResult = await buildRunner.dryRun(
51+
final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun(
5252
linkModePreference: LinkModePreferenceImpl.dynamic,
5353
targetOS: targetOS,
5454
workingDirectory: projectUri,
5555
includeParentEnvironment: true,
5656
);
57-
ensureNativeAssetsBuildSucceed(dryRunResult);
58-
final List<AssetImpl> nativeAssets = dryRunResult.assets;
57+
ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult);
58+
final LinkDryRunResult linkDryRunResult = await buildRunner.linkDryRun(
59+
linkModePreference: LinkModePreferenceImpl.dynamic,
60+
targetOS: targetOS,
61+
workingDirectory: projectUri,
62+
includeParentEnvironment: true,
63+
buildDryRunResult: buildDryRunResult,
64+
);
65+
ensureNativeAssetsLinkDryRunSucceed(linkDryRunResult);
66+
final List<AssetImpl> nativeAssets = <AssetImpl>[
67+
...buildDryRunResult.assets,
68+
...linkDryRunResult.assets,
69+
];
5970
ensureNoLinkModeStatic(nativeAssets);
6071
globals.logger.printTrace('Dry running native assets for $targetOS done.');
6172
return _assetTargetLocations(nativeAssets).values;
@@ -88,18 +99,31 @@ Future<List<Uri>> buildNativeAssetsIOS({
8899
final List<AssetImpl> nativeAssets = <AssetImpl>[];
89100
final Set<Uri> dependencies = <Uri>{};
90101
for (final Target target in targets) {
91-
final BuildResult result = await buildRunner.build(
102+
final BuildResult buildResult = await buildRunner.build(
103+
linkModePreference: LinkModePreferenceImpl.dynamic,
104+
target: target,
105+
targetIOSSdkImpl: iosSdk,
106+
buildMode: buildModeCli,
107+
workingDirectory: projectUri,
108+
includeParentEnvironment: true,
109+
cCompilerConfig: await buildRunner.cCompilerConfig,
110+
);
111+
ensureNativeAssetsBuildSucceed(buildResult);
112+
nativeAssets.addAll(buildResult.assets);
113+
dependencies.addAll(buildResult.dependencies);
114+
final LinkResult linkResult = await buildRunner.link(
92115
linkModePreference: LinkModePreferenceImpl.dynamic,
93116
target: target,
94117
targetIOSSdkImpl: iosSdk,
95118
buildMode: buildModeCli,
96119
workingDirectory: projectUri,
97120
includeParentEnvironment: true,
98121
cCompilerConfig: await buildRunner.cCompilerConfig,
122+
buildResult: buildResult,
99123
);
100-
ensureNativeAssetsBuildSucceed(result);
101-
nativeAssets.addAll(result.assets);
102-
dependencies.addAll(result.dependencies);
124+
ensureNativeAssetsLinkSucceed(linkResult);
125+
nativeAssets.addAll(linkResult.assets);
126+
dependencies.addAll(linkResult.dependencies);
103127
}
104128
ensureNoLinkModeStatic(nativeAssets);
105129
globals.logger.printTrace('Building native assets for $targets done.');

0 commit comments

Comments
 (0)