diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 9b11fde9d8c..ec33659f41b 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -26,6 +26,7 @@ enum Artifact { flutterPatchedSdkPath, frontendServerSnapshotForEngineDartSdk, engineDartSdkPath, + engineDartBinary, } String _artifactToFileName(Artifact artifact) { @@ -57,6 +58,8 @@ String _artifactToFileName(Artifact artifact) { return 'dart-sdk'; case Artifact.frontendServerSnapshotForEngineDartSdk: return 'frontend_server.dart.snapshot'; + case Artifact.engineDartBinary: + return 'dart'; } assert(false, 'Invalid artifact $artifact.'); return null; @@ -170,6 +173,8 @@ class CachedArtifacts extends Artifacts { return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact)); case Artifact.engineDartSdkPath: return dartSdkPath; + case Artifact.engineDartBinary: + return fs.path.join(dartSdkPath,'bin', _artifactToFileName(artifact)); case Artifact.platformKernelDill: return fs.path.join(_getFlutterPatchedSdkPath(), _artifactToFileName(artifact)); case Artifact.platformLibrariesJson: @@ -252,6 +257,8 @@ class LocalEngineArtifacts extends Artifacts { return fs.path.join(_hostEngineOutPath, 'gen', _artifactToFileName(artifact)); case Artifact.engineDartSdkPath: return fs.path.join(_hostEngineOutPath, 'dart-sdk'); + case Artifact.engineDartBinary: + return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', _artifactToFileName(artifact)); } assert(false, 'Invalid artifact $artifact.'); return null; diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index 251573e25ad..e374eda5457 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -357,6 +357,7 @@ class AppDomain extends Domain { @required bool trackWidgetCreation, String projectRootPath, String packagesFilePath, + String dillOutputPath, bool ipv6: false, }) async { if (await device.isLocalEmulator && !options.buildInfo.supportsEmulator) { @@ -371,6 +372,7 @@ class AppDomain extends Domain { device, previewDart2: options.buildInfo.previewDart2, trackWidgetCreation: trackWidgetCreation, + dillOutputPath: dillOutputPath, ); ResidentRunner runner; @@ -384,6 +386,7 @@ class AppDomain extends Domain { applicationBinary: applicationBinary, projectRootPath: projectRootPath, packagesFilePath: packagesFilePath, + dillOutputPath: dillOutputPath, ipv6: ipv6, hostIsIde: true, ); diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index b3699873f66..8541b5dce10 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -147,6 +147,10 @@ class RunCommand extends RunCommandBase { 'results out to "refresh_benchmark.json", and exit. This flag is\n' 'intended for use in generating automated flutter benchmarks.'); + argParser.addOption('output-dill', + hide: !verboseHelp, + help: 'Specify the path to frontend server output kernel file.'); + argParser.addOption(FlutterOptions.kExtraFrontEndOptions, hide: true); argParser.addOption(FlutterOptions.kExtraGenSnapshotOptions, hide: true); } @@ -262,6 +266,7 @@ class RunCommand extends RunCommandBase { trackWidgetCreation: argResults['track-widget-creation'], projectRootPath: argResults['project-root'], packagesFilePath: globalResults['packages'], + dillOutputPath: argResults['output-dill'], ipv6: ipv6, ); } catch (error) { @@ -301,6 +306,7 @@ class RunCommand extends RunCommandBase { device, previewDart2: argResults['preview-dart-2'], trackWidgetCreation: argResults['track-widget-creation'], + dillOutputPath: argResults['output-dill'], ); }).toList(); @@ -314,6 +320,7 @@ class RunCommand extends RunCommandBase { applicationBinary: argResults['use-application-binary'], projectRootPath: argResults['project-root'], packagesFilePath: globalResults['packages'], + dillOutputPath: argResults['output-dill'], stayResident: stayResident, ipv6: ipv6, ); diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index bfee499b37a..f99d21b8e3c 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -8,24 +8,10 @@ import 'dart:convert'; import 'package:usage/uuid/uuid.dart'; import 'artifacts.dart'; -import 'base/common.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 - ); - if (!fs.isDirectorySync(engineDartSdkPath)) { - throwToolExit('No dart sdk Flutter host engine build found at $engineDartSdkPath.\n' - 'Note that corresponding host engine build is required even when targeting particular device platforms.', - exitCode: 2); - } - return fs.path.join(engineDartSdkPath, 'bin', 'dart'); -} - class _StdoutHandler { _StdoutHandler() { reset(); @@ -74,7 +60,7 @@ Future compile( if (!sdkRoot.endsWith('/')) sdkRoot = '$sdkRoot/'; final List command = [ - _dartExecutable(), + artifacts.getArtifactPath(Artifact.engineDartBinary), frontendServer, '--sdk-root', sdkRoot, @@ -154,13 +140,13 @@ class ResidentCompiler { /// Binary file name is returned if compilation was successful, otherwise /// null is returned. Future recompile(String mainPath, List invalidatedFiles, - {String outputPath}) async { + {String outputPath, String packagesFilePath}) async { stdoutHandler.reset(); // 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, outputPath); + return _compile(mainPath, outputPath, packagesFilePath); final String inputKey = new Uuid().generateV4(); _server.stdin.writeln('recompile ${mainPath != null ? mainPath + " ": ""}$inputKey'); @@ -170,12 +156,12 @@ class ResidentCompiler { return stdoutHandler.outputFilename.future; } - Future _compile(String scriptFilename, String outputPath) async { + Future _compile(String scriptFilename, String outputPath, String packagesFilePath) async { final String frontendServer = artifacts.getArtifactPath( Artifact.frontendServerSnapshotForEngineDartSdk ); final List args = [ - _dartExecutable(), + artifacts.getArtifactPath(Artifact.engineDartBinary), frontendServer, '--sdk-root', _sdkRoot, @@ -186,6 +172,9 @@ class ResidentCompiler { if (outputPath != null) { args.addAll(['--output-dill', outputPath]); } + if (packagesFilePath != null) { + args.addAll(['--packages', packagesFilePath]); + } if (_trackWidgetCreation) { args.add('--track-widget-creation'); } diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart index 00a79b71053..efdf3edbc5e 100644 --- a/packages/flutter_tools/lib/src/devfs.dart +++ b/packages/flutter_tools/lib/src/devfs.dart @@ -407,6 +407,7 @@ class DevFS { bool bundleDirty: false, Set fileFilter, ResidentCompiler generator, + String dillOutputPath, bool fullRestart: false, }) async { // Mark all entries as possibly deleted. @@ -500,7 +501,8 @@ class DevFS { } final String compiledBinary = await generator.recompile(mainPath, invalidatedFiles, - outputPath: fs.path.join(getBuildDirectory(), 'app.dill')); + outputPath: dillOutputPath ?? fs.path.join(getBuildDirectory(), 'app.dill'), + packagesFilePath : _packagesFilePath); if (compiledBinary != null && compiledBinary.isNotEmpty) dirtyEntries.putIfAbsent( Uri.parse(target + '.dill'), diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index f671cd2fde2..3abc1525ad4 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -35,12 +35,14 @@ class FlutterDevice { DevFS devFS; ApplicationPackage package; ResidentCompiler generator; + String dillOutputPath; StreamSubscription _loggingSubscription; FlutterDevice(this.device, { @required bool previewDart2, @required bool trackWidgetCreation, + this.dillOutputPath, }) { if (previewDart2) { generator = new ResidentCompiler( @@ -386,7 +388,8 @@ class FlutterDevice { bundleDirty: bundleDirty, fileFilter: fileFilter, generator: generator, - fullRestart: fullRestart + fullRestart: fullRestart, + dillOutputPath: dillOutputPath, ); } on DevFSException { devFSStatus.cancel(); diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index f3258f935ae..b0f65d651fd 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -42,6 +42,7 @@ class HotRunner extends ResidentRunner { this.hostIsIde: false, String projectRootPath, String packagesFilePath, + this.dillOutputPath, bool stayResident: true, bool ipv6: false, }) : super(devices, @@ -57,6 +58,7 @@ class HotRunner extends ResidentRunner { final String applicationBinary; final bool hostIsIde; Set _dartDependencies; + final String dillOutputPath; final Map> benchmarkData = >{}; // The initial launch is from a snapshot. @@ -334,7 +336,12 @@ class HotRunner extends ResidentRunner { for (FlutterDevice device in flutterDevices) { final Uri deviceEntryUri = device.devFS.baseUri.resolveUri( fs.path.toUri(entryUri)); - final Uri devicePackagesUri = device.devFS.baseUri.resolve('.packages'); + Uri devicePackagesUri; + if (packagesFilePath != null) { + devicePackagesUri = fs.path.toUri(packagesFilePath); + } else { + devicePackagesUri = device.devFS.baseUri.resolve('.packages'); + } final Uri deviceAssetsDirectoryUri = device.devFS.baseUri.resolveUri( fs.path.toUri(getAssetBuildDirectory())); await _launchInView(device, @@ -389,7 +396,10 @@ class HotRunner extends ResidentRunner { } // We are now running from source. _runningFromSnapshot = false; - await _launchFromDevFS(debuggingOptions.buildInfo.previewDart2 ? mainPath + '.dill' : mainPath); + final String launchPath = debuggingOptions.buildInfo.previewDart2 + ? dillOutputPath ?? mainPath + '.dill' + : mainPath; + await _launchFromDevFS(launchPath); restartTimer.stop(); printTrace('Restart performed in ' '${getElapsedAsMilliseconds(restartTimer.elapsed)}.'); diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index a51e2b698a1..19b45cde744 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -59,6 +59,8 @@ void installHook({ bool machine: false, bool startPaused: false, bool previewDart2: false, + int port: 0, + String dillFilePath, int observatoryPort, InternetAddressType serverType: InternetAddressType.IP_V4, }) { @@ -75,6 +77,8 @@ void installHook({ explicitObservatoryPort: observatoryPort, host: _kHosts[serverType], previewDart2: previewDart2, + port: port, + dillFilePath: dillFilePath, ), ); } @@ -100,6 +104,8 @@ class _FlutterPlatform extends PlatformPlugin { this.explicitObservatoryPort, this.host, this.previewDart2, + this.port, + this.dillFilePath, }) : assert(shellPath != null) { compilerController.stream.listen((CompilationRequest request) async { final bool isEmpty = compilationQueue.isEmpty; @@ -133,6 +139,8 @@ class _FlutterPlatform extends PlatformPlugin { final int explicitObservatoryPort; final InternetAddress host; final bool previewDart2; + final int port; + final String dillFilePath; final StreamController compilerController = new StreamController(); ResidentCompiler compiler = @@ -193,7 +201,7 @@ class _FlutterPlatform extends PlatformPlugin { controller.sink.done.whenComplete(() { controllerSinkClosed = true; }); // ignore: unawaited_futures // Prepare our WebSocket server to talk to the engine subproces. - final HttpServer server = await HttpServer.bind(host, 0); + final HttpServer server = await HttpServer.bind(host, port); finalizers.add(() async { printTrace('test $ourTestCount: shutting down test harness socket server'); await server.close(force: true); @@ -218,56 +226,71 @@ class _FlutterPlatform extends PlatformPlugin { ); // Prepare a temporary directory to store the Dart file that will talk to us. - final Directory temporaryDirectory = fs.systemTempDirectory.createTempSync('dart_test_listener'); - finalizers.add(() async { - printTrace('test $ourTestCount: deleting temporary directory'); - temporaryDirectory.deleteSync(recursive: true); - }); - - // Prepare the Dart file that will talk to us and start the test. - final File listenerFile = fs.file('${temporaryDirectory.path}/listener.dart'); - listenerFile.createSync(); - listenerFile.writeAsStringSync(_generateTestMain( - testUrl: fs.path.toUri(fs.path.absolute(testPath)).toString(), - encodedWebsocketUrl: Uri.encodeComponent(_getWebSocketUrl(server)), - )); + // If a kernel file is given, then use that to launch the test. + File listenerFile; + if (dillFilePath == null) { + final Directory temporaryDirectory = fs.systemTempDirectory + .createTempSync('dart_test_listener'); + finalizers.add(() async { + printTrace('test $ourTestCount: deleting temporary directory'); + temporaryDirectory.deleteSync(recursive: true); + }); + // Prepare the Dart file that will talk to us and start the test. + listenerFile = fs.file( + '${temporaryDirectory.path}/listener.dart'); + listenerFile.createSync(); + listenerFile.writeAsStringSync(_generateTestMain( + testUrl: fs.path.toUri(fs.path.absolute(testPath)).toString(), + encodedWebsocketUrl: Uri.encodeComponent(_getWebSocketUrl(server)), + )); + } // Start the engine subprocess. printTrace('test $ourTestCount: starting shell process${previewDart2? " in preview-dart-2 mode":""}'); - String mainDart = listenerFile.path; + String mainDart = listenerFile?.path ?? testPath; String bundlePath; if (previewDart2) { - final Completer completer = new Completer(); - compilerController.add(new CompilationRequest(listenerFile.path, completer)); - mainDart = await completer.future; + if (dillFilePath == null) { + final Completer completer = new Completer(); + compilerController.add( + new CompilationRequest(listenerFile.path, completer)); + mainDart = await completer.future; - if (mainDart == null) { - controller.sink.addError(_getErrorMessage('Compilation failed', testPath, shellPath)); - return null; + if (mainDart == null) { + controller.sink.addError( + _getErrorMessage('Compilation failed', testPath, shellPath)); + return null; + } + + // bundlePath needs to point to a folder with `platform.dill` file. + final Directory tempBundleDirectory = fs.systemTempDirectory + .createTempSync('flutter_bundle_directory'); + finalizers.add(() async { + printTrace( + 'test $ourTestCount: deleting temporary bundle directory'); + tempBundleDirectory.deleteSync(recursive: true); + }); + + // copy 'vm_platform_strong.dill' into 'platform.dill' + final File vmPlatformStrongDill = fs.file( + artifacts.getArtifactPath(Artifact.platformKernelDill), + ); + final File platformDill = vmPlatformStrongDill.copySync( + tempBundleDirectory + .childFile('platform.dill') + .path, + ); + if (!platformDill.existsSync()) { + printError('unexpected error copying platform kernel file'); + } + + bundlePath = tempBundleDirectory.path; + } else { + mainDart = dillFilePath; + bundlePath = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath); } - - // bundlePath needs to point to a folder with `platform.dill` file. - final Directory tempBundleDirectory = fs.systemTempDirectory - .createTempSync('flutter_bundle_directory'); - finalizers.add(() async { - printTrace('test $ourTestCount: deleting temporary bundle directory'); - tempBundleDirectory.deleteSync(recursive: true); - }); - - // copy 'vm_platform_strong.dill' into 'platform.dill' - final File vmPlatformStrongDill = fs.file( - artifacts.getArtifactPath(Artifact.platformKernelDill), - ); - final File platformDill = vmPlatformStrongDill.copySync( - tempBundleDirectory.childFile('platform.dill').path, - ); - if (!platformDill.existsSync()) { - printError('unexpected error copying platform kernel file'); - } - - bundlePath = tempBundleDirectory.path; } final Process process = await _startProcess(