From cdbb2385f1a584d2056c386ffd7bd07b7af8cb87 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Sun, 29 Apr 2018 10:47:34 -0700 Subject: [PATCH] Extract kernel compile from buildAotSnapshot (#17062) Moves the kernel compile step to the beginning of the AOT build in a separate method. This is pre-factoring for iOS universal builds where the kernel build happens once, but we then snapshot twice: once for armv7 and once for arm64. This also writes dependencies to build/kernel_compile.d rather than build/aot/snapshot.d, since that is immediately overwritten by gen_snapshot. --- .../flutter_tools/lib/src/base/build.dart | 94 +++++++++++-------- .../lib/src/commands/build_aot.dart | 31 +++++- .../flutter_tools/test/base/build_test.dart | 42 ++------- 3 files changed, 86 insertions(+), 81 deletions(-) diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index 7174509a097..49b7bb07e0f 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart @@ -207,6 +207,52 @@ class Snapshotter { return exitCode; } + /// Compiles a Dart file to kernel. + /// + /// Returns the output kernel file path, or null on failure. + Future compileKernel({ + @required TargetPlatform platform, + @required BuildMode buildMode, + @required String mainPath, + @required String outputPath, + List extraFrontEndOptions: const [], + }) async { + final Directory outputDir = fs.directory(outputPath); + outputDir.createSync(recursive: true); + + printTrace('Compiling Dart to kernel: $mainPath'); + final bool aot = !_isInterpreted(platform, buildMode); + final List entryPointsJsonFiles = []; + if (aot) { + entryPointsJsonFiles.addAll([ + artifacts.getArtifactPath(Artifact.entryPointsJson, platform, buildMode), + artifacts.getArtifactPath(Artifact.entryPointsExtraJson, platform, buildMode), + ]); + } + + if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty) + printTrace('Extra front-end options: $extraFrontEndOptions'); + + final String depfilePath = fs.path.join(outputPath, 'kernel_compile.d'); + final CompilerOutput compilerOutput = await kernelCompiler.compile( + sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), + mainPath: mainPath, + outputFilePath: fs.path.join(outputPath, 'app.dill'), + depFilePath: depfilePath, + extraFrontEndOptions: extraFrontEndOptions, + linkPlatformKernelIn: true, + aot: aot, + entryPointsJsonFiles: entryPointsJsonFiles, + trackWidgetCreation: false, + ); + + // Write path to frontend_server, since things need to be re-generated when that changes. + final String frontendPath = artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk); + await fs.directory(outputPath).childFile('frontend_server.d').writeAsString('frontend_server.d: $frontendPath\n'); + + return compilerOutput?.outputFilename; + } + /// Builds an architecture-specific ahead-of-time compiled snapshot of the specified script. Future buildAotSnapshot({ @required TargetPlatform platform, @@ -216,7 +262,6 @@ class Snapshotter { @required String outputPath, @required bool previewDart2, @required bool preferSharedLibrary, - List extraFrontEndOptions: const [], List extraGenSnapshotOptions: const [], }) async { if (!(platform == TargetPlatform.android_arm || @@ -228,6 +273,7 @@ class Snapshotter { final Directory outputDir = fs.directory(outputPath); outputDir.createSync(recursive: true); + final String vmSnapshotData = fs.path.join(outputDir.path, 'vm_snapshot_data'); final String vmSnapshotInstructions = fs.path.join(outputDir.path, 'vm_snapshot_instr'); final String isolateSnapshotData = fs.path.join(outputDir.path, 'isolate_snapshot_data'); @@ -250,15 +296,6 @@ class Snapshotter { final String ioEntryPoints = artifacts.getArtifactPath(Artifact.dartIoEntriesTxt, platform, buildMode); assert(ioEntryPoints != null); - final bool interpreter = platform == TargetPlatform.ios && buildMode == BuildMode.debug; - final List entryPointsJsonFiles = []; - if (previewDart2 && !interpreter) { - entryPointsJsonFiles.addAll([ - artifacts.getArtifactPath(Artifact.entryPointsJson, platform, buildMode), - artifacts.getArtifactPath(Artifact.entryPointsExtraJson, platform, buildMode), - ]); - } - final PackageMap packageMap = new PackageMap(packagesPath); final String packageMapError = packageMap.checkValid(); if (packageMapError != null) { @@ -278,8 +315,6 @@ class Snapshotter { mainPath, ]; - inputPaths.addAll(entryPointsJsonFiles); - final Set outputPaths = new Set(); // These paths are used only on iOS. @@ -325,14 +360,12 @@ class Snapshotter { '--dependencies=$depfilePath', ]; - if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty) - printTrace('Extra front-end options: $extraFrontEndOptions'); - if ((extraGenSnapshotOptions != null) && extraGenSnapshotOptions.isNotEmpty) { printTrace('Extra gen-snapshot options: $extraGenSnapshotOptions'); genSnapshotArgs.addAll(extraGenSnapshotOptions); } + final bool interpreter = _isInterpreted(platform, buildMode); if (!interpreter) { genSnapshotArgs.add('--embedder_entry_points_manifest=$vmEntryPoints'); genSnapshotArgs.add('--embedder_entry_points_manifest=$ioEntryPoints'); @@ -347,7 +380,6 @@ class Snapshotter { final String kIsolateSnapshotDataC = fs.path.join(outputDir.path, '$kIsolateSnapshotData.c'); final String kVmSnapshotDataO = fs.path.join(outputDir.path, '$kVmSnapshotData.o'); final String kIsolateSnapshotDataO = fs.path.join(outputDir.path, '$kIsolateSnapshotData.o'); - final String kApplicationKernelPath = fs.path.join(getBuildDirectory(), 'app.dill'); switch (platform) { case TargetPlatform.android_arm: @@ -408,36 +440,13 @@ class Snapshotter { return 0; } - String entrypointPath = mainPath; if (previewDart2) { - final CompilerOutput compilerOutput = await kernelCompiler.compile( - sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), - mainPath: mainPath, - outputFilePath: kApplicationKernelPath, - depFilePath: depfilePath, - extraFrontEndOptions: extraFrontEndOptions, - linkPlatformKernelIn: true, - aot: !interpreter, - entryPointsJsonFiles: entryPointsJsonFiles, - trackWidgetCreation: false, - ); - entrypointPath = compilerOutput?.outputFilename; - if (entrypointPath == null) { - printError('Compiler terminated unexpectedly.'); - return -5; - } - // Write path to frontend_server, since things need to be re-generated when - // that changes. - await outputDir.childFile('frontend_server.d') - .writeAsString('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n'); - genSnapshotArgs.addAll([ '--reify-generic-functions', '--strong', ]); } - - genSnapshotArgs.add(entrypointPath); + genSnapshotArgs.add(mainPath); final int genSnapshotExitCode = await genSnapshot.run( snapshotType: new SnapshotType(platform, buildMode), @@ -517,6 +526,11 @@ class Snapshotter { return 0; } + /// Returns true if the specified platform and build mode require running in interpreted mode. + bool _isInterpreted(TargetPlatform platform, BuildMode buildMode) { + return platform == TargetPlatform.ios && buildMode == BuildMode.debug; + } + String _getPackagePath(PackageMap packageMap, String package) { return fs.path.dirname(fs.path.fromUri(packageMap.map[package])); } diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart index a84e5c62e90..53a7ed18646 100644 --- a/packages/flutter_tools/lib/src/commands/build_aot.dart +++ b/packages/flutter_tools/lib/src/commands/build_aot.dart @@ -54,29 +54,50 @@ class BuildAotCommand extends BuildSubCommand { @override Future runCommand() async { await super.runCommand(); + final String targetPlatform = argResults['target-platform']; final TargetPlatform platform = getTargetPlatformForName(targetPlatform); if (platform == null) throwToolExit('Unknown platform: $targetPlatform'); - final String typeName = artifacts.getEngineType(platform, getBuildMode()); + final BuildMode buildMode = getBuildMode(); + Status status; if (!argResults['quiet']) { + final String typeName = artifacts.getEngineType(platform, buildMode); status = logger.startProgress('Building AOT snapshot in ${getModeName(getBuildMode())} mode ($typeName)...', expectSlowOperation: true); } final String outputPath = argResults['output-dir'] ?? getAotBuildDirectory(); try { + final bool previewDart2 = argResults['preview-dart-2']; + String mainPath = findMainDartFile(targetFile); final Snapshotter snapshotter = new Snapshotter(); + + // Compile to kernel, if Dart 2. + if (previewDart2) { + mainPath = await snapshotter.compileKernel( + platform: platform, + buildMode: buildMode, + mainPath: mainPath, + outputPath: outputPath, + extraFrontEndOptions: argResults[FlutterOptions.kExtraFrontEndOptions], + ); + if (mainPath == null) { + printError('Compiler terminated unexpectedly.'); + return; + } + } + + // Build AOT snapshot. final int snapshotExitCode = await snapshotter.buildAotSnapshot( platform: platform, - buildMode: getBuildMode(), - mainPath: findMainDartFile(targetFile), + buildMode: buildMode, + mainPath: mainPath, packagesPath: PackageMap.globalPackagesPath, outputPath: outputPath, - previewDart2: argResults['preview-dart-2'], + previewDart2: previewDart2, preferSharedLibrary: argResults['prefer-shared-library'], - extraFrontEndOptions: argResults[FlutterOptions.kExtraFrontEndOptions], extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions], ); if (snapshotExitCode != 0) { diff --git a/packages/flutter_tools/test/base/build_test.dart b/packages/flutter_tools/test/base/build_test.dart index 294e485ca1a..ca5e76418ca 100644 --- a/packages/flutter_tools/test/base/build_test.dart +++ b/packages/flutter_tools/test/base/build_test.dart @@ -9,7 +9,6 @@ import 'dart:convert' show json; import 'package:file/memory.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/build_info.dart'; -import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/base/build.dart'; import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -70,29 +69,6 @@ class _FakeGenSnapshot implements GenSnapshot { } } -class _FakeKernelCompiler implements KernelCompiler { - CompilerOutput output; - - @override - Future compile({ - String sdkRoot, - String mainPath, - String outputFilePath, - String depFilePath, - bool linkPlatformKernelIn: false, - bool aot: false, - List entryPointsJsonFiles, - bool trackWidgetCreation: false, - List extraFrontEndOptions, - String incrementalCompilerByteStorePath, - String packagesPath, - List fileSystemRoots, - String fileSystemScheme, - }) async { - return output; - } -} - void main() { group('SnapshotType', () { test('throws, if build mode is null', () { @@ -606,7 +582,6 @@ void main() { String skyEnginePath; _FakeGenSnapshot genSnapshot; - _FakeKernelCompiler kernelCompiler; MemoryFileSystem fs; Snapshotter snapshotter; MockArtifacts mockArtifacts; @@ -630,7 +605,6 @@ void main() { fs.file(fs.path.join(skyEnginePath, 'sdk_ext', 'vmservice_io.dart')).createSync(); genSnapshot = new _FakeGenSnapshot(); - kernelCompiler = new _FakeKernelCompiler(); snapshotter = new Snapshotter(); mockArtifacts = new MockArtifacts(); mockXcode = new MockXcode(); @@ -648,18 +622,16 @@ void main() { Artifacts: () => mockArtifacts, FileSystem: () => fs, GenSnapshot: () => genSnapshot, - KernelCompiler: () => kernelCompiler, Xcode: () => mockXcode, Xxd: () => mockXxd, }; testUsingContext('builds iOS debug AOT snapshot', () async { - fs.file('main.dart').writeAsStringSync('void main() {}'); + fs.file('main.dill').writeAsStringSync('binary magic'); final String outputPath = fs.path.join('build', 'foo'); fs.directory(outputPath).createSync(recursive: true); - kernelCompiler.output = const CompilerOutput('main.dill', 0); genSnapshot.outputs = { fs.path.join(outputPath, 'vm_snapshot_data'): '', fs.path.join(outputPath, 'vm_snapshot_instr'): '', @@ -672,7 +644,7 @@ void main() { final int genSnapshotExitCode = await snapshotter.buildAotSnapshot( platform: TargetPlatform.ios, buildMode: BuildMode.debug, - mainPath: 'main.dart', + mainPath: 'main.dill', packagesPath: '.packages', outputPath: outputPath, preferSharedLibrary: false, @@ -701,12 +673,11 @@ void main() { }, overrides: contextOverrides); testUsingContext('builds iOS profile AOT snapshot', () async { - fs.file('main.dart').writeAsStringSync('void main() {}'); + fs.file('main.dill').writeAsStringSync('binary magic'); final String outputPath = fs.path.join('build', 'foo'); fs.directory(outputPath).createSync(recursive: true); - kernelCompiler.output = const CompilerOutput('main.dill', 0); genSnapshot.outputs = { fs.path.join(outputPath, 'snapshot_assembly.S'): '', fs.path.join(outputPath, 'snapshot.d'): '', @@ -715,7 +686,7 @@ void main() { final int genSnapshotExitCode = await snapshotter.buildAotSnapshot( platform: TargetPlatform.ios, buildMode: BuildMode.profile, - mainPath: 'main.dart', + mainPath: 'main.dill', packagesPath: '.packages', outputPath: outputPath, preferSharedLibrary: false, @@ -746,12 +717,11 @@ void main() { }, overrides: contextOverrides); testUsingContext('builds iOS release AOT snapshot', () async { - fs.file('main.dart').writeAsStringSync('void main() {}'); + fs.file('main.dill').writeAsStringSync('binary magic'); final String outputPath = fs.path.join('build', 'foo'); fs.directory(outputPath).createSync(recursive: true); - kernelCompiler.output = const CompilerOutput('main.dill', 0); genSnapshot.outputs = { fs.path.join(outputPath, 'snapshot_assembly.S'): '', fs.path.join(outputPath, 'snapshot.d'): '', @@ -760,7 +730,7 @@ void main() { final int genSnapshotExitCode = await snapshotter.buildAotSnapshot( platform: TargetPlatform.ios, buildMode: BuildMode.release, - mainPath: 'main.dart', + mainPath: 'main.dill', packagesPath: '.packages', outputPath: outputPath, preferSharedLibrary: false,