Introduce `--preview-dart-2` option to run new frontend compiler in flutter tools. (#11741) This adds --preview-dart-2 flag that enables use of Dart 2.0 Frontend in Flutter tools.
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index 6f2d0ec..a70a553 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -343,7 +343,6 @@ String route, DebuggingOptions debuggingOptions, Map<String, dynamic> platformArgs, - String kernelPath, bool prebuiltApplication: false, bool applicationNeedsRebuild: false, bool usesTerminalUi: true, @@ -361,7 +360,6 @@ await buildApk( target: mainPath, buildInfo: debuggingOptions.buildInfo, - kernelPath: kernelPath, ); // Package has been built, so we can get the updated application ID and // activity name from the .apk.
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index 6196ea6..e483f2c 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -192,7 +192,7 @@ settings.writeContents(localProperties); } -Future<Null> buildGradleProject(BuildInfo buildInfo, String target, String kernelPath) async { +Future<Null> buildGradleProject(BuildInfo buildInfo, String target) async { // Update the local.properties file with the build mode. // FlutterPlugin v1 reads local.properties to determine build mode. Plugin v2 // uses the standard Android way to determine what to build, but we still @@ -211,7 +211,7 @@ case FlutterPluginVersion.managed: // Fall through. Managed plugin builds the same way as plugin v2. case FlutterPluginVersion.v2: - return _buildGradleProjectV2(gradle, buildInfo, target, kernelPath); + return _buildGradleProjectV2(gradle, buildInfo, target); } } @@ -233,7 +233,7 @@ printStatus('Built $gradleAppOutV1 (${getSizeAsMB(apkFile.lengthSync())}).'); } -Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String target, String kernelPath) async { +Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String target) async { final GradleProject project = await _gradleProject(); final String assembleTask = project.assembleTaskFor(buildInfo); if (assembleTask == null) { @@ -266,8 +266,8 @@ if (target != null) { command.add('-Ptarget=$target'); } - if (kernelPath != null) - command.add('-Pkernel=$kernelPath'); + if (buildInfo.previewDart2) + command.add('-Ppreview-dart-2=true'); command.add(assembleTask); final int exitCode = await runCommandAndStreamOutput( command,
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 9cdd234..c6afb1a 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:meta/meta.dart'; + import 'base/context.dart'; import 'base/file_system.dart'; import 'base/platform.dart'; @@ -16,7 +18,12 @@ snapshotDart, flutterFramework, vmSnapshotData, - isolateSnapshotData + isolateSnapshotData, + platformKernelDill, + platformLibrariesJson, + flutterPatchedSdkPath, + frontendServerSnapshotForEngineDartSdk, + engineDartSdkPath, } String _artifactToFileName(Artifact artifact) { @@ -37,17 +44,37 @@ return 'vm_isolate_snapshot.bin'; case Artifact.isolateSnapshotData: return 'isolate_snapshot.bin'; + case Artifact.platformKernelDill: + return 'platform.dill'; + case Artifact.platformLibrariesJson: + return 'libraries.json'; + case Artifact.flutterPatchedSdkPath: + assert(false, 'No filename for sdk path, should not be invoked'); + return null; + case Artifact.engineDartSdkPath: + return 'dart-sdk'; + case Artifact.frontendServerSnapshotForEngineDartSdk: + return 'frontend_server.dart.snapshot'; } assert(false, 'Invalid artifact $artifact.'); return null; } +class EngineBuildPaths { + const EngineBuildPaths({ @required this.targetEngine, @required this.hostEngine }): + assert(targetEngine != null), + assert(hostEngine != null); + + final String targetEngine; + final String hostEngine; +} + // Manages the engine artifacts of Flutter. abstract class Artifacts { static Artifacts get instance => context[Artifacts]; - static void useLocalEngine(String engineSrcPath, String engineOutPath) { - context.setVariable(Artifacts, new LocalEngineArtifacts(engineSrcPath, engineOutPath)); + static void useLocalEngine(String engineSrcPath, EngineBuildPaths engineBuildPaths) { + context.setVariable(Artifacts, new LocalEngineArtifacts(engineSrcPath, engineBuildPaths.targetEngine, engineBuildPaths.hostEngine)); } // Returns the requested [artifact] for the [platform] and [mode] combination. @@ -91,6 +118,7 @@ switch (artifact) { case Artifact.dartIoEntriesTxt: case Artifact.dartVmEntryPointsTxt: + case Artifact.frontendServerSnapshotForEngineDartSdk: assert(mode != BuildMode.debug, 'Artifact $artifact only available in non-debug mode.'); return fs.path.join(engineDir, _artifactToFileName(artifact)); case Artifact.genSnapshot: @@ -111,6 +139,7 @@ case Artifact.genSnapshot: case Artifact.snapshotDart: case Artifact.flutterFramework: + case Artifact.frontendServerSnapshotForEngineDartSdk: return fs.path.join(engineDir, _artifactToFileName(artifact)); default: assert(false, 'Artifact $artifact not available for platform $platform.'); @@ -118,6 +147,11 @@ } } + String _getFlutterPatchedSdkPath() { + final String engineArtifactsPath = cache.getArtifactDirectory('engine').path; + return fs.path.join(engineArtifactsPath, 'common', 'flutter_patched_sdk'); + } + String _getHostArtifactPath(Artifact artifact, TargetPlatform platform) { switch (artifact) { case Artifact.genSnapshot: @@ -131,9 +165,17 @@ fallThrough: case Artifact.vmSnapshotData: case Artifact.isolateSnapshotData: + case Artifact.frontendServerSnapshotForEngineDartSdk: + case Artifact.engineDartSdkPath: final String engineArtifactsPath = cache.getArtifactDirectory('engine').path; final String platformDirName = getNameForTargetPlatform(platform); return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact)); + case Artifact.platformKernelDill: + return fs.path.join(_getFlutterPatchedSdkPath(), _artifactToFileName(artifact)); + case Artifact.platformLibrariesJson: + return fs.path.join(_getFlutterPatchedSdkPath(), 'lib', _artifactToFileName(artifact)); + case Artifact.flutterPatchedSdkPath: + return _getFlutterPatchedSdkPath(); default: assert(false, 'Artifact $artifact not available for platform $platform.'); return null; @@ -179,8 +221,9 @@ class LocalEngineArtifacts extends Artifacts { final String _engineSrcPath; final String engineOutPath; // TODO(goderbauer): This should be private. + String _hostEngineOutPath; - LocalEngineArtifacts(this._engineSrcPath, this.engineOutPath); + LocalEngineArtifacts(this._engineSrcPath, this.engineOutPath, this._hostEngineOutPath); @override String getArtifactPath(Artifact artifact, [TargetPlatform platform, BuildMode mode]) { @@ -198,8 +241,18 @@ case Artifact.isolateSnapshotData: case Artifact.vmSnapshotData: return fs.path.join(engineOutPath, 'gen', 'flutter', 'lib', 'snapshot', _artifactToFileName(artifact)); + case Artifact.platformKernelDill: + return fs.path.join(_getFlutterPatchedSdkPath(), _artifactToFileName(artifact)); + case Artifact.platformLibrariesJson: + return fs.path.join(_getFlutterPatchedSdkPath(), 'lib', _artifactToFileName(artifact)); case Artifact.flutterFramework: return fs.path.join(engineOutPath, _artifactToFileName(artifact)); + case Artifact.flutterPatchedSdkPath: + return _getFlutterPatchedSdkPath(); + case Artifact.frontendServerSnapshotForEngineDartSdk: + return fs.path.join(_hostEngineOutPath, 'gen', _artifactToFileName(artifact)); + case Artifact.engineDartSdkPath: + return fs.path.join(_hostEngineOutPath, 'dart-sdk'); } assert(false, 'Invalid artifact $artifact.'); return null; @@ -210,6 +263,10 @@ return fs.path.basename(engineOutPath); } + String _getFlutterPatchedSdkPath() { + return fs.path.join(engineOutPath, 'flutter_patched_sdk'); + } + String _genSnapshotPath() { const List<String> clangDirs = const <String>['clang_x86', 'clang_x64', 'clang_i386']; final String genSnapshotName = _artifactToFileName(Artifact.genSnapshot);
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart index c9a5662..b4be334 100644 --- a/packages/flutter_tools/lib/src/build_info.dart +++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -10,7 +10,7 @@ /// Information about a build to be performed or used. class BuildInfo { - const BuildInfo(this.mode, this.flavor); + const BuildInfo(this.mode, this.flavor, { this.previewDart2 }); final BuildMode mode; /// Represents a custom Android product flavor or an Xcode scheme, null for @@ -21,6 +21,9 @@ /// Mode-Flavor (e.g. Release-Paid). final String flavor; + // Whether build should be done using Dart2 Frontend parser. + final bool previewDart2; + static const BuildInfo debug = const BuildInfo(BuildMode.debug, null); static const BuildInfo profile = const BuildInfo(BuildMode.profile, null); static const BuildInfo release = const BuildInfo(BuildMode.release, null);
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index 007e7a5..30ddaed 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart
@@ -255,6 +255,8 @@ List<List<String>> _getBinaryDirs() { final List<List<String>> binaryDirs = <List<String>>[]; + binaryDirs.add(<String>['common', 'flutter_patched_sdk.zip']); + if (cache.includeAllPlatforms) binaryDirs ..addAll(_osxBinaryDirs) @@ -281,18 +283,21 @@ List<List<String>> get _osxBinaryDirs => <List<String>>[ <String>['darwin-x64', 'darwin-x64/artifacts.zip'], + <String>['darwin-x64', 'dart-sdk-darwin-x64.zip'], <String>['android-arm-profile/darwin-x64', 'android-arm-profile/darwin-x64.zip'], <String>['android-arm-release/darwin-x64', 'android-arm-release/darwin-x64.zip'], ]; List<List<String>> get _linuxBinaryDirs => <List<String>>[ <String>['linux-x64', 'linux-x64/artifacts.zip'], + <String>['linux-x64', 'dart-sdk-linux-x64.zip'], <String>['android-arm-profile/linux-x64', 'android-arm-profile/linux-x64.zip'], <String>['android-arm-release/linux-x64', 'android-arm-release/linux-x64.zip'], ]; List<List<String>> get _windowsBinaryDirs => <List<String>>[ <String>['windows-x64', 'windows-x64/artifacts.zip'], + <String>['windows-x64', 'dart-sdk-windows-x64.zip'], <String>['android-arm-profile/windows-x64', 'android-arm-profile/windows-x64.zip'], <String>['android-arm-release/windows-x64', 'android-arm-release/windows-x64.zip'], ];
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart index 3ca4219..4409374 100644 --- a/packages/flutter_tools/lib/src/commands/build_aot.dart +++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -13,6 +13,7 @@ import '../base/process_manager.dart'; import '../base/utils.dart'; import '../build_info.dart'; +import '../compile.dart'; import '../dart/package_map.dart'; import '../globals.dart'; import '../resident_runner.dart'; @@ -35,7 +36,8 @@ allowed: <String>['android-arm', 'ios'] ) ..addFlag('interpreter') - ..addFlag('quiet', defaultsTo: false); + ..addFlag('quiet', defaultsTo: false) + ..addFlag('preview-dart-2', negatable: false); } @override @@ -63,7 +65,8 @@ platform, getBuildMode(), outputPath: argResults['output-dir'], - interpreter: argResults['interpreter'] + interpreter: argResults['interpreter'], + previewDart2: argResults['preview-dart-2'], ); status?.stop(); @@ -90,7 +93,8 @@ TargetPlatform platform, BuildMode buildMode, { String outputPath, - bool interpreter: false + bool interpreter: false, + bool previewDart2: false, }) async { outputPath ??= getAotBuildDirectory(); try { @@ -99,7 +103,8 @@ platform, buildMode, outputPath: outputPath, - interpreter: interpreter + interpreter: interpreter, + previewDart2: previewDart2, ); } on String catch (error) { // Catch the String exceptions thrown from the `runCheckedSync` methods below. @@ -114,7 +119,8 @@ TargetPlatform platform, BuildMode buildMode, { String outputPath, - bool interpreter: false + bool interpreter: false, + bool previewDart2: false, }) async { outputPath ??= getAotBuildDirectory(); if (!isAotBuildMode(buildMode) && !interpreter) { @@ -137,7 +143,11 @@ final String isolateSnapshotInstructions = fs.path.join(outputDir.path, 'isolate_snapshot_instr'); final String dependencies = fs.path.join(outputDir.path, 'snapshot.d'); - final String vmEntryPoints = artifacts.getArtifactPath(Artifact.dartVmEntryPointsTxt, platform, buildMode); + final String vmEntryPoints = artifacts.getArtifactPath( + Artifact.dartVmEntryPointsTxt, + platform, + buildMode, + ); final String ioEntryPoints = artifacts.getArtifactPath(Artifact.dartIoEntriesTxt, platform, buildMode); final PackageMap packageMap = new PackageMap(PackageMap.globalPackagesPath); @@ -266,6 +276,13 @@ ]); } + if (previewDart2) { + mainPath = await compile( + sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), + mainPath: mainPath, + ); + } + genSnapshotCmd.add(mainPath); final SnapshotType snapshotType = new SnapshotType(platform, buildMode);
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart index 791b1cc..63c757d 100644 --- a/packages/flutter_tools/lib/src/commands/build_apk.dart +++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -35,6 +35,7 @@ BuildApkCommand() { usesTargetOption(); addBuildModeFlags(); + argParser.addFlag('preview-dart-2', negatable: false); usesFlavorOption(); usesPubOption(); } @@ -51,16 +52,13 @@ @override Future<Null> runCommand() async { await super.runCommand(); - - final BuildInfo buildInfo = getBuildInfo(); - await buildApk(buildInfo: buildInfo, target: targetFile); + await buildApk(buildInfo: getBuildInfo(), target: targetFile); } } Future<Null> buildApk({ String target, - BuildInfo buildInfo: BuildInfo.debug, - String kernelPath, + BuildInfo buildInfo: BuildInfo.debug }) async { if (!isProjectUsingGradle()) { throwToolExit( @@ -81,5 +79,5 @@ throwToolExit('Try re-installing or updating your Android SDK.'); } - return buildGradleProject(buildInfo, target, kernelPath); + return buildGradleProject(buildInfo, target); }
diff --git a/packages/flutter_tools/lib/src/commands/build_flx.dart b/packages/flutter_tools/lib/src/commands/build_flx.dart index 5495a71..9188cd5 100644 --- a/packages/flutter_tools/lib/src/commands/build_flx.dart +++ b/packages/flutter_tools/lib/src/commands/build_flx.dart
@@ -20,7 +20,7 @@ argParser.addOption('output-file', abbr: 'o', defaultsTo: defaultFlxOutputPath); argParser.addOption('snapshot', defaultsTo: defaultSnapshotPath); argParser.addOption('depfile', defaultsTo: defaultDepfilePath); - argParser.addOption('kernel'); + argParser.addFlag('preview-dart-2', negatable: false); argParser.addOption('working-dir', defaultsTo: getAssetBuildDirectory()); argParser.addFlag('report-licensed-packages', help: 'Whether to report the names of all the packages that are included in the application\'s LICENSE file.', defaultsTo: false); usesPubOption(); @@ -49,7 +49,7 @@ depfilePath: argResults['depfile'], privateKeyPath: argResults['private-key'], workingDirPath: argResults['working-dir'], - kernelPath: argResults['kernel'], + previewDart2: argResults['preview-dart-2'], precompiledSnapshot: argResults['precompiled'], reportLicensedPackages: argResults['report-licensed-packages'] );
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 7ca63a0..2229185 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -110,11 +110,10 @@ argParser.addOption('use-application-binary', hide: !verboseHelp, help: 'Specify a pre-built application binary to use when running.'); - argParser.addOption('kernel', + argParser.addFlag('preview-dart-2', hide: !verboseHelp, - help: 'Path to a pre-built kernel blob to use when running.\n' - 'This option only exists for testing new kernel code execution on devices\n' - 'and is not needed during normal application development.'); + defaultsTo: false, + help: 'Preview Dart 2.0 functionality.'); argParser.addOption('packages', hide: !verboseHelp, help: 'Specify the path to the .packages file.'); @@ -177,7 +176,7 @@ @override Future<Map<String, String>> get usageValues async { final bool isEmulator = await devices[0].isLocalEmulator; - final String deviceType = devices.length == 1 + final String deviceType = devices.length == 1 ? getNameForTargetPlatform(await devices[0].targetPlatform) : 'multiple'; @@ -300,7 +299,7 @@ } final List<FlutterDevice> flutterDevices = devices.map((Device device) { - return new FlutterDevice(device); + return new FlutterDevice(device, previewDart2: argResults['preview-dart-2']); }).toList(); ResidentRunner runner; @@ -311,7 +310,7 @@ debuggingOptions: _createDebuggingOptions(), benchmarkMode: argResults['benchmark'], applicationBinary: argResults['use-application-binary'], - kernelFilePath: argResults['kernel'], + previewDart2: argResults['preview-dart-2'], projectRootPath: argResults['project-root'], packagesFilePath: argResults['packages'], projectAssets: argResults['project-assets'], @@ -324,6 +323,7 @@ debuggingOptions: _createDebuggingOptions(), traceStartup: traceStartup, applicationBinary: argResults['use-application-binary'], + previewDart2: argResults['preview-dart-2'], stayResident: stayResident, ); }
diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart new file mode 100644 index 0000000..9c11d37 --- /dev/null +++ b/packages/flutter_tools/lib/src/compile.dart
@@ -0,0 +1,149 @@ +// Copyright 2017 The Chromium 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 'dart:async'; +import 'dart:convert'; + +import 'package:flutter_tools/src/base/process_manager.dart'; +import 'package:usage/uuid/uuid.dart'; + +import 'artifacts.dart'; +import 'base/file_system.dart'; +import 'base/io.dart'; +import 'base/process_manager.dart'; +import 'globals.dart'; + +String _dartExecutable() { + final String engineDartSdkPath = artifacts.getArtifactPath( + Artifact.engineDartSdkPath + ); + return fs.path.join(engineDartSdkPath, 'bin', 'dart'); +} + +class _StdoutHandler { + String boundaryKey; + Completer<String> outputFilename = new Completer<String>(); + + void handler(String string) { + const String kResultPrefix = 'result '; + if (boundaryKey == null) { + if (string.startsWith(kResultPrefix)) + boundaryKey = string.substring(kResultPrefix.length); + } else if (string.startsWith(boundaryKey)) + outputFilename.complete(string.length > boundaryKey.length + ? string.substring(boundaryKey.length + 1) + : null); + else + printTrace('compile debug message: $string'); + } +} + +Future<String> compile({String sdkRoot, String mainPath}) async { + final String frontendServer = artifacts.getArtifactPath( + Artifact.frontendServerSnapshotForEngineDartSdk + ); + + // This is a URI, not a file path, so the forward slash is correct even on Windows. + if (!sdkRoot.endsWith('/')) + sdkRoot = '$sdkRoot/'; + final Process server = await processManager.start(<String>[ + _dartExecutable(), + frontendServer, + '--sdk-root', + sdkRoot, + mainPath + ]); + + final _StdoutHandler stdoutHandler = new _StdoutHandler(); + server.stderr + .transform(UTF8.decoder) + .listen((String s) { printTrace('compile debug message: $s'); }); + server.stdout + .transform(UTF8.decoder) + .transform(const LineSplitter()) + .listen(stdoutHandler.handler); + await server.exitCode; + return stdoutHandler.outputFilename.future; +} + +/// Wrapper around incremental frontend server compiler, that communicates with +/// server via stdin/stdout. +/// +/// The wrapper is intended to stay resident in memory as user changes, reloads, +/// restarts the Flutter app. +class ResidentCompiler { + ResidentCompiler(this._sdkRoot) { + assert(_sdkRoot != null); + // This is a URI, not a file path, so the forward slash is correct even on Windows. + if (!_sdkRoot.endsWith('/')) + _sdkRoot = '$_sdkRoot/'; + } + + String _sdkRoot; + Process _server; + final _StdoutHandler stdoutHandler = new _StdoutHandler(); + + /// If invoked for the first time, it compiles Dart script identified by + /// [mainPath], [invalidatedFiles] list is ignored. + /// Otherwise, [mainPath] is ignored, but [invalidatedFiles] is recompiled + /// into new binary. + /// Binary file name is returned if compilation was successful, otherwise + /// `null` is returned. + Future<String> recompile(String mainPath, List<String> invalidatedFiles) async { + // First time recompile is called we actually have to compile the app from + // scratch ignoring list of invalidated files. + if (_server == null) + return _compile(mainPath); + + final String inputKey = new Uuid().generateV4(); + _server.stdin.writeln('recompile $inputKey'); + for (String invalidatedFile in invalidatedFiles) + _server.stdin.writeln(invalidatedFile); + _server.stdin.writeln(inputKey); + + return stdoutHandler.outputFilename.future; + } + + Future<String> _compile(String scriptFilename) async { + if (_server == null) { + final String frontendServer = artifacts.getArtifactPath( + Artifact.frontendServerSnapshotForEngineDartSdk + ); + _server = await processManager.start(<String>[ + _dartExecutable(), + frontendServer, + '--sdk-root', + _sdkRoot, + '--incremental' + ]); + } + _server.stdout + .transform(UTF8.decoder) + .transform(const LineSplitter()) + .listen(stdoutHandler.handler); + _server.stderr + .transform(UTF8.decoder) + .transform(const LineSplitter()) + .listen((String s) { printTrace('compile debug message: $s'); }); + + _server.stdin.writeln('compile $scriptFilename'); + + return stdoutHandler.outputFilename.future; + } + + + /// Should be invoked when results of compilation are accepted by the client. + /// + /// Either [accept] or [reject] should be called after every [recompile] call. + void accept() { + _server.stdin.writeln('accept'); + } + + /// Should be invoked when results of compilation are rejected by the client. + /// + /// Either [accept] or [reject] should be called after every [recompile] call. + void reject() { + _server.stdin.writeln('reject'); + } +}
diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart index 72c0369..5a0e550 100644 --- a/packages/flutter_tools/lib/src/devfs.dart +++ b/packages/flutter_tools/lib/src/devfs.dart
@@ -12,6 +12,7 @@ import 'base/file_system.dart'; import 'base/io.dart'; import 'build_info.dart'; +import 'compile.dart'; import 'dart/package_map.dart'; import 'globals.dart'; import 'vmservice.dart'; @@ -361,9 +362,12 @@ /// Update files on the device and return the number of bytes sync'd Future<int> update({ + String mainPath, + String target, AssetBundle bundle, bool bundleDirty: false, Set<String> fileFilter, + ResidentCompiler generator, }) async { // Mark all entries as possibly deleted. for (DevFSContent content in _entries.values) { @@ -427,6 +431,18 @@ }); if (dirtyEntries.isNotEmpty) { printTrace('Updating files'); + if (generator != null) { + final List<String> invalidatedFiles = <String>[]; + dirtyEntries.forEach((Uri deviceUri, DevFSContent content) { + if (content is DevFSFileContent) + invalidatedFiles.add(content.file.uri.toString()); + }); + final String compiledBinary = await generator.recompile(mainPath, invalidatedFiles); + if (compiledBinary != null && compiledBinary.isNotEmpty) + dirtyEntries.putIfAbsent(Uri.parse(target + '.dill'), + () => new DevFSFileContent(fs.file(compiledBinary))); + } + if (_httpWriter != null) { try { await _httpWriter.write(dirtyEntries);
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 52434aa..9bc0634 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart
@@ -240,7 +240,6 @@ String route, DebuggingOptions debuggingOptions, Map<String, dynamic> platformArgs, - String kernelPath, bool prebuiltApplication: false, bool applicationNeedsRebuild: false, bool usesTerminalUi: true,
diff --git a/packages/flutter_tools/lib/src/flx.dart b/packages/flutter_tools/lib/src/flx.dart index 2d9abf2..f51ff1e 100644 --- a/packages/flutter_tools/lib/src/flx.dart +++ b/packages/flutter_tools/lib/src/flx.dart
@@ -4,11 +4,13 @@ import 'dart:async'; +import 'artifacts.dart'; import 'asset.dart'; import 'base/build.dart'; import 'base/common.dart'; import 'base/file_system.dart'; import 'build_info.dart'; +import 'compile.dart'; import 'dart/package_map.dart'; import 'devfs.dart'; import 'globals.dart'; @@ -25,6 +27,7 @@ const String _kKernelKey = 'kernel_blob.bin'; const String _kSnapshotKey = 'snapshot_blob.bin'; const String _kDylibKey = 'libapp.so'; +const String _kPlatformKernelKey = 'platform.dill'; Future<Null> build({ String mainPath: defaultMainPath, @@ -35,7 +38,7 @@ String privateKeyPath: defaultPrivateKeyPath, String workingDirPath, String packagesPath, - String kernelPath, + bool previewDart2 : false, bool precompiledSnapshot: false, bool reportLicensedPackages: false }) async { @@ -46,7 +49,7 @@ packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath); File snapshotFile; - if (!precompiledSnapshot) { + if (!precompiledSnapshot && !previewDart2) { ensureDirectoryExists(snapshotPath); // In a precompiled snapshot, the instruction buffer contains script @@ -65,8 +68,13 @@ } DevFSContent kernelContent; - if (kernelPath != null) - kernelContent = new DevFSFileContent(fs.file(kernelPath)); + if (!precompiledSnapshot && previewDart2) { + final String kernelBinaryFilename = await compile( + sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), + mainPath: fs.file(mainPath).absolute.path + ); + kernelContent = new DevFSFileContent(fs.file(kernelBinaryFilename)); + } return assemble( manifestPath: manifestPath, @@ -118,8 +126,11 @@ .expand((DevFSContent content) => content.fileDependencies) .toList(); - if (kernelContent != null) + if (kernelContent != null) { + final String platformKernelDill = artifacts.getArtifactPath(Artifact.platformKernelDill); zipBuilder.entries[_kKernelKey] = kernelContent; + zipBuilder.entries[_kPlatformKernelKey] = new DevFSFileContent(fs.file(platformKernelDill)); + } if (snapshotFile != null) zipBuilder.entries[_kSnapshotKey] = new DevFSFileContent(snapshotFile); if (dylibFile != null)
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart index 1587577..08af8c7 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
@@ -65,7 +65,7 @@ DebuggingOptions debuggingOptions, Map<String, dynamic> platformArgs, bool prebuiltApplication: false, - String kernelPath, + bool previewDart2: false, bool applicationNeedsRebuild: false, bool usesTerminalUi: false, }) => new Future<Null>.error('unimplemented');
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index 1ad358c..5d311d1 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -170,7 +170,7 @@ DebuggingOptions debuggingOptions, Map<String, dynamic> platformArgs, bool prebuiltApplication: false, - String kernelPath, + bool previewDart2: false, bool applicationNeedsRebuild: false, bool usesTerminalUi: true, }) async {
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index 03c73e4..ce98c7c 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -311,7 +311,7 @@ String route, DebuggingOptions debuggingOptions, Map<String, dynamic> platformArgs, - String kernelPath, + bool previewDart2: false, bool prebuiltApplication: false, bool applicationNeedsRebuild: false, bool usesTerminalUi: true,
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 003531d..3e1db2a 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -8,6 +8,7 @@ import 'android/gradle.dart'; import 'application_package.dart'; +import 'artifacts.dart'; import 'asset.dart'; import 'base/common.dart'; import 'base/file_system.dart'; @@ -16,6 +17,7 @@ import 'base/terminal.dart'; import 'base/utils.dart'; import 'build_info.dart'; +import 'compile.dart'; import 'dart/dependencies.dart'; import 'dart/package_map.dart'; import 'dependency_checker.dart'; @@ -32,10 +34,15 @@ List<VMService> vmServices; DevFS devFS; ApplicationPackage package; + ResidentCompiler generator; StreamSubscription<String> _loggingSubscription; - FlutterDevice(this.device); + FlutterDevice(this.device, { bool previewDart2 : false }) { + if (previewDart2) + generator = new ResidentCompiler( + artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath)); + } String viewFilter; @@ -244,7 +251,6 @@ platformArgs: platformArgs, route: route, prebuiltApplication: prebuiltMode, - kernelPath: hotRunner.kernelFilePath, applicationNeedsRebuild: shouldBuild || hasDirtyDependencies, usesTerminalUi: hotRunner.usesTerminalUI, ); @@ -319,6 +325,8 @@ } Future<bool> updateDevFS({ + String mainPath, + String target, AssetBundle bundle, bool bundleDirty: false, Set<String> fileFilter @@ -330,9 +338,12 @@ int bytes = 0; try { bytes = await devFS.update( + mainPath: mainPath, + target: target, bundle: bundle, bundleDirty: bundleDirty, - fileFilter: fileFilter + fileFilter: fileFilter, + generator: generator ); } on DevFSException { devFSStatus.cancel(); @@ -342,6 +353,13 @@ printTrace('Synced ${getSizeAsMB(bytes)}.'); return true; } + + void updateReloadStatus(bool wasReloadSuccessful) { + if (wasReloadSuccessful) + generator?.accept(); + else + generator?.reject(); + } } // Shared code between different resident application runners.
diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart index 95894b53..76d8bd6 100644 --- a/packages/flutter_tools/lib/src/run_cold.dart +++ b/packages/flutter_tools/lib/src/run_cold.dart
@@ -20,6 +20,7 @@ bool usesTerminalUI: true, this.traceStartup: false, this.applicationBinary, + this.previewDart2 : false, bool stayResident: true, }) : super(devices, target: target, @@ -29,6 +30,7 @@ final bool traceStartup; final String applicationBinary; + final bool previewDart2; @override Future<int> run({
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index a4faac3..464ded4 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -4,9 +4,9 @@ import 'dart:async'; -import 'package:meta/meta.dart'; import 'package:json_rpc_2/error_code.dart' as rpc_error_code; import 'package:json_rpc_2/json_rpc_2.dart' as rpc; +import 'package:meta/meta.dart'; import 'base/context.dart'; import 'base/file_system.dart'; @@ -39,7 +39,7 @@ bool usesTerminalUI: true, this.benchmarkMode: false, this.applicationBinary, - this.kernelFilePath, + this.previewDart2: false, String projectRootPath, String packagesFilePath, String projectAssets, @@ -60,7 +60,7 @@ final Map<String, int> benchmarkData = <String, int>{}; // The initial launch is from a snapshot. bool _runningFromSnapshot = true; - String kernelFilePath; + bool previewDart2 = false; bool _refreshDartDependencies() { if (!hotRunnerConfig.computeDartDependencies) { @@ -112,7 +112,6 @@ for (FlutterDevice device in flutterDevices) device.initLogReader(); - try { final List<Uri> baseUris = await _initDevFS(); if (connectionInfoCompleter != null) { @@ -251,6 +250,8 @@ for (FlutterDevice device in flutterDevices) { final bool result = await device.updateDevFS( + mainPath: mainPath, + target: target, bundle: assetBundle, bundleDirty: rebuildBundle, fileFilter: _dartDependencies, @@ -363,15 +364,20 @@ } /// Returns [true] if the reload was successful. - static bool validateReloadReport(Map<String, dynamic> reloadReport) { + /// Prints errors if [printErrors] is [true]. + static bool validateReloadReport(Map<String, dynamic> reloadReport, + { bool printErrors: true }) { if (reloadReport['type'] != 'ReloadReport') { - printError('Hot reload received invalid response: $reloadReport'); + if (printErrors) + printError('Hot reload received invalid response: $reloadReport'); return false; } if (!reloadReport['success']) { - printError('Hot reload was rejected:'); - for (Map<String, dynamic> notice in reloadReport['details']['notices']) - printError('${notice['message']}'); + if (printErrors) { + printError('Hot reload was rejected:'); + for (Map<String, dynamic> notice in reloadReport['details']['notices']) + printError('${notice['message']}'); + } return false; } return true; @@ -464,23 +470,39 @@ return new OperationResult(1, 'Dart source error'); String reloadMessage; try { - final String entryPath = fs.path.relative(mainPath, from: projectRootPath); + final String entryPath = fs.path.relative( + previewDart2 ? mainPath + '.dill' : mainPath, + from: projectRootPath + ); if (benchmarkMode) vmReloadTimer.start(); - final List<Future<Map<String, dynamic>>> reloadReportFutures = <Future<Map<String, dynamic>>>[]; + final Completer<Map<String, dynamic>> retrieveFirstReloadReport = new Completer<Map<String, dynamic>>(); + + int countExpectedReports = 0; for (FlutterDevice device in flutterDevices) { + // List has one report per Flutter view. final List<Future<Map<String, dynamic>>> reports = device.reloadSources( entryPath, pause: pause ); - reloadReportFutures.addAll(reports); + countExpectedReports += reports.length; + Future.wait(reports).then((List<Map<String, dynamic>> list) { + // TODO(aam): Investigate why we are validating only first reload report, + // which seems to be current behavior + final Map<String, dynamic> firstReport = list.first; + // Don't print errors because they will be printed further down when + // `validateReloadReport` is called again. + device.updateReloadStatus(validateReloadReport(firstReport, + printErrors: false)); + retrieveFirstReloadReport.complete(firstReport); + }); } - if (reloadReportFutures.isEmpty) { + + if (countExpectedReports == 0) { printError('Unable to hot reload. No instance of Flutter is currently running.'); return new OperationResult(1, 'No instances running'); } - final Map<String, dynamic> reloadReport = (await Future.wait(reloadReportFutures)).first; - + final Map<String, dynamic> reloadReport = await retrieveFirstReloadReport.future; if (!validateReloadReport(reloadReport)) { // Reload failed. flutterUsage.sendEvent('hot', 'reload-reject');
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 18f33df..8a2d30f 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -143,10 +143,13 @@ } BuildInfo getBuildInfo() { - if (argParser.options.containsKey('flavor')) - return new BuildInfo(getBuildMode(), argResults['flavor']); - else - return new BuildInfo(getBuildMode(), null); + return new BuildInfo(getBuildMode(), + argParser.options.containsKey('flavor') + ? argResults['flavor'] + : null, + previewDart2: argParser.options.containsKey('preview-dart-2') + ? argResults['preview-dart-2'] + : false); } void setupApplicationPackages() {
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart index 408db71..dbdcb11 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -270,8 +270,7 @@ } if (globalResults['machine']) { - printError('The --machine flag is only valid with the --version flag.'); - throw new ProcessExit(2); + throwToolExit('The --machine flag is only valid with the --version flag.', exitCode: 2); } await super.runCommand(globalResults); @@ -304,40 +303,44 @@ engineSourcePath ??= _tryEnginePath(fs.path.join(Cache.flutterRoot, '../engine/src')); if (engineSourcePath == null) { - printError('Unable to detect local Flutter engine build directory.\n' - 'Either specify a dependency_override for the $kFlutterEnginePackageName package in your pubspec.yaml and\n' - 'ensure --package-root is set if necessary, or set the \$$kFlutterEngineEnvironmentVariableName environment variable, or\n' - 'use --local-engine-src-path to specify the path to the root of your flutter/engine repository.'); - throw new ProcessExit(2); + throwToolExit('Unable to detect local Flutter engine build directory.\n' + 'Either specify a dependency_override for the $kFlutterEnginePackageName package in your pubspec.yaml and\n' + 'ensure --package-root is set if necessary, or set the \$$kFlutterEngineEnvironmentVariableName environment variable, or\n' + 'use --local-engine-src-path to specify the path to the root of your flutter/engine repository.', + exitCode: 2); } } if (engineSourcePath != null && _tryEnginePath(engineSourcePath) == null) { - printError('Unable to detect a Flutter engine build directory in $engineSourcePath.\n' - 'Please ensure that $engineSourcePath is a Flutter engine \'src\' directory and that\n' - 'you have compiled the engine in that directory, which should produce an \'out\' directory'); - throw new ProcessExit(2); + throwToolExit('Unable to detect a Flutter engine build directory in $engineSourcePath.\n' + 'Please ensure that $engineSourcePath is a Flutter engine \'src\' directory and that\n' + 'you have compiled the engine in that directory, which should produce an \'out\' directory', + exitCode: 2); } return engineSourcePath; } - String _findEngineBuildPath(ArgResults globalResults, String enginePath) { + EngineBuildPaths _findEngineBuildPath(ArgResults globalResults, String enginePath) { String localEngine; if (globalResults['local-engine'] != null) { localEngine = globalResults['local-engine']; } else { - printError('You must specify --local-engine if you are using a locally built engine.'); - throw new ProcessExit(2); + throwToolExit('You must specify --local-engine if you are using a locally built engine.', exitCode: 2); } final String engineBuildPath = fs.path.normalize(fs.path.join(enginePath, 'out', localEngine)); if (!fs.isDirectorySync(engineBuildPath)) { - printError('No Flutter engine build found at $engineBuildPath.'); - throw new ProcessExit(2); + throwToolExit('No Flutter engine build found at $engineBuildPath.', exitCode: 2); } - return engineBuildPath; + final String hostLocalEngine = 'host_' + localEngine.substring(localEngine.indexOf('_') + 1); + final String engineHostBuildPath = fs.path.normalize(fs.path.join(enginePath, 'out', hostLocalEngine)); + if (!fs.isDirectorySync(engineHostBuildPath)) { + throwToolExit('No Flutter host engine build found at $engineHostBuildPath.', exitCode: 2); + } + + return new EngineBuildPaths(targetEngine: engineBuildPath, hostEngine: engineHostBuildPath); } static void initFlutterRoot() {