From 1f16d9121c7c44a1e2a84f7801411a9ab32467db Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Wed, 22 May 2024 18:02:00 +0200 Subject: [PATCH] [native_assets] Add support for link hooks (#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 (https://github.com/dart-lang/sdk/issues/55494). Issue: * https://github.com/flutter/flutter/issues/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.) --- dev/integration_tests/link_hook/.gitignore | 30 ++++ dev/integration_tests/link_hook/README.md | 3 + dev/integration_tests/link_hook/ffigen.yaml | 20 +++ .../link_hook/hook/build.dart | 36 ++++ .../link_hook/hook/link.dart | 22 +++ .../link_hook/lib/link_hook.dart | 8 + .../lib/link_hook_bindings_generated.dart | 19 ++ dev/integration_tests/link_hook/pubspec.yaml | 70 ++++++++ .../link_hook/src/link_hook.c | 12 ++ .../link_hook/src/link_hook.h | 22 +++ .../link_hook/test/link_hook_test.dart | 13 ++ .../native_assets/android/native_assets.dart | 38 +++- .../native_assets/ios/native_assets.dart | 38 +++- .../native_assets/macos/native_assets.dart | 37 +++- .../isolated/native_assets/native_assets.dart | 155 ++++++++++++++-- packages/flutter_tools/pubspec.yaml | 6 +- .../templates/package_ffi/pubspec.yaml.tmpl | 8 +- .../isolated/android/native_assets_test.dart | 167 +++++++++-------- .../fake_native_assets_build_runner.dart | 57 +++++- .../test/general.shard/isolated/hot_test.dart | 8 +- .../isolated/ios/native_assets_test.dart | 168 ++++++++++-------- .../isolated/linux/native_assets_test.dart | 166 +++++++++-------- .../isolated/macos/native_assets_test.dart | 168 ++++++++++-------- .../isolated/resident_runner_test.dart | 4 +- .../isolated/windows/native_assets_test.dart | 152 +++++++++------- .../isolated/native_assets_test.dart | 48 +++++ 26 files changed, 1054 insertions(+), 421 deletions(-) create mode 100644 dev/integration_tests/link_hook/.gitignore create mode 100644 dev/integration_tests/link_hook/README.md create mode 100644 dev/integration_tests/link_hook/ffigen.yaml create mode 100644 dev/integration_tests/link_hook/hook/build.dart create mode 100644 dev/integration_tests/link_hook/hook/link.dart create mode 100644 dev/integration_tests/link_hook/lib/link_hook.dart create mode 100644 dev/integration_tests/link_hook/lib/link_hook_bindings_generated.dart create mode 100644 dev/integration_tests/link_hook/pubspec.yaml create mode 100644 dev/integration_tests/link_hook/src/link_hook.c create mode 100644 dev/integration_tests/link_hook/src/link_hook.h create mode 100644 dev/integration_tests/link_hook/test/link_hook_test.dart diff --git a/dev/integration_tests/link_hook/.gitignore b/dev/integration_tests/link_hook/.gitignore new file mode 100644 index 00000000000..96486fd9302 --- /dev/null +++ b/dev/integration_tests/link_hook/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/dev/integration_tests/link_hook/README.md b/dev/integration_tests/link_hook/README.md new file mode 100644 index 00000000000..121b301b49e --- /dev/null +++ b/dev/integration_tests/link_hook/README.md @@ -0,0 +1,3 @@ +# link_hook + +Test project for the native assets test to exercise adding assets during a link hook. diff --git a/dev/integration_tests/link_hook/ffigen.yaml b/dev/integration_tests/link_hook/ffigen.yaml new file mode 100644 index 00000000000..eeabc65ac1e --- /dev/null +++ b/dev/integration_tests/link_hook/ffigen.yaml @@ -0,0 +1,20 @@ +# Run with `dart run ffigen --config ffigen.yaml`. +name: LinkHookBindings +description: | + Bindings for `src/link_hook.h`. + + Regenerate bindings with `dart run ffigen --config ffigen.yaml`. +output: 'lib/link_hook_bindings_generated.dart' +headers: + entry-points: + - 'src/link_hook.h' + include-directives: + - 'src/link_hook.h' +ffi-native: +preamble: | + // ignore_for_file: always_specify_types + // ignore_for_file: camel_case_types + // ignore_for_file: non_constant_identifier_names +comments: + style: any + length: full diff --git a/dev/integration_tests/link_hook/hook/build.dart b/dev/integration_tests/link_hook/hook/build.dart new file mode 100644 index 00000000000..19fb66ccc25 --- /dev/null +++ b/dev/integration_tests/link_hook/hook/build.dart @@ -0,0 +1,36 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:logging/logging.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_toolchain_c/native_toolchain_c.dart'; + + +void main(List args) async { + await build(args, (BuildConfig config, BuildOutput output) async { + final String packageName = config.packageName; + final CBuilder cbuilder = CBuilder.library( + name: packageName, + assetName: 'some_asset_name_that_is_not_used', + sources: [ + 'src/$packageName.c', + ], + dartBuildFiles: ['hook/build.dart'], + ); + final BuildOutput outputCatcher = BuildOutput(); + await cbuilder.run( + buildConfig: config, + buildOutput: outputCatcher, + logger: Logger('') + ..level = Level.ALL + ..onRecord.listen((LogRecord record) => print(record.message)), + ); + output.addDependencies(outputCatcher.dependencies); + // Send the asset to hook/link.dart. + output.addAsset( + outputCatcher.assets.single, + linkInPackage: 'link_hook', + ); + }); +} diff --git a/dev/integration_tests/link_hook/hook/link.dart b/dev/integration_tests/link_hook/hook/link.dart new file mode 100644 index 00000000000..3c37155587f --- /dev/null +++ b/dev/integration_tests/link_hook/hook/link.dart @@ -0,0 +1,22 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:native_assets_cli/native_assets_cli.dart'; + +void main(List args) async { + await link(args, (LinkConfig config, LinkOutput output) async { + final NativeCodeAsset asset = config.assets.single as NativeCodeAsset; + final String packageName = config.packageName; + output.addAsset(NativeCodeAsset( + package: packageName, + // Change the asset id to something that is used. + name: '${packageName}_bindings_generated.dart', + linkMode: asset.linkMode, + os: asset.os, + architecture: asset.architecture, + file: asset.file, + )); + output.addDependency(config.packageRoot.resolve('hook/link.dart')); + }); +} diff --git a/dev/integration_tests/link_hook/lib/link_hook.dart b/dev/integration_tests/link_hook/lib/link_hook.dart new file mode 100644 index 00000000000..8bbe2db2858 --- /dev/null +++ b/dev/integration_tests/link_hook/lib/link_hook.dart @@ -0,0 +1,8 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'link_hook_bindings_generated.dart' as bindings; + +/// A very short-lived native function. +int difference(int a, int b) => bindings.difference(a, b); diff --git a/dev/integration_tests/link_hook/lib/link_hook_bindings_generated.dart b/dev/integration_tests/link_hook/lib/link_hook_bindings_generated.dart new file mode 100644 index 00000000000..02a533ca93f --- /dev/null +++ b/dev/integration_tests/link_hook/lib/link_hook_bindings_generated.dart @@ -0,0 +1,19 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: always_specify_types +// ignore_for_file: camel_case_types +// ignore_for_file: non_constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +import 'dart:ffi' as ffi; + +/// A very short-lived native function. +@ffi.Native() +external int difference( + int a, + int b, +); diff --git a/dev/integration_tests/link_hook/pubspec.yaml b/dev/integration_tests/link_hook/pubspec.yaml new file mode 100644 index 00000000000..32516fe290a --- /dev/null +++ b/dev/integration_tests/link_hook/pubspec.yaml @@ -0,0 +1,70 @@ +name: link_hook +description: "A new Dart FFI package project." +version: 0.0.1 + +environment: + sdk: '>=3.5.0-154.0.dev <4.0.0' + +dependencies: + cli_config: 0.2.0 + logging: 1.2.0 + native_assets_cli: 0.6.0 + native_toolchain_c: 0.4.2 + + args: 2.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + async: 2.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + collection: 1.18.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + crypto: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + file: 7.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + glob: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + meta: 1.14.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + pub_semver: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + source_span: 1.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + string_scanner: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + typed_data: 1.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + yaml: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + +dev_dependencies: + ffi: 2.1.2 + ffigen: 12.0.0 + flutter_lints: 4.0.0 + test: 1.25.5 + + _fe_analyzer_shared: 68.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 6.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + cli_util: 0.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + coverage: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + lints: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + node_preamble: 2.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + quiver: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf: 1.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf_packages_handler: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf_static: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf_web_socket: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + source_map_stack_trace: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + source_maps: 0.10.12 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + stack_trace: 1.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + stream_channel: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + test_api: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + test_core: 0.6.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + vm_service: 14.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + watcher: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web: 0.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 2.4.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + yaml_edit: 2.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + +# PUBSPEC CHECKSUM: e03f diff --git a/dev/integration_tests/link_hook/src/link_hook.c b/dev/integration_tests/link_hook/src/link_hook.c new file mode 100644 index 00000000000..d0086495a3b --- /dev/null +++ b/dev/integration_tests/link_hook/src/link_hook.c @@ -0,0 +1,12 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "link_hook.h" + +// A very short-lived native function. +// +// For very short-lived functions, it is fine to call them on the main isolate. +// They will block the Dart execution while running the native function, so +// only do this for native functions which are guaranteed to be short-lived. +FFI_PLUGIN_EXPORT intptr_t difference(intptr_t a, intptr_t b) { return a - b; } diff --git a/dev/integration_tests/link_hook/src/link_hook.h b/dev/integration_tests/link_hook/src/link_hook.h new file mode 100644 index 00000000000..569c8f16382 --- /dev/null +++ b/dev/integration_tests/link_hook/src/link_hook.h @@ -0,0 +1,22 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +#if _WIN32 +#include +#else +#include +#include +#endif + +#if _WIN32 +#define FFI_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FFI_PLUGIN_EXPORT +#endif + +FFI_PLUGIN_EXPORT intptr_t difference(intptr_t a, intptr_t b); diff --git a/dev/integration_tests/link_hook/test/link_hook_test.dart b/dev/integration_tests/link_hook/test/link_hook_test.dart new file mode 100644 index 00000000000..cf142e73a90 --- /dev/null +++ b/dev/integration_tests/link_hook/test/link_hook_test.dart @@ -0,0 +1,13 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:link_hook/link_hook.dart'; +import 'package:test/test.dart'; + +void main() { + test('invoke native function', () { + // Tests are run in debug mode. + expect(difference(24, 18), 24 - 18); + }); +} diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/android/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/android/native_assets.dart index 012cab9ddeb..421e352b30e 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/android/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/android/native_assets.dart @@ -50,14 +50,25 @@ Future> dryRunNativeAssetsAndroidInternal( const OSImpl targetOS = OSImpl.android; globals.logger.printTrace('Dry running native assets for $targetOS.'); - final DryRunResult dryRunResult = await buildRunner.dryRun( + final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun( linkModePreference: LinkModePreferenceImpl.dynamic, targetOS: targetOS, workingDirectory: projectUri, includeParentEnvironment: true, ); - ensureNativeAssetsBuildSucceed(dryRunResult); - final List nativeAssets = dryRunResult.assets; + ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult); + final LinkDryRunResult linkDryRunResult = await buildRunner.linkDryRun( + linkModePreference: LinkModePreferenceImpl.dynamic, + targetOS: targetOS, + workingDirectory: projectUri, + includeParentEnvironment: true, + buildDryRunResult: buildDryRunResult, + ); + ensureNativeAssetsLinkDryRunSucceed(linkDryRunResult); + final List nativeAssets = [ + ...buildDryRunResult.assets, + ...linkDryRunResult.assets, + ]; ensureNoLinkModeStatic(nativeAssets); globals.logger.printTrace('Dry running native assets for $targetOS done.'); final Map assetTargetLocations = @@ -97,7 +108,7 @@ Future<(Uri? nativeAssetsYaml, List dependencies)> final List nativeAssets = []; final Set dependencies = {}; for (final Target target in targets) { - final BuildResult result = await buildRunner.build( + final BuildResult buildResult = await buildRunner.build( linkModePreference: LinkModePreferenceImpl.dynamic, target: target, buildMode: buildModeCli, @@ -106,9 +117,22 @@ Future<(Uri? nativeAssetsYaml, List dependencies)> cCompilerConfig: await buildRunner.ndkCCompilerConfigImpl, targetAndroidNdkApi: targetAndroidNdkApi, ); - ensureNativeAssetsBuildSucceed(result); - nativeAssets.addAll(result.assets); - dependencies.addAll(result.dependencies); + ensureNativeAssetsBuildSucceed(buildResult); + nativeAssets.addAll(buildResult.assets); + dependencies.addAll(buildResult.dependencies); + final LinkResult linkResult = await buildRunner.link( + linkModePreference: LinkModePreferenceImpl.dynamic, + target: target, + buildMode: buildModeCli, + workingDirectory: projectUri, + includeParentEnvironment: true, + cCompilerConfig: await buildRunner.ndkCCompilerConfigImpl, + targetAndroidNdkApi: targetAndroidNdkApi, + buildResult: buildResult, + ); + ensureNativeAssetsLinkSucceed(linkResult); + nativeAssets.addAll(linkResult.assets); + dependencies.addAll(linkResult.dependencies); } ensureNoLinkModeStatic(nativeAssets); globals.logger.printTrace('Building native assets for $targets done.'); diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/ios/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/ios/native_assets.dart index 578908565b1..773b4528735 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/ios/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/ios/native_assets.dart @@ -48,14 +48,25 @@ Future> dryRunNativeAssetsIOSInternal( ) async { const OSImpl targetOS = OSImpl.iOS; globals.logger.printTrace('Dry running native assets for $targetOS.'); - final DryRunResult dryRunResult = await buildRunner.dryRun( + final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun( linkModePreference: LinkModePreferenceImpl.dynamic, targetOS: targetOS, workingDirectory: projectUri, includeParentEnvironment: true, ); - ensureNativeAssetsBuildSucceed(dryRunResult); - final List nativeAssets = dryRunResult.assets; + ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult); + final LinkDryRunResult linkDryRunResult = await buildRunner.linkDryRun( + linkModePreference: LinkModePreferenceImpl.dynamic, + targetOS: targetOS, + workingDirectory: projectUri, + includeParentEnvironment: true, + buildDryRunResult: buildDryRunResult, + ); + ensureNativeAssetsLinkDryRunSucceed(linkDryRunResult); + final List nativeAssets = [ + ...buildDryRunResult.assets, + ...linkDryRunResult.assets, + ]; ensureNoLinkModeStatic(nativeAssets); globals.logger.printTrace('Dry running native assets for $targetOS done.'); return _assetTargetLocations(nativeAssets).values; @@ -88,7 +99,7 @@ Future> buildNativeAssetsIOS({ final List nativeAssets = []; final Set dependencies = {}; for (final Target target in targets) { - final BuildResult result = await buildRunner.build( + final BuildResult buildResult = await buildRunner.build( linkModePreference: LinkModePreferenceImpl.dynamic, target: target, targetIOSSdkImpl: iosSdk, @@ -97,9 +108,22 @@ Future> buildNativeAssetsIOS({ includeParentEnvironment: true, cCompilerConfig: await buildRunner.cCompilerConfig, ); - ensureNativeAssetsBuildSucceed(result); - nativeAssets.addAll(result.assets); - dependencies.addAll(result.dependencies); + ensureNativeAssetsBuildSucceed(buildResult); + nativeAssets.addAll(buildResult.assets); + dependencies.addAll(buildResult.dependencies); + final LinkResult linkResult = await buildRunner.link( + linkModePreference: LinkModePreferenceImpl.dynamic, + target: target, + targetIOSSdkImpl: iosSdk, + buildMode: buildModeCli, + workingDirectory: projectUri, + includeParentEnvironment: true, + cCompilerConfig: await buildRunner.cCompilerConfig, + buildResult: buildResult, + ); + ensureNativeAssetsLinkSucceed(linkResult); + nativeAssets.addAll(linkResult.assets); + dependencies.addAll(linkResult.dependencies); } ensureNoLinkModeStatic(nativeAssets); globals.logger.printTrace('Building native assets for $targets done.'); diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets.dart index 54677a63887..c506c7f80a1 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/macos/native_assets.dart @@ -52,14 +52,25 @@ Future> dryRunNativeAssetsMacOSInternal( final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS); globals.logger.printTrace('Dry running native assets for $targetOS.'); - final DryRunResult dryRunResult = await buildRunner.dryRun( + final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun( linkModePreference: LinkModePreferenceImpl.dynamic, targetOS: targetOS, workingDirectory: projectUri, includeParentEnvironment: true, ); - ensureNativeAssetsBuildSucceed(dryRunResult); - final List nativeAssets = dryRunResult.assets; + ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult); + final LinkDryRunResult linkDryRunResult = await buildRunner.linkDryRun( + linkModePreference: LinkModePreferenceImpl.dynamic, + targetOS: targetOS, + workingDirectory: projectUri, + includeParentEnvironment: true, + buildDryRunResult: buildDryRunResult, + ); + ensureNativeAssetsLinkDryRunSucceed(linkDryRunResult); + final List nativeAssets = [ + ...buildDryRunResult.assets, + ...linkDryRunResult.assets, + ]; ensureNoLinkModeStatic(nativeAssets); globals.logger.printTrace('Dry running native assets for $targetOS done.'); final Uri? absolutePath = flutterTester ? buildUri : null; @@ -110,7 +121,7 @@ Future<(Uri? nativeAssetsYaml, List dependencies)> buildNativeAssetsMacOS({ final List nativeAssets = []; final Set dependencies = {}; for (final Target target in targets) { - final BuildResult result = await buildRunner.build( + final BuildResult buildResult = await buildRunner.build( linkModePreference: LinkModePreferenceImpl.dynamic, target: target, buildMode: buildModeCli, @@ -118,9 +129,21 @@ Future<(Uri? nativeAssetsYaml, List dependencies)> buildNativeAssetsMacOS({ includeParentEnvironment: true, cCompilerConfig: await buildRunner.cCompilerConfig, ); - ensureNativeAssetsBuildSucceed(result); - nativeAssets.addAll(result.assets); - dependencies.addAll(result.dependencies); + ensureNativeAssetsBuildSucceed(buildResult); + nativeAssets.addAll(buildResult.assets); + dependencies.addAll(buildResult.dependencies); + final LinkResult linkResult = await buildRunner.link( + linkModePreference: LinkModePreferenceImpl.dynamic, + target: target, + buildMode: buildModeCli, + workingDirectory: projectUri, + includeParentEnvironment: true, + cCompilerConfig: await buildRunner.cCompilerConfig, + buildResult: buildResult, + ); + ensureNativeAssetsLinkSucceed(linkResult); + nativeAssets.addAll(linkResult.assets); + dependencies.addAll(linkResult.dependencies); } ensureNoLinkModeStatic(nativeAssets); globals.logger.printTrace('Building native assets for $targets done.'); diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart index f644246fda5..843c7b3ca77 100644 --- a/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart +++ b/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart @@ -38,14 +38,14 @@ abstract class NativeAssetsBuildRunner { /// Whether the project has a `.dart_tools/package_config.json`. /// /// If there is no package config, [packagesWithNativeAssets], [build], and - /// [dryRun] must not be invoked. + /// [buildDryRun] must not be invoked. Future hasPackageConfig(); /// All packages in the transitive dependencies that have a `build.dart`. Future> packagesWithNativeAssets(); /// Runs all [packagesWithNativeAssets] `build.dart` in dry run. - Future dryRun({ + Future buildDryRun({ required bool includeParentEnvironment, required LinkModePreferenceImpl linkModePreference, required OSImpl targetOS, @@ -64,6 +64,28 @@ abstract class NativeAssetsBuildRunner { IOSSdkImpl? targetIOSSdkImpl, }); + /// Runs all [packagesWithNativeAssets] `link.dart` in dry run. + Future linkDryRun({ + required bool includeParentEnvironment, + required LinkModePreferenceImpl linkModePreference, + required OSImpl targetOS, + required Uri workingDirectory, + required BuildDryRunResult buildDryRunResult, + }); + + /// Runs all [packagesWithNativeAssets] `link.dart`. + Future link({ + required bool includeParentEnvironment, + required BuildModeImpl buildMode, + required LinkModePreferenceImpl linkModePreference, + required Target target, + required Uri workingDirectory, + required BuildResult buildResult, + CCompilerConfigImpl? cCompilerConfig, + int? targetAndroidNdkApi, + IOSSdkImpl? targetIOSSdkImpl, + }); + /// The C compiler config to use for compilation. Future get cCompilerConfig; @@ -120,11 +142,14 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner { packageConfig, projectUri.resolve('.dart_tool/package_config.json'), ); - return packageLayout.packagesWithNativeAssets; + // It suffices to only check for build hooks. If no packages have a build + // hook. Then no build hook will output any assets for any link hook, and + // thus the link hooks will never be run. + return packageLayout.packagesWithAssets(Hook.build); } @override - Future dryRun({ + Future buildDryRun({ required bool includeParentEnvironment, required LinkModePreferenceImpl linkModePreference, required OSImpl targetOS, @@ -134,7 +159,7 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner { packageConfig, projectUri.resolve('.dart_tool/package_config.json'), ); - return _buildRunner.dryRun( + return _buildRunner.buildDryRun( includeParentEnvironment: includeParentEnvironment, linkModePreference: linkModePreference, targetOS: targetOS, @@ -171,6 +196,59 @@ class NativeAssetsBuildRunnerImpl implements NativeAssetsBuildRunner { ); } + + @override + Future linkDryRun({ + required bool includeParentEnvironment, + required LinkModePreferenceImpl linkModePreference, + required OSImpl targetOS, + required Uri workingDirectory, + required BuildDryRunResult buildDryRunResult, + }) { + final PackageLayout packageLayout = PackageLayout.fromPackageConfig( + packageConfig, + projectUri.resolve('.dart_tool/package_config.json'), + ); + return _buildRunner.linkDryRun( + includeParentEnvironment: includeParentEnvironment, + linkModePreference: linkModePreference, + targetOS: targetOS, + workingDirectory: workingDirectory, + packageLayout: packageLayout, + buildDryRunResult: buildDryRunResult, + ); + } + + @override + Future link({ + required bool includeParentEnvironment, + required BuildModeImpl buildMode, + required LinkModePreferenceImpl linkModePreference, + required Target target, + required Uri workingDirectory, + required BuildResult buildResult, + CCompilerConfigImpl? cCompilerConfig, + int? targetAndroidNdkApi, + IOSSdkImpl? targetIOSSdkImpl, + }) { + final PackageLayout packageLayout = PackageLayout.fromPackageConfig( + packageConfig, + projectUri.resolve('.dart_tool/package_config.json'), + ); + return _buildRunner.link( + buildMode: buildMode, + cCompilerConfig: cCompilerConfig, + includeParentEnvironment: includeParentEnvironment, + linkModePreference: linkModePreference, + target: target, + targetAndroidNdkApi: targetAndroidNdkApi, + targetIOSSdk: targetIOSSdkImpl, + workingDirectory: workingDirectory, + packageLayout: packageLayout, + buildResult: buildResult, + ); + } + @override late final Future cCompilerConfig = () { if (globals.platform.isMacOS || globals.platform.isIOS) { @@ -560,14 +638,25 @@ Future> dryRunNativeAssetsSingleArchitectureInternal( globals.logger.printTrace('Dry running native assets for $targetOS.'); - final DryRunResult dryRunResult = await buildRunner.dryRun( + final BuildDryRunResult buildDryRunResult = await buildRunner.buildDryRun( linkModePreference: LinkModePreferenceImpl.dynamic, targetOS: targetOS, workingDirectory: projectUri, includeParentEnvironment: true, ); - ensureNativeAssetsBuildSucceed(dryRunResult); - final List nativeAssets = dryRunResult.assets; + ensureNativeAssetsBuildDryRunSucceed(buildDryRunResult); + final LinkDryRunResult linkDryRunResult = await buildRunner.linkDryRun( + linkModePreference: LinkModePreferenceImpl.dynamic, + targetOS: targetOS, + workingDirectory: projectUri, + includeParentEnvironment: true, + buildDryRunResult: buildDryRunResult, + ); + ensureNativeAssetsLinkDryRunSucceed(linkDryRunResult); + final List nativeAssets = [ + ...buildDryRunResult.assets, + ...linkDryRunResult.assets, + ]; ensureNoLinkModeStatic(nativeAssets); globals.logger.printTrace('Dry running native assets for $targetOS done.'); final Uri? absolutePath = flutterTester ? buildUri : null; @@ -615,7 +704,7 @@ Future<(Uri? nativeAssetsYaml, List dependencies)> buildNativeAssetsSingleA final BuildModeImpl buildModeCli = nativeAssetsBuildMode(buildMode); globals.logger.printTrace('Building native assets for $target $buildModeCli.'); - final BuildResult result = await buildRunner.build( + final BuildResult buildResult = await buildRunner.build( linkModePreference: LinkModePreferenceImpl.dynamic, target: target, buildMode: buildModeCli, @@ -623,9 +712,25 @@ Future<(Uri? nativeAssetsYaml, List dependencies)> buildNativeAssetsSingleA includeParentEnvironment: true, cCompilerConfig: await buildRunner.cCompilerConfig, ); - ensureNativeAssetsBuildSucceed(result); - final List nativeAssets = result.assets; - final Set dependencies = result.dependencies.toSet(); + ensureNativeAssetsBuildSucceed(buildResult); + final LinkResult linkResult = await buildRunner.link( + linkModePreference: LinkModePreferenceImpl.dynamic, + target: target, + buildMode: buildModeCli, + workingDirectory: projectUri, + includeParentEnvironment: true, + cCompilerConfig: await buildRunner.ndkCCompilerConfigImpl, + buildResult: buildResult, + ); + ensureNativeAssetsLinkSucceed(linkResult); + final List nativeAssets = [ + ...buildResult.assets, + ...linkResult.assets, + ]; + final Set dependencies = { + ...buildResult.dependencies, + ...linkResult.dependencies, + }; ensureNoLinkModeStatic(nativeAssets); globals.logger.printTrace('Building native assets for $target done.'); final Uri? absolutePath = flutterTester ? buildUri : null; @@ -751,10 +856,34 @@ Future _copyNativeAssetsSingleArchitecture( } } -void ensureNativeAssetsBuildSucceed(DryRunResult result) { +void ensureNativeAssetsBuildDryRunSucceed(BuildDryRunResult result) { + if (!result.success) { + throwToolExit( + 'Building (dry run) native assets failed. See the logs for more details.', + ); + } +} + +void ensureNativeAssetsBuildSucceed(BuildResult result) { if (!result.success) { throwToolExit( 'Building native assets failed. See the logs for more details.', ); } } + +void ensureNativeAssetsLinkDryRunSucceed(LinkDryRunResult result) { + if (!result.success) { + throwToolExit( + 'Linking (dry run) native assets failed. See the logs for more details.', + ); + } +} + +void ensureNativeAssetsLinkSucceed(LinkResult result) { + if (!result.success) { + throwToolExit( + 'Linking native assets failed. See the logs for more details.', + ); + } +} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 16e17863af0..d71ee0e5637 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -56,8 +56,8 @@ dependencies: cli_config: 0.2.0 graphs: 2.3.1 - native_assets_builder: 0.6.1 - native_assets_cli: 0.5.4 + native_assets_builder: 0.7.0 + native_assets_cli: 0.6.0 # We depend on very specific internal implementation details of the # 'test' package, which change between versions, so when upgrading @@ -120,4 +120,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: d60f +# PUBSPEC CHECKSUM: 3f0c diff --git a/packages/flutter_tools/templates/package_ffi/pubspec.yaml.tmpl b/packages/flutter_tools/templates/package_ffi/pubspec.yaml.tmpl index 093b354f791..fa72154cdfc 100644 --- a/packages/flutter_tools/templates/package_ffi/pubspec.yaml.tmpl +++ b/packages/flutter_tools/templates/package_ffi/pubspec.yaml.tmpl @@ -9,11 +9,11 @@ environment: dependencies: cli_config: ^0.2.0 logging: ^1.2.0 - native_assets_cli: ^0.5.3 - native_toolchain_c: ^0.4.1 + native_assets_cli: ^0.6.0 + native_toolchain_c: ^0.4.2 dev_dependencies: - ffi: ^2.1.0 - ffigen: ^11.0.0 + ffi: ^2.1.2 + ffigen: ^12.0.0 flutter_lints: ^4.0.0 test: ^1.24.9 diff --git a/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart index 17fb07bba9f..60ca0609a55 100644 --- a/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart @@ -117,32 +117,33 @@ void main() { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.macOS, + architecture: ArchitectureImpl.arm64, + file: Uri.file('libbar.so'), + ), + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.macOS, + architecture: ArchitectureImpl.x64, + file: Uri.file('libbar.so'), + ), + ], + ), + ); final Uri? nativeAssetsYaml = await dryRunNativeAssetsAndroid( projectUri: projectUri, fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.arm64, - file: Uri.file('libbar.so'), - ), - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.x64, - file: Uri.file('libbar.so'), - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -159,6 +160,8 @@ void main() { await fileSystem.file(nativeAssetsYaml).readAsString(), contains('package:bar/bar.dart'), ); + expect(buildRunner.buildDryRunInvocations, 1); + expect(buildRunner.linkDryRunInvocations, 1); }); testUsingContext('build with assets but not enabled', () async { @@ -224,6 +227,22 @@ void main() { final File dylibAfterCompiling = fileSystem.file('libbar.so'); // The mock doesn't create the file, so create it here. await dylibAfterCompiling.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildResult: FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.android, + architecture: ArchitectureImpl.arm64, + file: Uri.file('libbar.so'), + ), + ], + ), + ); await buildNativeAssetsAndroid( androidArchs: [AndroidArch.arm64_v8a], targetAndroidNdkApi: 21, @@ -231,22 +250,7 @@ void main() { buildMode: BuildMode.debug, fileSystem: fileSystem, yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.android, - architecture: ArchitectureImpl.arm64, - file: Uri.file('libbar.so'), - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -259,6 +263,8 @@ void main() { environment.buildDir.childFile('native_assets.yaml'), exists, ); + expect(buildRunner.buildInvocations, 1); + expect(buildRunner.linkInvocations, 1); }); // Ensure no exceptions for a non installed NDK are thrown if no native @@ -314,7 +320,6 @@ void main() { ); }); - testUsingContext('Native assets dry run error', overrides: { FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true), ProcessManager: () => FakeProcessManager.empty(), @@ -323,24 +328,29 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => dryRunNativeAssetsAndroid( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => dryRunNativeAssetsAndroid( + projectUri: projectUri, + fileSystem: fileSystem, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook (dry run) native assets failed. See the logs for more details.', + ), + ); + } }); testUsingContext('Native assets build error', overrides: { @@ -351,28 +361,33 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => buildNativeAssetsAndroid( - androidArchs: [AndroidArch.arm64_v8a], - targetAndroidNdkApi: 21, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => buildNativeAssetsAndroid( + androidArchs: [AndroidArch.arm64_v8a], + targetAndroidNdkApi: 21, + projectUri: projectUri, + buildMode: BuildMode.debug, + fileSystem: fileSystem, + yamlParentDirectory: environment.buildDir.uri, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook native assets failed. See the logs for more details.', + ), + ); + } }); } diff --git a/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart b/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart index c3dd78062d1..88f51d59e3d 100644 --- a/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart +++ b/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart @@ -19,8 +19,10 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner { this.hasPackageConfigResult = true, this.packagesWithNativeAssetsResult = const [], this.onBuild, - this.dryRunResult = const FakeNativeAssetsBuilderResult(), + this.buildDryRunResult = const FakeNativeAssetsBuilderResult(), this.buildResult = const FakeNativeAssetsBuilderResult(), + this.linkResult = const FakeNativeAssetsBuilderResult(), + this.linkDryRunResult = const FakeNativeAssetsBuilderResult(), CCompilerConfigImpl? cCompilerConfigResult, CCompilerConfigImpl? ndkCCompilerConfigImplResult, }) : cCompilerConfigResult = cCompilerConfigResult ?? CCompilerConfigImpl(), @@ -29,14 +31,18 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner { final native_assets_builder.BuildResult Function(Target)? onBuild; final native_assets_builder.BuildResult buildResult; - final native_assets_builder.DryRunResult dryRunResult; + final native_assets_builder.LinkResult linkResult; + final native_assets_builder.BuildDryRunResult buildDryRunResult; + final native_assets_builder.LinkDryRunResult linkDryRunResult; final bool hasPackageConfigResult; final List packagesWithNativeAssetsResult; final CCompilerConfigImpl cCompilerConfigResult; final CCompilerConfigImpl ndkCCompilerConfigImplResult; int buildInvocations = 0; - int dryRunInvocations = 0; + int buildDryRunInvocations = 0; + int linkInvocations = 0; + int linkDryRunInvocations = 0; int hasPackageConfigInvocations = 0; int packagesWithNativeAssetsInvocations = 0; BuildModeImpl? lastBuildMode; @@ -58,14 +64,43 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner { } @override - Future dryRun({ + Future link({ + required bool includeParentEnvironment, + required BuildModeImpl buildMode, + required LinkModePreferenceImpl linkModePreference, + required Target target, + required Uri workingDirectory, + required native_assets_builder.BuildResult buildResult, + CCompilerConfigImpl? cCompilerConfig, + int? targetAndroidNdkApi, + IOSSdkImpl? targetIOSSdkImpl, + }) async { + linkInvocations++; + lastBuildMode = buildMode; + return linkResult; + } + + @override + Future buildDryRun({ required bool includeParentEnvironment, required LinkModePreferenceImpl linkModePreference, required OSImpl targetOS, required Uri workingDirectory, }) async { - dryRunInvocations++; - return dryRunResult; + buildDryRunInvocations++; + return buildDryRunResult; + } + + @override + Future linkDryRun({ + required bool includeParentEnvironment, + required LinkModePreferenceImpl linkModePreference, + required OSImpl targetOS, + required Uri workingDirectory, + required native_assets_builder.BuildDryRunResult buildDryRunResult, + }) async { + linkDryRunInvocations++; + return linkDryRunResult; } @override @@ -90,9 +125,14 @@ class FakeNativeAssetsBuildRunner implements NativeAssetsBuildRunner { } final class FakeNativeAssetsBuilderResult - implements native_assets_builder.BuildResult { + implements + native_assets_builder.BuildResult, + native_assets_builder.BuildDryRunResult, + native_assets_builder.LinkResult, + native_assets_builder.LinkDryRunResult { const FakeNativeAssetsBuilderResult({ this.assets = const [], + this.assetsForLinking = const >{}, this.dependencies = const [], this.success = true, }); @@ -100,6 +140,9 @@ final class FakeNativeAssetsBuilderResult @override final List assets; + @override + final Map> assetsForLinking; + @override final List dependencies; diff --git a/packages/flutter_tools/test/general.shard/isolated/hot_test.dart b/packages/flutter_tools/test/general.shard/isolated/hot_test.dart index b4a99f532fa..ff10468eec6 100644 --- a/packages/flutter_tools/test/general.shard/isolated/hot_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/hot_test.dart @@ -62,7 +62,7 @@ void main() { packagesWithNativeAssetsResult: [ Package('bar', fileSystem.currentDirectory.uri), ], - dryRunResult: FakeNativeAssetsBuilderResult( + buildDryRunResult: FakeNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', @@ -95,7 +95,9 @@ void main() { // Hot restart does not require rerunning anything for native assets. // The previous native assets mapping should be used. expect(buildRunner.buildInvocations, 0); - expect(buildRunner.dryRunInvocations, 0); + expect(buildRunner.buildDryRunInvocations, 0); + expect(buildRunner.linkInvocations, 0); + expect(buildRunner.linkDryRunInvocations, 0); expect(buildRunner.hasPackageConfigInvocations, 0); expect(buildRunner.packagesWithNativeAssetsInvocations, 0); }, overrides: { @@ -129,7 +131,7 @@ void main() { packagesWithNativeAssetsResult: [ Package('bar', fileSystem.currentDirectory.uri), ], - dryRunResult: FakeNativeAssetsBuilderResult( + buildDryRunResult: FakeNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', diff --git a/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart index d1320657569..94b7cb7ed12 100644 --- a/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart @@ -119,32 +119,33 @@ void main() { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.macOS, + architecture: ArchitectureImpl.arm64, + file: Uri.file('libbar.dylib'), + ), + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.macOS, + architecture: ArchitectureImpl.x64, + file: Uri.file('libbar.dylib'), + ), + ], + ), + ); final Uri? nativeAssetsYaml = await dryRunNativeAssetsIOS( projectUri: projectUri, fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.arm64, - file: Uri.file('libbar.dylib'), - ), - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.x64, - file: Uri.file('libbar.dylib'), - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -161,6 +162,8 @@ void main() { await fileSystem.file(nativeAssetsYaml).readAsString(), contains('package:bar/bar.dart'), ); + expect(buildRunner.buildDryRunInvocations, 1); + expect(buildRunner.linkDryRunInvocations, 1); }); testUsingContext('build with assets but not enabled', () async { @@ -255,6 +258,23 @@ void main() { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + onBuild: (native_assets_cli.Target target) => + FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: target.os, + architecture: target.architecture, + file: Uri.file('${target.architecture}/libbar.dylib'), + ), + ], + ), + ); await buildNativeAssetsIOS( darwinArchs: [DarwinArch.arm64, DarwinArch.x86_64], environmentType: EnvironmentType.simulator, @@ -262,22 +282,7 @@ void main() { buildMode: BuildMode.debug, fileSystem: fileSystem, yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - onBuild: (native_assets_cli.Target target) => FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: target.os, - architecture: target.architecture, - file: Uri.file('${target.architecture}/libbar.dylib'), - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -290,6 +295,9 @@ void main() { environment.buildDir.childFile('native_assets.yaml'), exists, ); + // Two archs. + expect(buildRunner.buildInvocations, 2); + expect(buildRunner.linkInvocations, 2); }); testUsingContext('Native assets dry run error', overrides: { @@ -300,24 +308,29 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => dryRunNativeAssetsIOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => dryRunNativeAssetsIOS( + projectUri: projectUri, + fileSystem: fileSystem, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook (dry run) native assets failed. See the logs for more details.', + ), + ); + } }); testUsingContext('Native assets build error', overrides: { @@ -328,27 +341,32 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => buildNativeAssetsIOS( - darwinArchs: [DarwinArch.arm64], - environmentType: EnvironmentType.simulator, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => buildNativeAssetsIOS( + darwinArchs: [DarwinArch.arm64], + environmentType: EnvironmentType.simulator, + projectUri: projectUri, + buildMode: BuildMode.debug, + fileSystem: fileSystem, + yamlParentDirectory: environment.buildDir.uri, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook native assets failed. See the logs for more details.', + ), + ); + } }); } diff --git a/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart index 99579559e8d..35593d90442 100644 --- a/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart @@ -155,32 +155,33 @@ void main() { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.linux, + architecture: ArchitectureImpl.x64, + file: Uri.file('libbar.so'), + ), + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.linux, + architecture: ArchitectureImpl.arm64, + file: Uri.file('libbar.so'), + ), + ], + ), + ); final Uri? nativeAssetsYaml = await dryRunNativeAssetsLinux( projectUri: projectUri, fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.linux, - architecture: ArchitectureImpl.x64, - file: Uri.file('libbar.so'), - ), - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.linux, - architecture: ArchitectureImpl.arm64, - file: Uri.file('libbar.so'), - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -197,6 +198,8 @@ void main() { await fileSystem.file(nativeAssetsYaml).readAsString(), contains('package:bar/bar.dart'), ); + expect(buildRunner.buildDryRunInvocations, 1); + expect(buildRunner.linkDryRunInvocations, 1); }); testUsingContext('build with assets but not enabled', overrides: { @@ -273,28 +276,29 @@ void main() { final File dylibAfterCompiling = fileSystem.file('libbar.so'); // The mock doesn't create the file, so create it here. await dylibAfterCompiling.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildResult: FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.linux, + architecture: ArchitectureImpl.x64, + file: dylibAfterCompiling.uri, + ), + ], + ), + ); final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsLinux( targetPlatform: TargetPlatform.linux_x64, projectUri: projectUri, buildMode: BuildMode.debug, fileSystem: fileSystem, flutterTester: flutterTester, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.linux, - architecture: ArchitectureImpl.x64, - file: dylibAfterCompiling.uri, - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -319,6 +323,8 @@ void main() { '- libbar.so', ]), ); + expect(buildRunner.buildInvocations, 1); + expect(buildRunner.linkInvocations, 1); }); } @@ -337,7 +343,7 @@ void main() { packagesWithNativeAssetsResult: [ Package('bar', projectUri), ], - dryRunResult: FakeNativeAssetsBuilderResult( + buildDryRunResult: FakeNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', @@ -373,24 +379,29 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => dryRunNativeAssetsLinux( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => dryRunNativeAssetsLinux( + projectUri: projectUri, + fileSystem: fileSystem, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook (dry run) native assets failed. See the logs for more details.', + ), + ); + } }); testUsingContext('Native assets build error', overrides: { @@ -401,27 +412,32 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => buildNativeAssetsLinux( - targetPlatform: TargetPlatform.linux_x64, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => buildNativeAssetsLinux( + targetPlatform: TargetPlatform.linux_x64, + projectUri: projectUri, + buildMode: BuildMode.debug, + fileSystem: fileSystem, + yamlParentDirectory: environment.buildDir.uri, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook native assets failed. See the logs for more details.', + ), + ); + } }); // This logic is mocked in the other tests to avoid having test order diff --git a/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart index f59fde6f867..69add748a0e 100644 --- a/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart @@ -138,32 +138,33 @@ void main() { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.macOS, + architecture: ArchitectureImpl.arm64, + file: Uri.file('libbar.dylib'), + ), + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.macOS, + architecture: ArchitectureImpl.x64, + file: Uri.file('libbar.dylib'), + ), + ], + ), + ); final Uri? nativeAssetsYaml = await dryRunNativeAssetsMacOS( projectUri: projectUri, fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.arm64, - file: Uri.file('libbar.dylib'), - ), - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.macOS, - architecture: ArchitectureImpl.x64, - file: Uri.file('libbar.dylib'), - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -180,6 +181,8 @@ void main() { await fileSystem.file(nativeAssetsYaml).readAsString(), contains('package:bar/bar.dart'), ); + expect(buildRunner.buildDryRunInvocations, 1); + expect(buildRunner.linkDryRunInvocations, 1); }); testUsingContext('build with assets but not enabled', overrides: { @@ -293,28 +296,30 @@ void main() { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + onBuild: (native_assets_cli.Target target) => + FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: target.os, + architecture: target.architecture, + file: Uri.file('${target.architecture}/libbar.dylib'), + ), + ], + ), + ); final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsMacOS( darwinArchs: [DarwinArch.arm64, DarwinArch.x86_64], projectUri: projectUri, buildMode: BuildMode.debug, fileSystem: fileSystem, flutterTester: flutterTester, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - onBuild: (native_assets_cli.Target target) => FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: target.os, - architecture: target.architecture, - file: Uri.file('${target.architecture}/libbar.dylib'), - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -339,6 +344,9 @@ void main() { '- bar.framework/bar', ]), ); + // Multi arch. + expect(buildRunner.buildInvocations, 2); + expect(buildRunner.linkInvocations, 2); }); } @@ -357,7 +365,7 @@ void main() { packagesWithNativeAssetsResult: [ Package('bar', projectUri), ], - dryRunResult: FakeNativeAssetsBuilderResult( + buildDryRunResult: FakeNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', @@ -395,24 +403,29 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => dryRunNativeAssetsMacOS( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => dryRunNativeAssetsMacOS( + projectUri: projectUri, + fileSystem: fileSystem, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook (dry run) native assets failed. See the logs for more details.', + ), + ); + } }); testUsingContext('Native assets build error', overrides: { @@ -423,27 +436,32 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => buildNativeAssetsMacOS( - darwinArchs: [DarwinArch.arm64], - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => buildNativeAssetsMacOS( + darwinArchs: [DarwinArch.arm64], + projectUri: projectUri, + buildMode: BuildMode.debug, + fileSystem: fileSystem, + yamlParentDirectory: environment.buildDir.uri, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook native assets failed. See the logs for more details.', + ), + ); + } }); // This logic is mocked in the other tests to avoid having test order diff --git a/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart index b04729bd9d2..8c261280733 100644 --- a/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart @@ -81,7 +81,9 @@ void main() { expect(result, 0); expect(buildRunner.buildInvocations, 0); - expect(buildRunner.dryRunInvocations, 0); + expect(buildRunner.buildDryRunInvocations, 0); + expect(buildRunner.linkInvocations, 0); + expect(buildRunner.linkDryRunInvocations, 0); expect(buildRunner.hasPackageConfigInvocations, 0); expect(buildRunner.packagesWithNativeAssetsInvocations, 0); diff --git a/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart index fd27541d9f5..46b4c175178 100644 --- a/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart +++ b/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart @@ -135,25 +135,26 @@ void main() { final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.windows, + architecture: ArchitectureImpl.x64, + file: Uri.file('bar.dll'), + ), + ], + ), + ); final Uri? nativeAssetsYaml = await dryRunNativeAssetsWindows( projectUri: projectUri, fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.windows, - architecture: ArchitectureImpl.x64, - file: Uri.file('bar.dll'), - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -170,6 +171,8 @@ void main() { await fileSystem.file(nativeAssetsYaml).readAsString(), contains('package:bar/bar.dart'), ); + expect(buildRunner.buildDryRunInvocations, 1); + expect(buildRunner.linkDryRunInvocations, 1); }); testUsingContext('build with assets but not enabled', overrides: { @@ -243,28 +246,29 @@ void main() { final File dylibAfterCompiling = fileSystem.file('bar.dll'); // The mock doesn't create the file, so create it here. await dylibAfterCompiling.create(); + final FakeNativeAssetsBuildRunner buildRunner = FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildResult: FakeNativeAssetsBuilderResult( + assets: [ + NativeCodeAssetImpl( + id: 'package:bar/bar.dart', + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.windows, + architecture: ArchitectureImpl.x64, + file: dylibAfterCompiling.uri, + ), + ], + ), + ); final (Uri? nativeAssetsYaml, _) = await buildNativeAssetsWindows( targetPlatform: TargetPlatform.windows_x64, projectUri: projectUri, buildMode: BuildMode.debug, fileSystem: fileSystem, flutterTester: flutterTester, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: FakeNativeAssetsBuilderResult( - assets: [ - NativeCodeAssetImpl( - id: 'package:bar/bar.dart', - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.windows, - architecture: ArchitectureImpl.x64, - file: dylibAfterCompiling.uri, - ), - ], - ), - ), + buildRunner: buildRunner, ); expect( (globals.logger as BufferLogger).traceText, @@ -289,6 +293,8 @@ void main() { '- bar.dll', ]), ); + expect(buildRunner.buildInvocations, 1); + expect(buildRunner.linkInvocations, 1); }); } @@ -307,7 +313,7 @@ void main() { packagesWithNativeAssetsResult: [ Package('bar', projectUri), ], - dryRunResult: FakeNativeAssetsBuilderResult( + buildDryRunResult: FakeNativeAssetsBuilderResult( assets: [ NativeCodeAssetImpl( id: 'package:bar/bar.dart', @@ -338,24 +344,29 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => dryRunNativeAssetsWindows( - projectUri: projectUri, - fileSystem: fileSystem, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - dryRunResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => dryRunNativeAssetsWindows( + projectUri: projectUri, + fileSystem: fileSystem, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkDryRunResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook (dry run) native assets failed. See the logs for more details.', + ), + ); + } }); testUsingContext('Native assets build error', overrides: { @@ -366,27 +377,32 @@ void main() { environment.projectDir.childFile('.dart_tool/package_config.json'); await packageConfig.parent.create(); await packageConfig.create(); - expect( - () => buildNativeAssetsWindows( - targetPlatform: TargetPlatform.windows_x64, - projectUri: projectUri, - buildMode: BuildMode.debug, - fileSystem: fileSystem, - yamlParentDirectory: environment.buildDir.uri, - buildRunner: FakeNativeAssetsBuildRunner( - packagesWithNativeAssetsResult: [ - Package('bar', projectUri), - ], - buildResult: const FakeNativeAssetsBuilderResult( - success: false, + for (final String hook in ['Building', 'Linking']) { + expect( + () => buildNativeAssetsWindows( + targetPlatform: TargetPlatform.windows_x64, + projectUri: projectUri, + buildMode: BuildMode.debug, + fileSystem: fileSystem, + yamlParentDirectory: environment.buildDir.uri, + buildRunner: FakeNativeAssetsBuildRunner( + packagesWithNativeAssetsResult: [ + Package('bar', projectUri), + ], + buildResult: FakeNativeAssetsBuilderResult( + success: hook != 'Building', + ), + linkResult: FakeNativeAssetsBuilderResult( + success: hook != 'Linking', + ), ), ), - ), - throwsToolExit( - message: - 'Building native assets failed. See the logs for more details.', - ), - ); + throwsToolExit( + message: + '$hook native assets failed. See the logs for more details.', + ), + ); + } }); // This logic is mocked in the other tests to avoid having test order diff --git a/packages/flutter_tools/test/integration.shard/isolated/native_assets_test.dart b/packages/flutter_tools/test/integration.shard/isolated/native_assets_test.dart index 9168aead50c..ae6456cb55b 100644 --- a/packages/flutter_tools/test/integration.shard/isolated/native_assets_test.dart +++ b/packages/flutter_tools/test/integration.shard/isolated/native_assets_test.dart @@ -550,6 +550,8 @@ Future createTestProject(String packageName, Directory tempDirectory) await pinDependencies( packageDirectory.childDirectory('example').childFile('pubspec.yaml')); + await addLinkHookDepedendency(packageDirectory); + final ProcessResult result2 = await processManager.run( [ flutterBin, @@ -563,6 +565,52 @@ Future createTestProject(String packageName, Directory tempDirectory) return packageDirectory; } +Future addLinkHookDepedendency(Directory packageDirectory) async { + final Directory flutterDirectory = fileSystem.currentDirectory.parent.parent; + final Directory linkHookDirectory = flutterDirectory + .childDirectory('dev') + .childDirectory('integration_tests') + .childDirectory('link_hook'); + expect(linkHookDirectory, exists); + + final File pubspecFile = packageDirectory.childFile('pubspec.yaml'); + final String pubspecOld = + (await pubspecFile.readAsString()).replaceAll('\r\n', '\n'); + final String pubspecNew = pubspecOld.replaceFirst(''' +dependencies: +''', ''' +dependencies: + link_hook: + path: ${linkHookDirectory.path} +'''); + expect(pubspecNew, isNot(pubspecOld)); + await pubspecFile.writeAsString(pubspecNew); + + final File dartFile = + packageDirectory.childDirectory('lib').childFile('$packageName.dart'); + final String dartFileOld = + (await dartFile.readAsString()).replaceAll('\r\n', '\n'); + // Replace with something that results in the same resulting int, so that the + // tests don't have to be updated. + final String dartFileNew = dartFileOld.replaceFirst( + ''' +import '${packageName}_bindings_generated.dart' as bindings; +''', + ''' +import 'package:link_hook/link_hook.dart' as l; + +import '${packageName}_bindings_generated.dart' as bindings; +''', + ); + expect(dartFileNew, isNot(dartFileOld)); + final String dartFileNew2 = dartFileNew.replaceFirst( + 'int sum(int a, int b) => bindings.sum(a, b);', + 'int sum(int a, int b) => bindings.sum(a, b) + l.difference(2, 1) - 1;', + ); + expect(dartFileNew2, isNot(dartFileNew)); + await dartFile.writeAsString(dartFileNew2); +} + Future pinDependencies(File pubspecFile) async { expect(pubspecFile, exists); final String oldPubspec = await pubspecFile.readAsString();