From e410b7cd5e01554f899d43c5b2a8ee5baea7e760 Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Thu, 25 Apr 2024 13:22:17 +0200 Subject: [PATCH] [native_assets] Use kernel concatenation (#147158) Uses kernel concatenation to concatenate the `program.dill` and `native_assets.dill`. This will enable compiling `native_assets.dill` after `program.dill` so that tree-shaking information can be used. Issue: * https://github.com/flutter/flutter/issues/146270 ## Implementation choices * We can either run the frontend_server twice as a process invocation (current PR), or keep the process running after the dart compilation and then shut it down after native assets compilation to kernel. Keeping the process running would require more coordination between different `Target`s. * We can either chose to only do separate kernel file compilation in release (AOT) builds, or do it both for JIT and AOT (current PR). Doing it differently in JIT/AOT means more conditionals in lib/src/build_system/targets/, doing it single-shot in JIT might be more performant though. Update: Both of these are mitigated by not running the kernel compiler process if there are no native assets at all. ## Implementation notes This only updates `flutter assemble`. The kernel compilation calls in `flutter run` do not require kernel concatenation. The native-assets-kernel-mapping in `flutter run` contains results from `--dry-run` invocations of `hook/build.dart` (and in the future `hook/link.dart`). These `--dry-run` invocations do not get access to tree-shaking information, so the `link` hook to be introduced later can be run before Dart compilation. ## Tests * Updated the `Target`s tests to reflect the new implementation. ## Related CLs * frontend_server support: https://dart-review.googlesource.com/c/sdk/+/363567 * Same PR for Dart standalone: https://dart-review.googlesource.com/c/sdk/+/362384 --- .../lib/src/build_system/targets/common.dart | 172 +++++++++++++++-- .../build_system/targets/native_assets.dart | 6 +- packages/flutter_tools/lib/src/compile.dart | 15 +- .../build_system/targets/common_test.dart | 175 ++++++++++-------- 4 files changed, 269 insertions(+), 99 deletions(-) diff --git a/packages/flutter_tools/lib/src/build_system/targets/common.dart b/packages/flutter_tools/lib/src/build_system/targets/common.dart index ba03270c526..258eaa86185 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/common.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/common.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:package_config/package_config.dart'; +import 'package:yaml/yaml.dart'; import '../../artifacts.dart'; import '../../base/build.dart'; @@ -121,15 +122,17 @@ class ReleaseCopyFlutterBundle extends CopyFlutterBundle { /// even though it is not listed as an input. Pub inserts a timestamp into /// the file which causes unnecessary rebuilds, so instead a subset of the contents /// are used an input instead. -class KernelSnapshot extends Target { - const KernelSnapshot(); +/// +/// This kernel snapshot is concatenated with the [KernelSnapshotNativeAssets] +/// inside [KernelSnapshot] byte-wise to create the combined kernel snapshot. +class KernelSnapshotProgram extends Target { + const KernelSnapshotProgram(); @override - String get name => 'kernel_snapshot'; + String get name => 'kernel_snapshot_program'; @override List get inputs => const [ - Source.pattern('{BUILD_DIR}/native_assets.yaml'), Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'), Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/common.dart'), Source.artifact(Artifact.platformKernelDill), @@ -139,7 +142,9 @@ class KernelSnapshot extends Target { ]; @override - List get outputs => const []; + List get outputs => const [ + // TODO(mosuem): Should output resources.json. https://github.com/flutter/flutter/issues/146263 + ]; @override List get depfiles => [ @@ -148,11 +153,12 @@ class KernelSnapshot extends Target { @override List get dependencies => const [ - NativeAssets(), GenerateLocalizationsTarget(), DartPluginRegistrantTarget(), ]; + static const String dillName = 'program.dill'; + @override Future build(Environment environment) async { final KernelCompiler compiler = KernelCompiler( @@ -186,13 +192,6 @@ class KernelSnapshot extends Target { final List? fileSystemRoots = environment.defines[kFileSystemRoots]?.split(','); final String? fileSystemScheme = environment.defines[kFileSystemScheme]; - final File nativeAssetsFile = environment.buildDir.childFile('native_assets.yaml'); - final String nativeAssets = nativeAssetsFile.path; - if (!await nativeAssetsFile.exists()) { - throwToolExit("$nativeAssets doesn't exist."); - } - environment.logger.printTrace('Embedding native assets mapping $nativeAssets in kernel.'); - TargetModel targetModel = TargetModel.flutter; if (targetPlatform == TargetPlatform.fuchsia_x64 || targetPlatform == TargetPlatform.fuchsia_arm64) { @@ -242,6 +241,8 @@ class KernelSnapshot extends Target { logger: environment.logger, ); + final String dillPath = environment.buildDir.childFile(dillName).path; + final CompilerOutput? output = await compiler.compile( sdkRoot: environment.artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -252,9 +253,8 @@ class KernelSnapshot extends Target { buildMode: buildMode, trackWidgetCreation: trackWidgetCreation && buildMode != BuildMode.release, targetModel: targetModel, - outputFilePath: environment.buildDir.childFile('app.dill').path, - initializeFromDill: buildMode.isPrecompiled ? null : - environment.buildDir.childFile('app.dill').path, + outputFilePath: dillPath, + initializeFromDill: buildMode.isPrecompiled ? null : dillPath, packagesPath: packagesFile.path, linkPlatformKernelIn: forceLinkPlatform || buildMode.isPrecompiled, mainPath: targetFileAbsolute, @@ -268,6 +268,109 @@ class KernelSnapshot extends Target { buildDir: environment.buildDir, targetOS: targetOS, checkDartPluginRegistry: environment.generateDartPluginRegistry, + ); + if (output == null || output.errorCount != 0) { + throw Exception(); + } + } +} + +/// Generate a kernel snapshot of the native assets mapping for resolving +/// `@Native` assets at runtime. +/// +/// This kernel snapshot is concatenated to the [KernelSnapshotProgram] +/// inside [KernelSnapshot] to create the combined kernel snapshot. +class KernelSnapshotNativeAssets extends Target { + const KernelSnapshotNativeAssets(); + + @override + String get name => 'kernel_snapshot_native_assets'; + + @override + List get inputs => [ + const Source.pattern('{BUILD_DIR}/native_assets.yaml'), + ...const KernelSnapshotProgram().inputs, + ]; + + @override + List get outputs => const []; + + @override + List get depfiles => const []; + + @override + List get dependencies => [ + const NativeAssets(), + ]; + + static const String dillName = 'native_assets.dill'; + + @override + Future build(Environment environment) async { + final File nativeAssetsFile = environment.buildDir.childFile('native_assets.yaml'); + final File dillFile = environment.buildDir.childFile(dillName); + + final YamlNode nativeAssetContents = loadYamlNode(await nativeAssetsFile.readAsString()); + final Object? nativeAssetsInYaml = (nativeAssetContents as Map)['native-assets']; + if (nativeAssetsInYaml is! Map || nativeAssetsInYaml.isEmpty) { + // Write an empty file to make concatenation a no-op. + // Write the file out to disk for caching. + await dillFile.writeAsBytes([]); + return; + } + + final KernelCompiler compiler = KernelCompiler( + fileSystem: environment.fileSystem, + logger: environment.logger, + processManager: environment.processManager, + artifacts: environment.artifacts, + fileSystemRoots: [], + ); + final String? buildModeEnvironment = environment.defines[kBuildMode]; + if (buildModeEnvironment == null) { + throw MissingDefineException(kBuildMode, 'kernel_snapshot'); + } + final String? targetPlatformEnvironment = environment.defines[kTargetPlatform]; + if (targetPlatformEnvironment == null) { + throw MissingDefineException(kTargetPlatform, 'kernel_snapshot'); + } + final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment); + final File packagesFile = environment.projectDir + .childDirectory('.dart_tool') + .childFile('package_config.json'); + + final TargetPlatform targetPlatform = getTargetPlatformForName(targetPlatformEnvironment); + + final String? frontendServerStarterPath = environment.defines[kFrontendServerStarterPath]; + + final String nativeAssets = nativeAssetsFile.path; + if (!await nativeAssetsFile.exists()) { + throwToolExit("$nativeAssets doesn't exist."); + } + environment.logger.printTrace('Embedding native assets mapping $nativeAssets in kernel.'); + + final PackageConfig packageConfig = await loadPackageConfigWithLogging( + packagesFile, + logger: environment.logger, + ); + + final String dillPath = dillFile.path; + + final CompilerOutput? output = await compiler.compile( + sdkRoot: environment.artifacts.getArtifactPath( + Artifact.flutterPatchedSdkPath, + platform: targetPlatform, + mode: buildMode, + ), + aot: buildMode.isPrecompiled, + buildMode: buildMode, + trackWidgetCreation: false, + outputFilePath: dillPath, + packagesPath: packagesFile.path, + frontendServerStarterPath: frontendServerStarterPath, + packageConfig: packageConfig, + buildDir: environment.buildDir, + dartDefines: [], nativeAssets: nativeAssets, ); if (output == null || output.errorCount != 0) { @@ -276,6 +379,43 @@ class KernelSnapshot extends Target { } } +class KernelSnapshot extends Target { + const KernelSnapshot(); + + @override + String get name => 'kernel_snapshot'; + + @override + List get dependencies => const [ + KernelSnapshotProgram(), + KernelSnapshotNativeAssets(), + ]; + + @override + List get inputs => []; + + @override + List get outputs => []; + + static const String dillName = 'app.dill'; + + @override + Future build(Environment environment) async { + final File programDill = environment.buildDir.childFile( + KernelSnapshotProgram.dillName, + ); + final File nativeAssetsDill = environment.buildDir.childFile( + KernelSnapshotNativeAssets.dillName, + ); + final File dill = environment.buildDir.childFile(dillName); + await programDill.copy(dill.path); + await dill.writeAsBytes( + await nativeAssetsDill.readAsBytes(), + mode: FileMode.append, + ); + } +} + /// Supports compiling a dart kernel file to an ELF binary. abstract class AotElfBase extends Target { const AotElfBase(); diff --git a/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart b/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart index e2ab35de109..4775a262d69 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart @@ -371,13 +371,17 @@ class NativeAssets extends Target { ]; @override - List get dependencies => []; + List get dependencies => const [ + // In AOT, depends on tree-shaking information (resources.json) from compiling dart. + KernelSnapshotProgram(), + ]; @override List get inputs => const [ Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart'), // If different packages are resolved, different native assets might need to be built. Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'), + // TODO(mosuem): Should consume resources.json. https://github.com/flutter/flutter/issues/146263 ]; @override diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index 2fe3b8ba3e9..f1ce205dbfc 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -247,12 +247,14 @@ class KernelCompiler { sdkRoot = '$sdkRoot/'; } String? mainUri; - final File mainFile = _fileSystem.file(mainPath); - final Uri mainFileUri = mainFile.uri; - if (packagesPath != null) { - mainUri = packageConfig.toPackageUri(mainFileUri)?.toString(); + if (mainPath != null) { + final File mainFile = _fileSystem.file(mainPath); + final Uri mainFileUri = mainFile.uri; + if (packagesPath != null) { + mainUri = packageConfig.toPackageUri(mainFileUri)?.toString(); + } + mainUri ??= toMultiRootPath(mainFileUri, _fileSystemScheme, _fileSystemRoots, _fileSystem.path.separator == r'\'); } - mainUri ??= toMultiRootPath(mainFileUri, _fileSystemScheme, _fileSystemRoots, _fileSystem.path.separator == r'\'); if (outputFilePath != null && !_fileSystem.isFileSync(outputFilePath)) { _fileSystem.file(outputFilePath).createSync(recursive: true); } @@ -359,7 +361,8 @@ class KernelCompiler { // See: https://github.com/flutter/flutter/issues/103994 '--verbosity=error', ...?extraFrontEndOptions, - mainUri, + if (mainUri != null) mainUri + else '--native-assets-only', ]; _logger.printTrace(command.join(' ')); diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart index 0eab969458e..79a778e0675 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart @@ -64,10 +64,10 @@ void main() { iosEnvironment.buildDir.createSync(recursive: true); }); - testWithoutContext('KernelSnapshot throws error if missing build mode', () async { + testWithoutContext('KernelSnapshotProgram throws error if missing build mode', () async { androidEnvironment.defines.remove(kBuildMode); expect( - const KernelSnapshot().build(androidEnvironment), + const KernelSnapshotProgram().build(androidEnvironment), throwsA(isA())); }); @@ -79,13 +79,22 @@ format-version: native-assets: {} '''; - testWithoutContext('KernelSnapshot handles null result from kernel compilation', () async { + const String nonEmptyNativeAssets = ''' +format-version: + - 1 + - 0 + - 0 +native-assets: + macos_arm64: + package:my_package/my_package_bindings_generated.dart: + - absolute + - my_package.framework/my_package +'''; + + testWithoutContext('KernelSnapshotProgram handles null result from kernel compilation', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -110,27 +119,22 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'file:///lib/main.dart', ], exitCode: 1), ]); - await expectLater(() => const KernelSnapshot().build(androidEnvironment), throwsException); + await expectLater(() => const KernelSnapshotProgram().build(androidEnvironment), throwsException); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot does use track widget creation on profile builds', () async { + testWithoutContext('KernelSnapshotProgram does use track widget creation on profile builds', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -155,28 +159,23 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot().build(androidEnvironment); + await const KernelSnapshotProgram().build(androidEnvironment); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot correctly handles an empty string in ExtraFrontEndOptions', () async { + testWithoutContext('KernelSnapshotProgram correctly handles an empty string in ExtraFrontEndOptions', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -201,29 +200,24 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot() + await const KernelSnapshotProgram() .build(androidEnvironment..defines[kExtraFrontEndOptions] = ''); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot correctly forwards FrontendServerStarterPath', () async { + testWithoutContext('KernelSnapshotProgram correctly forwards FrontendServerStarterPath', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -248,29 +242,24 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot() + await const KernelSnapshotProgram() .build(androidEnvironment..defines[kFrontendServerStarterPath] = 'path/to/frontend_server_starter.dart'); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot correctly forwards ExtraFrontEndOptions', () async { + testWithoutContext('KernelSnapshotProgram correctly forwards ExtraFrontEndOptions', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -295,31 +284,27 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', - '--native-assets', - '$build/native_assets.yaml', '--verbosity=error', 'foo', 'bar', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot() + await const KernelSnapshotProgram() .build(androidEnvironment..defines[kExtraFrontEndOptions] = 'foo,bar'); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot can disable track-widget-creation on debug builds', () async { + testWithoutContext('KernelSnapshotProgram can disable track-widget-creation on debug builds', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); + final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -340,33 +325,28 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', '--incremental', '--initialize-from-dill', - '$build/app.dill', - '--native-assets', - '$build/native_assets.yaml', + '$build/program.dill', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot().build(androidEnvironment + await const KernelSnapshotProgram().build(androidEnvironment ..defines[kBuildMode] = BuildMode.debug.cliName ..defines[kTrackWidgetCreation] = 'false'); expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot forces platform linking on debug for darwin target platforms', () async { + testWithoutContext('KernelSnapshotProgram forces platform linking on debug for darwin target platforms', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - androidEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = androidEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -386,20 +366,18 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', '--incremental', '--initialize-from-dill', - '$build/app.dill', - '--native-assets', - '$build/native_assets.yaml', + '$build/program.dill', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/program.dill 0\n'), ]); - await const KernelSnapshot().build(androidEnvironment + await const KernelSnapshotProgram().build(androidEnvironment ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin) ..defines[kBuildMode] = BuildMode.debug.cliName ..defines[kTrackWidgetCreation] = 'false' @@ -408,7 +386,7 @@ native-assets: {} expect(processManager, hasNoRemainingExpectations); }); - testWithoutContext('KernelSnapshot does use track widget creation on debug builds', () async { + testWithoutContext('KernelSnapshotProgram does use track widget creation on debug builds', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); @@ -423,9 +401,6 @@ native-assets: {} fileSystem: fileSystem, logger: logger, ); - testEnvironment.buildDir.childFile('native_assets.yaml') - ..createSync(recursive: true) - ..writeAsStringSync(emptyNativeAssets); final String build = testEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -447,24 +422,72 @@ native-assets: {} '--packages', '/.dart_tool/package_config.json', '--output-dill', - '$build/app.dill', + '$build/program.dill', '--depfile', '$build/kernel_snapshot.d', '--incremental', '--initialize-from-dill', - '$build/app.dill', - '--native-assets', - '$build/native_assets.yaml', + '$build/program.dill', '--verbosity=error', 'file:///lib/main.dart', - ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey /build/653e11a8e6908714056a57cd6b4f602a/app.dill 0\n'), + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey /build/653e11a8e6908714056a57cd6b4f602a/program.dill 0\n'), ]); - await const KernelSnapshot().build(testEnvironment); + await const KernelSnapshotProgram().build(testEnvironment); expect(processManager, hasNoRemainingExpectations); }); + for (final BuildMode buildMode in [BuildMode.debug, BuildMode.release]) { + for (final bool empty in [true, false]) { + final String testName = empty ? 'empty' : 'non empty'; + testWithoutContext('KernelSnapshotNativeAssets ${buildMode.name} $testName', () async { + fileSystem.file('.dart_tool/package_config.json') + ..createSync(recursive: true) + ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); + androidEnvironment.buildDir.childFile('native_assets.yaml') + ..createSync(recursive: true) + ..writeAsStringSync(empty ? emptyNativeAssets : nonEmptyNativeAssets); + final String build = androidEnvironment.buildDir.path; + final String flutterPatchedSdkPath = artifacts.getArtifactPath( + Artifact.flutterPatchedSdkPath, + platform: TargetPlatform.darwin, + mode: buildMode, + ); + processManager.addCommands([ + if (!empty) + FakeCommand(command: [ + artifacts.getArtifactPath(Artifact.engineDartAotRuntime), + '--disable-dart-dev', + artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk), + '--sdk-root', + '$flutterPatchedSdkPath/', + '--target=flutter', + '--no-print-incremental-dependencies', + ...buildModeOptions(buildMode, []), + '--no-link-platform', + if (buildMode == BuildMode.release) ...['--aot', '--tfa'], + '--packages', + '/.dart_tool/package_config.json', + '--output-dill', + '$build/native_assets.dill', + '--native-assets', + '$build/native_assets.yaml', + '--verbosity=error', + '--native-assets-only', + ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'), + ]); + + await const KernelSnapshotNativeAssets().build(androidEnvironment + ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin) + ..defines[kBuildMode] = buildMode.cliName + ); + + expect(processManager, hasNoRemainingExpectations); + }); + } + } + testUsingContext('AotElfProfile Produces correct output directory', () async { final String build = androidEnvironment.buildDir.path; processManager.addCommands([