diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 11f94f307ad..ab4087ec4e9 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -328,17 +328,8 @@ Future _runBuildTests() async { Future _flutterBuildAot(String relativePathToApplication) async { print('${green}Testing AOT build$reset for $cyan$relativePathToApplication$reset...'); - final String absoluteTarget = path.join(path.absolute(relativePathToApplication), 'lib', 'main.dart'); await runCommand(flutter, - [ - 'assemble', - '-dTargetFile=$absoluteTarget', - '-dTargetPlatform=android', - '-dBuildMode=release', - '--output=build/aot', - 'android_aot_bundle_release_android-arm64', - 'android_aot_bundle_release_android-arm', - ], + ['build', 'aot', '-v'], workingDirectory: path.join(flutterRoot, relativePathToApplication), ); } diff --git a/packages/flutter_tools/lib/src/aot.dart b/packages/flutter_tools/lib/src/aot.dart index bad73843814..750d20d25e4 100644 --- a/packages/flutter_tools/lib/src/aot.dart +++ b/packages/flutter_tools/lib/src/aot.dart @@ -6,13 +6,17 @@ import 'dart:async'; import 'package:meta/meta.dart'; +import 'base/build.dart'; import 'base/common.dart'; +import 'base/io.dart'; import 'base/logger.dart'; +import 'base/process.dart'; import 'build_info.dart'; import 'build_system/build_system.dart'; import 'build_system/targets/dart.dart'; import 'build_system/targets/ios.dart'; import 'cache.dart'; +import 'dart/package_map.dart'; import 'globals.dart' as globals; import 'ios/bitcode.dart'; import 'project.dart'; @@ -51,7 +55,123 @@ class AotBuilder { ); return; } - throwToolExit('"flutter build aot" does not support configuration: $platform/$buildMode.'); + + if (bitcode) { + if (platform != TargetPlatform.ios) { + throwToolExit('Bitcode is only supported on iOS (TargetPlatform is $platform).'); + } + await validateBitcode(buildMode, platform); + } + + Status status; + if (!quiet) { + final String typeName = globals.artifacts.getEngineType(platform, buildMode); + status = globals.logger.startProgress( + 'Building AOT snapshot in ${getFriendlyModeName(buildMode)} mode ($typeName)...', + timeout: timeoutConfiguration.slowOperation, + ); + } + try { + final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: reportTimings); + + // Compile to kernel. + final String kernelOut = await snapshotter.compileKernel( + platform: platform, + buildMode: buildMode, + mainPath: mainDartFile, + packagesPath: PackageMap.globalPackagesPath, + trackWidgetCreation: false, + outputPath: outputPath, + extraFrontEndOptions: extraFrontEndOptions, + dartDefines: dartDefines, + ); + if (kernelOut == null) { + throwToolExit('Compiler terminated unexpectedly.'); + return; + } + + // Build AOT snapshot. + if (platform == TargetPlatform.ios) { + // Determine which iOS architectures to build for. + final Map iosBuilds = {}; + for (final DarwinArch arch in iosBuildArchs) { + iosBuilds[arch] = globals.fs.path.join(outputPath, getNameForDarwinArch(arch)); + } + + // Generate AOT snapshot and compile to arch-specific App.framework. + final Map> exitCodes = >{}; + iosBuilds.forEach((DarwinArch iosArch, String outputPath) { + exitCodes[iosArch] = snapshotter.build( + platform: platform, + darwinArch: iosArch, + buildMode: buildMode, + mainPath: kernelOut, + packagesPath: PackageMap.globalPackagesPath, + outputPath: outputPath, + extraGenSnapshotOptions: extraGenSnapshotOptions, + bitcode: bitcode, + quiet: quiet, + ).then((int buildExitCode) { + return buildExitCode; + }); + }); + + // Merge arch-specific App.frameworks into a multi-arch App.framework. + if ((await Future.wait(exitCodes.values)).every((int buildExitCode) => buildExitCode == 0)) { + final Iterable dylibs = iosBuilds.values.map( + (String outputDir) => globals.fs.path.join(outputDir, 'App.framework', 'App')); + globals.fs.directory(globals.fs.path.join(outputPath, 'App.framework'))..createSync(); + await processUtils.run( + [ + 'lipo', + ...dylibs, + '-create', + '-output', globals.fs.path.join(outputPath, 'App.framework', 'App'), + ], + throwOnError: true, + ); + } else { + status?.cancel(); + exitCodes.forEach((DarwinArch iosArch, Future exitCodeFuture) async { + final int buildExitCode = await exitCodeFuture; + globals.printError('Snapshotting ($iosArch) exited with non-zero exit code: $buildExitCode'); + }); + } + } else { + // Android AOT snapshot. + final int snapshotExitCode = await snapshotter.build( + platform: platform, + buildMode: buildMode, + mainPath: kernelOut, + packagesPath: PackageMap.globalPackagesPath, + outputPath: outputPath, + extraGenSnapshotOptions: extraGenSnapshotOptions, + bitcode: false, + ); + if (snapshotExitCode != 0) { + status?.cancel(); + throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode'); + } + } + } on ProcessException catch (error) { + // Catch the String exceptions thrown from the `runSync` methods below. + status?.cancel(); + globals.printError(error.toString()); + return; + } + status?.stop(); + + if (outputPath == null) { + throwToolExit(null); + } + + final String builtMessage = 'Built to $outputPath${globals.fs.path.separator}.'; + if (quiet) { + globals.printTrace(builtMessage); + } else { + globals.printStatus(builtMessage); + } + return; } bool _canUseAssemble(TargetPlatform targetPlatform) {