From a2d349c4c1f31f3dbaf168fb2a636e1a4ff6e5cd Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Thu, 7 Mar 2019 11:02:42 -0800 Subject: [PATCH] select ResidentCompiler during FlutterDevice initialization (#28603) --- .gitignore | 3 ++ dev/integration_tests/codegen/lib/main.dart | 13 +++-- .../codegen/lib/message.spec | 2 +- .../codegen/test/example_test.dart | 11 +++++ .../codegen/test_driver/main_test.dart | 2 +- .../flutter_build/lib/src/kernel_builder.dart | 18 ++----- .../lib/src/build_runner/build_runner.dart | 49 +++++++++++-------- packages/flutter_tools/lib/src/codegen.dart | 41 +++++++++------- .../lib/src/commands/attach.dart | 3 +- .../lib/src/commands/daemon.dart | 3 +- .../lib/src/commands/generate.dart | 4 +- .../flutter_tools/lib/src/commands/run.dart | 17 +++---- .../flutter_tools/lib/src/commands/test.dart | 3 ++ packages/flutter_tools/lib/src/compile.dart | 6 ++- .../lib/src/dart/package_map.dart | 2 + packages/flutter_tools/lib/src/project.dart | 7 +++ .../lib/src/resident_runner.dart | 39 +++++++++++++++ .../lib/src/test/flutter_platform.dart | 23 +++++++-- .../flutter_tools/lib/src/test/runner.dart | 3 ++ .../code_generating_kernel_compiler.dart | 3 +- 20 files changed, 176 insertions(+), 76 deletions(-) create mode 100644 dev/integration_tests/codegen/test/example_test.dart diff --git a/.gitignore b/.gitignore index ce9e65c132..40308abf0a 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,9 @@ /packages/flutter/coverage/ version +# packages file containing multi-root paths +.packages.generated + # Flutter/Dart/Pub related **/doc/api/ .dart_tool/ diff --git a/dev/integration_tests/codegen/lib/main.dart b/dev/integration_tests/codegen/lib/main.dart index 41f755f0f9..556c37d36c 100644 --- a/dev/integration_tests/codegen/lib/main.dart +++ b/dev/integration_tests/codegen/lib/main.dart @@ -17,7 +17,7 @@ class ExampleWidget extends StatefulWidget { } class _ExampleWidgetState extends State { - String _message = ''; + bool _pressed = false; @override Widget build(BuildContext context) { @@ -30,14 +30,21 @@ class _ExampleWidgetState extends State { child: const Text('Press Button, Get Coffee'), onPressed: () async { setState(() { - _message = generated.message; + _pressed = true; }); }, ), - Text(_message), + _pressed ? GeneratedWidget() : const SizedBox(), ], ), ), ); } } + +class GeneratedWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Text(generated.message); + } +} diff --git a/dev/integration_tests/codegen/lib/message.spec b/dev/integration_tests/codegen/lib/message.spec index 13c2e9ead0..4ec81420e9 100644 --- a/dev/integration_tests/codegen/lib/message.spec +++ b/dev/integration_tests/codegen/lib/message.spec @@ -1 +1 @@ -final String message = 'Thanks for using PourOverSupremeFiesta by Coffee by Flutter Inc.'; +String get message => 'Thanks for using PourOverSupremeFiesta by Coffee by Flutter Inc.'; diff --git a/dev/integration_tests/codegen/test/example_test.dart b/dev/integration_tests/codegen/test/example_test.dart new file mode 100644 index 0000000000..6d687eb92d --- /dev/null +++ b/dev/integration_tests/codegen/test/example_test.dart @@ -0,0 +1,11 @@ +import 'package:codegen/main.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('can reference generated code', (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp(home: GeneratedWidget())); + + expect(find.text('Thanks for using PourOverSupremeFiesta by Coffee by Flutter Inc.'), findsOneWidget); + }); +} diff --git a/dev/integration_tests/codegen/test_driver/main_test.dart b/dev/integration_tests/codegen/test_driver/main_test.dart index df5bf2419c..1ae419a188 100644 --- a/dev/integration_tests/codegen/test_driver/main_test.dart +++ b/dev/integration_tests/codegen/test_driver/main_test.dart @@ -24,4 +24,4 @@ void main() { final String fullMessage = await driver.getText(find.text(message)); expect(fullMessage, message); }); -} \ No newline at end of file +} diff --git a/packages/flutter_build/lib/src/kernel_builder.dart b/packages/flutter_build/lib/src/kernel_builder.dart index 47aa614e04..af52be8818 100644 --- a/packages/flutter_build/lib/src/kernel_builder.dart +++ b/packages/flutter_build/lib/src/kernel_builder.dart @@ -89,25 +89,18 @@ class FlutterKernelBuilder implements Builder { return; } final AssetId outputId = buildStep.inputId.changeExtension(_kFlutterDillOutputExtension); - final AssetId packagesOutputId = buildStep.inputId.changeExtension(_kPackagesExtension); // Create a scratch space file that can be read/written by the frontend server. // It is okay to hard-code these file names because we will copy them back // from the temp directory at the end of the build step. final Directory tempDirecory = await Directory.systemTemp.createTemp('_flutter_build'); - final File packagesFile = File(path.join(tempDirecory.path, _kPackagesExtension)); + final Directory projectDir = File(packagesPath).parent; + final String packagesFilePath = path.join(projectDir.path, '.packages.generated'); final File outputFile = File(path.join(tempDirecory.path, 'main.app.dill')); await outputFile.create(); - await packagesFile.create(); - final Directory projectDir = File(packagesPath).parent; final String packageName = buildStep.inputId.package; - final String oldPackagesContents = await File(packagesPath).readAsString(); - // Note: currently we only replace the root package with a multiroot - // scheme. To support codegen on arbitrary packages we will need to do - // this for each dependency. - final String newPackagesContents = oldPackagesContents.replaceFirst('$packageName:lib/', '$packageName:$_kMultirootScheme:/'); - await packagesFile.writeAsString(newPackagesContents); + String absoluteMainPath; if (path.isAbsolute(mainPath)) { absoluteMainPath = mainPath; @@ -143,7 +136,7 @@ class FlutterKernelBuilder implements Builder { final String normalRoot = path.join(projectDir.absolute.path, 'lib${Platform.pathSeparator}'); arguments.addAll([ '--packages', - Uri.file(packagesFile.path).toString(), + packagesFilePath, '--output-dill', outputFile.path, '--filesystem-root', @@ -158,7 +151,7 @@ class FlutterKernelBuilder implements Builder { } final Uri mainUri = _PackageUriMapper.findUri( absoluteMainPath, - packagesFile.path, + packagesFilePath, _kMultirootScheme, [normalRoot, generatedRoot], ); @@ -178,7 +171,6 @@ class FlutterKernelBuilder implements Builder { await server.exitCode; await _stdoutHandler.compilerOutput.future; await buildStep.writeAsBytes(outputId, await outputFile.readAsBytes()); - await buildStep.writeAsBytes(packagesOutputId, await packagesFile.readAsBytes()); } catch (err, stackTrace) { log.shout('frontend server failed to start: $err, $stackTrace'); } diff --git a/packages/flutter_tools/lib/src/build_runner/build_runner.dart b/packages/flutter_tools/lib/src/build_runner/build_runner.dart index f77c5eb29c..37eb0aec79 100644 --- a/packages/flutter_tools/lib/src/build_runner/build_runner.dart +++ b/packages/flutter_tools/lib/src/build_runner/build_runner.dart @@ -22,12 +22,18 @@ import '../base/logger.dart'; import '../base/process_manager.dart'; import '../codegen.dart'; import '../convert.dart'; +import '../dart/package_map.dart'; import '../dart/pub.dart'; import '../globals.dart'; import '../project.dart'; import '../resident_runner.dart'; import 'build_script_generator.dart'; +// Arbitrarily choosen multi-root file scheme. This is used to configure the +// frontend_server to resolve a package uri to multiple filesystem directories. +// In this case, the source directory and a generated directory. +const String _kMultirootScheme = 'org-dartlang-app'; + /// A wrapper for a build_runner process which delegates to a generated /// build script. /// @@ -37,7 +43,7 @@ class BuildRunner extends CodeGenerator { const BuildRunner(); @override - Future build({ + Future build(FlutterProject flutterProject, { @required String mainPath, @required bool aot, @required bool linkPlatformKernelIn, @@ -46,8 +52,7 @@ class BuildRunner extends CodeGenerator { List extraFrontEndOptions = const [], bool disableKernelGeneration = false, }) async { - await generateBuildScript(); - final FlutterProject flutterProject = await FlutterProject.current(); + await generateBuildScript(flutterProject); final String frontendServerPath = artifacts.getArtifactPath( Artifact.frontendServerSnapshotForEngineDartSdk ); @@ -99,7 +104,7 @@ class BuildRunner extends CodeGenerator { status.stop(); } if (disableKernelGeneration) { - return const CodeGenerationResult(null, null); + return const CodeGenerationResult(null); } /// We don't check for this above because it might be generated for the /// first time by invoking the build. @@ -114,21 +119,17 @@ class BuildRunner extends CodeGenerator { throw Exception('build_runner cannot find generated directory'); } final String relativeMain = fs.path.relative(mainPath, from: flutterProject.directory.path); - final File packagesFile = fs.file( - fs.path.join(generatedDirectory.path, fs.path.setExtension(relativeMain, '.packages')) - ); final File dillFile = fs.file( fs.path.join(generatedDirectory.path, fs.path.setExtension(relativeMain, '.app.dill')) ); - if (!packagesFile.existsSync() || !dillFile.existsSync()) { + if (!dillFile.existsSync()) { throw Exception('build_runner did not produce output at expected location: ${dillFile.path} missing'); } - return CodeGenerationResult(packagesFile, dillFile); + return CodeGenerationResult(dillFile); } @override - Future generateBuildScript() async { - final FlutterProject flutterProject = await FlutterProject.current(); + Future generateBuildScript(FlutterProject flutterProject) async { final Directory entrypointDirectory = fs.directory(fs.path.join(flutterProject.dartTool.path, 'build', 'entrypoint')); final Directory generatedDirectory = fs.directory(fs.path.join(flutterProject.dartTool.path, 'flutter_tool')); final File buildScript = entrypointDirectory.childFile('build.dart'); @@ -180,7 +181,6 @@ class BuildRunner extends CodeGenerator { stringBuffer.writeln(' flutter_build:'); stringBuffer.writeln(' sdk: flutter'); syntheticPubspec.writeAsStringSync(stringBuffer.toString()); - await pubGet( context: PubContext.pubGet, directory: generatedDirectory.path, @@ -210,7 +210,7 @@ class BuildRunner extends CodeGenerator { } @override - Future daemon({ + Future daemon(FlutterProject flutterProject, { String mainPath, bool linkPlatformKernelIn = false, bool targetProductVm = false, @@ -218,8 +218,8 @@ class BuildRunner extends CodeGenerator { List extraFrontEndOptions = const [], }) async { mainPath ??= findMainDartFile(); - await generateBuildScript(); - final FlutterProject flutterProject = await FlutterProject.current(); + await generateBuildScript(flutterProject); + _generatePackages(flutterProject); final String frontendServerPath = artifacts.getArtifactPath( Artifact.frontendServerSnapshotForEngineDartSdk ); @@ -265,19 +265,28 @@ class BuildRunner extends CodeGenerator { builder.target = flutterProject.manifest.appName; })); final String relativeMain = fs.path.relative(mainPath, from: flutterProject.directory.path); - final File generatedPackagesFile = fs.file(fs.path.join(flutterProject.generated.path, fs.path.setExtension(relativeMain, '.packages'))); final File generatedDillFile = fs.file(fs.path.join(flutterProject.generated.path, fs.path.setExtension(relativeMain, '.app.dill'))); - return _BuildRunnerCodegenDaemon(buildDaemonClient, generatedPackagesFile, generatedDillFile); + return _BuildRunnerCodegenDaemon(buildDaemonClient, generatedDillFile); + } + + // Create generated packages file which adds a multi-root scheme to the user's + // project directory. Currently we only replace the root package with a multiroot + // scheme. To support codegen on arbitrary packages we would need to do + // this for each dependency. + void _generatePackages(FlutterProject flutterProject) { + final String oldPackagesContents = fs.file(PackageMap.globalPackagesPath).readAsStringSync(); + final String appName = flutterProject.manifest.appName; + final String newPackagesContents = oldPackagesContents.replaceFirst('$appName:lib/', '$appName:$_kMultirootScheme:/'); + final String generatedPackagesPath = fs.path.setExtension(PackageMap.globalPackagesPath, '.generated'); + fs.file(generatedPackagesPath).writeAsStringSync(newPackagesContents); } } class _BuildRunnerCodegenDaemon implements CodegenDaemon { - _BuildRunnerCodegenDaemon(this.buildDaemonClient, this.packagesFile, this.dillFile); + _BuildRunnerCodegenDaemon(this.buildDaemonClient, this.dillFile); final BuildDaemonClient buildDaemonClient; @override - final File packagesFile; - @override final File dillFile; @override CodegenStatus get lastStatus => _lastStatus; diff --git a/packages/flutter_tools/lib/src/codegen.dart b/packages/flutter_tools/lib/src/codegen.dart index 7d521a4f21..11db84af75 100644 --- a/packages/flutter_tools/lib/src/codegen.dart +++ b/packages/flutter_tools/lib/src/codegen.dart @@ -9,6 +9,7 @@ import 'base/context.dart'; import 'base/file_system.dart'; import 'base/platform.dart'; import 'compile.dart'; +import 'dart/package_map.dart'; import 'globals.dart'; import 'project.dart'; @@ -44,8 +45,9 @@ abstract class CodeGenerator { const CodeGenerator(); /// Run a partial build include code generators but not kernel. - Future generate({@required String mainPath}) async { + Future generate(FlutterProject flutterProject, {@required String mainPath}) async { await build( + flutterProject, mainPath: mainPath, aot: false, linkPlatformKernelIn: false, @@ -59,7 +61,7 @@ abstract class CodeGenerator { /// /// The defines of the build command are the arguments required in the /// flutter_build kernel builder. - Future build({ + Future build(FlutterProject flutterProject, { @required String mainPath, @required bool aot, @required bool linkPlatformKernelIn, @@ -73,7 +75,7 @@ abstract class CodeGenerator { /// /// The defines of the daemon command are the arguments required in the /// flutter_build kernel builder. - Future daemon({ + Future daemon(FlutterProject flutterProject, { @required String mainPath, bool linkPlatformKernelIn = false, bool targetProductVm = false, @@ -83,14 +85,14 @@ abstract class CodeGenerator { // Generates a synthetic package under .dart_tool/flutter_tool which is in turn // used to generate a build script. - Future generateBuildScript(); + Future generateBuildScript(FlutterProject flutterProject); } class UnsupportedCodeGenerator extends CodeGenerator { const UnsupportedCodeGenerator(); @override - Future build({ + Future build(FlutterProject flutterProject, { String mainPath, bool aot, bool linkPlatformKernelIn, @@ -103,12 +105,12 @@ class UnsupportedCodeGenerator extends CodeGenerator { } @override - Future generateBuildScript() { + Future generateBuildScript(FlutterProject flutterProject) { throw UnsupportedError('build_runner is not currently supported.'); } @override - Future daemon({ + Future daemon(FlutterProject flutterProject, { String mainPath, bool linkPlatformKernelIn = false, bool targetProductVm = false, @@ -128,8 +130,6 @@ abstract class CodegenDaemon { /// Starts a new build. void startBuild(); - File get packagesFile; - File get dillFile; } @@ -137,9 +137,8 @@ abstract class CodegenDaemon { /// /// If no dill or packages file is generated, they will be null. class CodeGenerationResult { - const CodeGenerationResult(this.packagesFile, this.dillFile); + const CodeGenerationResult(this.dillFile); - final File packagesFile; final File dillFile; } @@ -176,8 +175,10 @@ class CodeGeneratingKernelCompiler implements KernelCompiler { 'sdkRoot, packagesPath are not supported when using the experimental ' 'build* pipeline'); } + final FlutterProject flutterProject = await FlutterProject.current(); try { final CodeGenerationResult buildResult = await codeGenerator.build( + flutterProject, aot: aot, linkPlatformKernelIn: linkPlatformKernelIn, trackWidgetCreation: trackWidgetCreation, @@ -206,13 +207,16 @@ class CodeGeneratingResidentCompiler implements ResidentCompiler { /// Creates a new [ResidentCompiler] and configures a [BuildDaemonClient] to /// run builds. static Future create({ - @required String mainPath, + @required FlutterProject flutterProject, + String mainPath, bool trackWidgetCreation = false, CompilerMessageConsumer compilerMessageConsumer = printError, bool unsafePackageSerialization = false, + String outputPath, + String initializeFromDill, }) async { - final FlutterProject flutterProject = await FlutterProject.current(); final CodegenDaemon codegenDaemon = await codeGenerator.daemon( + flutterProject, extraFrontEndOptions: [], linkPlatformKernelIn: false, mainPath: mainPath, @@ -221,22 +225,23 @@ class CodeGeneratingResidentCompiler implements ResidentCompiler { ); codegenDaemon.startBuild(); final CodegenStatus status = await codegenDaemon.buildResults.firstWhere((CodegenStatus status) { - return status ==CodegenStatus.Succeeded || status == CodegenStatus.Failed; + return status == CodegenStatus.Succeeded || status == CodegenStatus.Failed; }); if (status == CodegenStatus.Failed) { - printError('Codegeneration failed, halting build.'); + printError('Code generation failed, build may have compile errors'); } final ResidentCompiler residentCompiler = ResidentCompiler( artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), trackWidgetCreation: trackWidgetCreation, - packagesPath: codegenDaemon.packagesFile.path, + packagesPath: PackageMap.globalGeneratedPackagesPath, fileSystemRoots: [ - fs.path.join(flutterProject.generated.absolute.path, 'lib${platform.pathSeparator}'), + fs.path.join(flutterProject.generated.path, 'lib${platform.pathSeparator}'), fs.path.join(flutterProject.directory.path, 'lib${platform.pathSeparator}'), ], fileSystemScheme: _kMultiRootScheme, targetModel: TargetModel.flutter, unsafePackageSerialization: unsafePackageSerialization, + initializeFromDill: initializeFromDill, ); return CodeGeneratingResidentCompiler._(residentCompiler, codegenDaemon); } @@ -274,7 +279,7 @@ class CodeGeneratingResidentCompiler implements ResidentCompiler { mainPath, invalidatedFiles, outputPath: outputPath, - packagesFilePath: _codegenDaemon.packagesFile.path, + packagesFilePath: PackageMap.globalGeneratedPackagesPath, ); } diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart index 6f4dc417b4..a71524c9e6 100644 --- a/packages/flutter_tools/lib/src/commands/attach.dart +++ b/packages/flutter_tools/lib/src/commands/attach.dart @@ -205,13 +205,14 @@ class AttachCommand extends FlutterCommand { } try { final bool useHot = getBuildInfo().isDebug; - final FlutterDevice flutterDevice = FlutterDevice( + final FlutterDevice flutterDevice = await FlutterDevice.create( device, trackWidgetCreation: false, dillOutputPath: argResults['output-dill'], fileSystemRoots: argResults['filesystem-root'], fileSystemScheme: argResults['filesystem-scheme'], viewFilter: argResults['isolate-filter'], + target: argResults['target'], targetModel: TargetModel(argResults['target-model']), ); flutterDevice.observatoryUris = [ observatoryUri ]; diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index d6364b3349..9d954929a4 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -346,11 +346,12 @@ class AppDomain extends Domain { final Directory cwd = fs.currentDirectory; fs.currentDirectory = fs.directory(projectDirectory); - final FlutterDevice flutterDevice = FlutterDevice( + final FlutterDevice flutterDevice = await FlutterDevice.create( device, trackWidgetCreation: trackWidgetCreation, dillOutputPath: dillOutputPath, viewFilter: isolateFilter, + target: target, ); ResidentRunner runner; diff --git a/packages/flutter_tools/lib/src/commands/generate.dart b/packages/flutter_tools/lib/src/commands/generate.dart index 2b0ed3c9ea..d773e90849 100644 --- a/packages/flutter_tools/lib/src/commands/generate.dart +++ b/packages/flutter_tools/lib/src/commands/generate.dart @@ -4,6 +4,7 @@ import '../base/common.dart'; import '../codegen.dart'; +import '../project.dart'; import '../runner/flutter_command.dart'; class GenerateCommand extends FlutterCommand { @@ -24,7 +25,8 @@ class GenerateCommand extends FlutterCommand { if (!experimentalBuildEnabled) { throwToolExit('FLUTTER_EXPERIMENTAL_BUILD is not enabled, codegen is unsupported.'); } - await codeGenerator.generate(mainPath: argResults['target']); + final FlutterProject flutterProject = await FlutterProject.current(); + await codeGenerator.generate(flutterProject, mainPath: argResults['target']); return null; } } diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index c79a8a83c4..b0634902de 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -10,8 +10,6 @@ import '../base/time.dart'; import '../base/utils.dart'; import '../build_info.dart'; import '../cache.dart'; -import '../codegen.dart'; -import '../compile.dart'; import '../device.dart'; import '../globals.dart'; import '../ios/mac.dart'; @@ -346,13 +344,9 @@ class RunCommand extends RunCommandBase { argResults[FlutterOptions.kEnableExperiment].isNotEmpty) { expFlags = argResults[FlutterOptions.kEnableExperiment]; } - - ResidentCompiler residentCompiler; - if (experimentalBuildEnabled) { - residentCompiler = await CodeGeneratingResidentCompiler.create(mainPath: argResults['target']); - } - final List flutterDevices = devices.map((Device device) { - return FlutterDevice( + final List flutterDevices = []; + for (Device device in devices) { + final FlutterDevice flutterDevice = await FlutterDevice.create( device, trackWidgetCreation: argResults['track-widget-creation'], dillOutputPath: argResults['output-dill'], @@ -360,9 +354,10 @@ class RunCommand extends RunCommandBase { fileSystemScheme: argResults['filesystem-scheme'], viewFilter: argResults['isolate-filter'], experimentalFlags: expFlags, - generator: residentCompiler, + target: argResults['target'], ); - }).toList(); + flutterDevices.add(flutterDevice); + } ResidentRunner runner; final String applicationBinaryPath = argResults['use-application-binary']; diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index b39a2210f8..4779e4210b 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -9,6 +9,7 @@ import '../base/common.dart'; import '../base/file_system.dart'; import '../base/platform.dart'; import '../cache.dart'; +import '../project.dart'; import '../runner/flutter_command.dart'; import '../test/coverage_collector.dart'; import '../test/event_printer.dart'; @@ -104,6 +105,7 @@ class TestCommand extends FlutterCommand { Future runCommand() async { final List names = argResults['name']; final List plainNames = argResults['plain-name']; + final FlutterProject flutterProject = await FlutterProject.current(); Iterable files = argResults.rest.map((String testPath) => fs.path.absolute(testPath)).toList(); @@ -170,6 +172,7 @@ class TestCommand extends FlutterCommand { trackWidgetCreation: argResults['track-widget-creation'], updateGoldens: argResults['update-goldens'], concurrency: jobs, + flutterProject: flutterProject, ); if (collector != null) { diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index dd255f53aa..86d895dfc5 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -661,7 +661,11 @@ class ResidentCompiler { return null; } - Future shutdown() { + Future shutdown() async { + // Server was never sucessfully created. + if (_server == null) { + return 0; + } _server.kill(); return _server.exitCode; } diff --git a/packages/flutter_tools/lib/src/dart/package_map.dart b/packages/flutter_tools/lib/src/dart/package_map.dart index 6d940128b3..fafb4e8463 100644 --- a/packages/flutter_tools/lib/src/dart/package_map.dart +++ b/packages/flutter_tools/lib/src/dart/package_map.dart @@ -20,6 +20,8 @@ class PackageMap { static String get globalPackagesPath => _globalPackagesPath ?? kPackagesFileName; + static String get globalGeneratedPackagesPath => fs.path.setExtension(globalPackagesPath, '.generated'); + static set globalPackagesPath(String value) { _globalPackagesPath = value; } diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index f14e2a6431..26a6b2b056 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -113,6 +113,7 @@ class FlutterProject { /// The directory containing the generated code for this project. Directory get generated => directory + .absolute .childDirectory('.dart_tool') .childDirectory('build') .childDirectory('generated') @@ -165,6 +166,12 @@ class FlutterProject { final YamlMap pubspec = loadYaml(await pubspecFile.readAsString()); return pubspec['builders']; } + + /// Whether there are any builders used by this package. + Future get hasBuilders async { + final YamlMap result = await builders; + return result != null && result.isNotEmpty; + } } /// Represents the iOS sub-project of a Flutter project. diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 982ca22f9e..a03791bb9a 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -49,6 +49,45 @@ class FlutterDevice { experimentalFlags: experimentalFlags, ); + /// Create a [FlutterDevice] with optional code generation enabled. + static Future create(Device device, { + @required bool trackWidgetCreation, + String dillOutputPath, + List fileSystemRoots, + String fileSystemScheme, + String viewFilter, + @required String target, + TargetModel targetModel = TargetModel.flutter, + List experimentalFlags, + ResidentCompiler generator, + }) async { + ResidentCompiler generator; + final FlutterProject flutterProject = await FlutterProject.current(); + if (experimentalBuildEnabled && await flutterProject.hasBuilders) { + generator = await CodeGeneratingResidentCompiler.create(flutterProject: flutterProject, mainPath: target); + } else { + generator = ResidentCompiler( + artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), + trackWidgetCreation: trackWidgetCreation, + fileSystemRoots: fileSystemRoots, + fileSystemScheme: fileSystemScheme, + targetModel: targetModel, + experimentalFlags: experimentalFlags, + ); + } + return FlutterDevice( + device, + trackWidgetCreation: trackWidgetCreation, + dillOutputPath: dillOutputPath, + fileSystemRoots: fileSystemRoots, + fileSystemScheme:fileSystemScheme, + viewFilter: viewFilter, + experimentalFlags: experimentalFlags, + targetModel: targetModel, + generator: generator, + ); + } + final Device device; final ResidentCompiler generator; List observatoryUris; diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index a17c008eb2..0bbe735a56 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -24,10 +24,12 @@ import '../base/process_manager.dart'; import '../base/terminal.dart'; import '../build_info.dart'; import '../bundle.dart'; +import '../codegen.dart'; import '../compile.dart'; import '../convert.dart'; import '../dart/package_map.dart'; import '../globals.dart'; +import '../project.dart'; import '../vmservice.dart'; import 'watcher.dart'; @@ -89,6 +91,7 @@ void installHook({ int observatoryPort, InternetAddressType serverType = InternetAddressType.IPv4, Uri projectRootDirectory, + FlutterProject flutterProject, }) { assert(enableObservatory || (!startPaused && observatoryPort == null)); hack.registerPlatformPlugin( @@ -107,6 +110,7 @@ void installHook({ trackWidgetCreation: trackWidgetCreation, updateGoldens: updateGoldens, projectRootDirectory: projectRootDirectory, + flutterProject: flutterProject, ), ); } @@ -232,7 +236,7 @@ class _CompilationRequest { // This class is a wrapper around compiler that allows multiple isolates to // enqueue compilation requests, but ensures only one compilation at a time. class _Compiler { - _Compiler(bool trackWidgetCreation, Uri projectRootDirectory) { + _Compiler(bool trackWidgetCreation, Uri projectRootDirectory, FlutterProject flutterProject) { // Compiler maintains and updates single incremental dill file. // Incremental compilation requests done for each test copy that file away // for independent execution. @@ -265,7 +269,16 @@ class _Compiler { trackWidgetCreation: trackWidgetCreation, ); - ResidentCompiler createCompiler() { + Future createCompiler() async { + if (experimentalBuildEnabled && await flutterProject.hasBuilders) { + return CodeGeneratingResidentCompiler.create( + flutterProject: flutterProject, + trackWidgetCreation: trackWidgetCreation, + initializeFromDill: null, // TODO(jonahwilliams): investigate multi-root support in init from dill. + unsafePackageSerialization: false, + compilerMessageConsumer: reportCompilerMessage, + ); + } return ResidentCompiler( artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), packagesPath: PackageMap.globalPackagesPath, @@ -290,7 +303,7 @@ class _Compiler { final Stopwatch compilerTime = Stopwatch()..start(); bool firstCompile = false; if (compiler == null) { - compiler = createCompiler(); + compiler = await createCompiler(); firstCompile = true; } suppressOutput = false; @@ -374,6 +387,7 @@ class _FlutterPlatform extends PlatformPlugin { this.trackWidgetCreation, this.updateGoldens, this.projectRootDirectory, + this.flutterProject, }) : assert(shellPath != null); final String shellPath; @@ -389,6 +403,7 @@ class _FlutterPlatform extends PlatformPlugin { final bool trackWidgetCreation; final bool updateGoldens; final Uri projectRootDirectory; + final FlutterProject flutterProject; Directory fontsDirectory; _Compiler compiler; @@ -545,7 +560,7 @@ class _FlutterPlatform extends PlatformPlugin { if (precompiledDillPath == null && precompiledDillFiles == null) { // Lazily instantiate compiler so it is built only if it is actually used. - compiler ??= _Compiler(trackWidgetCreation, projectRootDirectory); + compiler ??= _Compiler(trackWidgetCreation, projectRootDirectory, flutterProject); mainDart = await compiler.compile(mainDart); if (mainDart == null) { diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart index 633b9b34b4..dfcb81af68 100644 --- a/packages/flutter_tools/lib/src/test/runner.dart +++ b/packages/flutter_tools/lib/src/test/runner.dart @@ -15,6 +15,7 @@ import '../base/process_manager.dart'; import '../base/terminal.dart'; import '../dart/package_map.dart'; import '../globals.dart'; +import '../project.dart'; import 'flutter_platform.dart' as loader; import 'watcher.dart'; @@ -34,6 +35,7 @@ Future runTests( bool updateGoldens = false, TestWatcher watcher, @required int concurrency, + FlutterProject flutterProject, }) async { // Compute the command-line arguments for package:test. final List testArgs = []; @@ -80,6 +82,7 @@ Future runTests( trackWidgetCreation: trackWidgetCreation, updateGoldens: updateGoldens, projectRootDirectory: fs.currentDirectory.uri, + flutterProject: flutterProject, ); // Make the global packages path absolute. diff --git a/packages/flutter_tools/test/build_runner/code_generating_kernel_compiler.dart b/packages/flutter_tools/test/build_runner/code_generating_kernel_compiler.dart index c5381a286f..3652ec0c9d 100644 --- a/packages/flutter_tools/test/build_runner/code_generating_kernel_compiler.dart +++ b/packages/flutter_tools/test/build_runner/code_generating_kernel_compiler.dart @@ -30,6 +30,7 @@ void main() { testUsingContext('delegates to build_runner', () async { const CodeGeneratingKernelCompiler kernelCompiler = CodeGeneratingKernelCompiler(); when(mockBuildRunner.build( + any, aot: anyNamed('aot'), extraFrontEndOptions: anyNamed('extraFrontEndOptions'), linkPlatformKernelIn: anyNamed('linkPlatformKernelIn'), @@ -37,7 +38,7 @@ void main() { targetProductVm: anyNamed('targetProductVm'), trackWidgetCreation: anyNamed('trackWidgetCreation'), )).thenAnswer((Invocation invocation) async { - return CodeGenerationResult(fs.file('.packages'), fs.file('main.app.dill')); + return CodeGenerationResult(fs.file('main.app.dill')); }); final CompilerOutput buildResult = await kernelCompiler.compile( outputFilePath: 'output.app.dill',