Add basic codegen app to be used for integration testing and benchmarks (#27257)
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 160cfc3..1415fa5 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart
@@ -5,6 +5,12 @@ import 'dart:async'; import 'runner.dart' as runner; +import 'src/base/context.dart'; +// The build_runner code generation is provided here to make it easier to +// avoid introducing the dependency into google3. Not all build* packages +// are synced internally. +import 'src/build_runner/build_runner.dart'; +import 'src/codegen.dart'; import 'src/commands/analyze.dart'; import 'src/commands/attach.dart'; import 'src/commands/build.dart'; @@ -18,6 +24,7 @@ import 'src/commands/drive.dart'; import 'src/commands/emulators.dart'; import 'src/commands/format.dart'; +import 'src/commands/generate.dart'; import 'src/commands/ide_config.dart'; import 'src/commands/inject_plugins.dart'; import 'src/commands/install.dart'; @@ -63,6 +70,7 @@ DriveCommand(), EmulatorsCommand(), FormatCommand(), + GenerateCommand(), IdeConfigCommand(hidden: !verboseHelp), InjectPluginsCommand(hidden: !verboseHelp), InstallCommand(), @@ -81,5 +89,10 @@ VersionCommand(), ], verbose: verbose, muteCommandLogging: muteCommandLogging, - verboseHelp: verboseHelp); + verboseHelp: verboseHelp, + overrides: <Type, Generator>{ + // The build runner instance is not supported in google3 because + // the build runner packages are not synced internally. + CodeGenerator: () => experimentalBuildEnabled ? const BuildRunner() : const UnsupportedCodeGenerator(), + }); }
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index a62a353..7be3b8c 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -302,7 +302,6 @@ printTrace('Extra front-end options: $extraFrontEndOptions'); final String depfilePath = fs.path.join(outputPath, 'kernel_compile.d'); - final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(); final CompilerOutput compilerOutput = await kernelCompiler.compile( sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), mainPath: mainPath,
diff --git a/packages/flutter_tools/lib/src/build_runner/build_kernel_compiler.dart b/packages/flutter_tools/lib/src/build_runner/build_kernel_compiler.dart deleted file mode 100644 index d60bc8c..0000000 --- a/packages/flutter_tools/lib/src/build_runner/build_kernel_compiler.dart +++ /dev/null
@@ -1,64 +0,0 @@ -// Copyright 2019 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 '../base/file_system.dart'; -import '../compile.dart'; -import '../globals.dart'; -import 'build_runner.dart'; - -/// An implementation of the [KernelCompiler] which delegates to build_runner. -/// -/// Only a subset of the arguments provided to the [KernelCompiler] are -/// supported here. Using the build pipeline implies a fixed multiroot -/// filesystem and requires a pubspec. -/// -/// This is only safe to use if [experimentalBuildEnabled] is true. -class BuildKernelCompiler implements KernelCompiler { - const BuildKernelCompiler(); - - @override - Future<CompilerOutput> compile({ - String mainPath, - String outputFilePath, - bool linkPlatformKernelIn = false, - bool aot = false, - bool trackWidgetCreation, - List<String> extraFrontEndOptions, - String incrementalCompilerByteStorePath, - bool targetProductVm = false, - // These arguments are currently unused. - String sdkRoot, - String packagesPath, - List<String> fileSystemRoots, - String fileSystemScheme, - String depFilePath, - TargetModel targetModel = TargetModel.flutter, - }) async { - if (fileSystemRoots != null || fileSystemScheme != null || depFilePath != null || targetModel != null || sdkRoot != null || packagesPath != null) { - printTrace('fileSystemRoots, fileSystemScheme, depFilePath, targetModel,' - 'sdkRoot, packagesPath are not supported when using the experimental ' - 'build* pipeline'); - } - final BuildRunner buildRunner = buildRunnerFactory.create(); - try { - final BuildResult buildResult = await buildRunner.build( - aot: aot, - linkPlatformKernelIn: linkPlatformKernelIn, - trackWidgetCreation: trackWidgetCreation, - mainPath: mainPath, - targetProductVm: targetProductVm, - extraFrontEndOptions: extraFrontEndOptions - ); - final File outputFile = fs.file(outputFilePath); - if (!await outputFile.exists()) { - await outputFile.create(); - } - await outputFile.writeAsBytes(await buildResult.dillFile.readAsBytes()); - return CompilerOutput(outputFilePath, 0); - } on Exception catch (err) { - printError('Compilation Failed: $err'); - return const CompilerOutput(null, 1); - } - } -}
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 68591c0..90f660f 100644 --- a/packages/flutter_tools/lib/src/build_runner/build_runner.dart +++ b/packages/flutter_tools/lib/src/build_runner/build_runner.dart
@@ -4,69 +4,45 @@ import 'dart:async'; +import 'package:build_daemon/data/build_target.dart'; import 'package:build_runner_core/build_runner_core.dart'; +import 'package:build_daemon/data/server_log.dart'; +import 'package:build_daemon/data/build_status.dart' as build; +import 'package:build_daemon/client.dart'; import 'package:meta/meta.dart'; +import 'package:yaml/yaml.dart'; import '../artifacts.dart'; -import '../base/context.dart'; import '../base/file_system.dart'; import '../base/io.dart'; import '../base/logger.dart'; -import '../base/platform.dart'; import '../base/process_manager.dart'; import '../cache.dart'; +import '../codegen.dart'; import '../convert.dart'; import '../dart/pub.dart'; import '../globals.dart'; import '../project.dart'; +import '../resident_runner.dart'; import 'build_script_generator.dart'; -/// The [BuildRunnerFactory] instance. -BuildRunnerFactory get buildRunnerFactory => context[BuildRunnerFactory]; - -/// Whether to attempt to build a flutter project using build* libraries. -/// -/// This requires both an experimental opt in via the environment variable -/// 'FLUTTER_EXPERIMENTAL_BUILD' and that the project itself has a -/// dependency on the package 'flutter_build' and 'build_runner.' -bool get experimentalBuildEnabled { - return _experimentalBuildEnabled ??= platform.environment['FLUTTER_EXPERIMENTAL_BUILD']?.toLowerCase() == 'true'; -} -bool _experimentalBuildEnabled; - -@visibleForTesting -set experimentalBuildEnabled(bool value) { - _experimentalBuildEnabled = value; -} - -/// An injectable factory to create instances of [BuildRunner]. -class BuildRunnerFactory { - const BuildRunnerFactory(); - - /// Creates a new [BuildRunner] instance. - BuildRunner create() { - return BuildRunner(); - } -} - /// A wrapper for a build_runner process which delegates to a generated /// build script. /// /// This is only enabled if [experimentalBuildEnabled] is true, and only for /// external flutter users. -class BuildRunner { +class BuildRunner extends CodeGenerator { + const BuildRunner(); - /// Run a build_runner build and return the resulting .packages and dill file. - /// - /// The defines of the build command are the arguments required in the - /// flutter_build kernel builder. - Future<BuildResult> build({ + @override + Future<CodeGenerationResult> build({ + @required String mainPath, @required bool aot, @required bool linkPlatformKernelIn, @required bool trackWidgetCreation, @required bool targetProductVm, - @required String mainPath, - @required List<String> extraFrontEndOptions, + List<String> extraFrontEndOptions = const <String>[], + bool disableKernelGeneration = false, }) async { await generateBuildScript(); final FlutterProject flutterProject = await FlutterProject.current(); @@ -95,7 +71,7 @@ '--packages=$scriptPackagesPath', buildScript, 'build', - '--define', 'flutter_build|kernel=disabled=false', + '--define', 'flutter_build|kernel=disabled=$disableKernelGeneration', '--define', 'flutter_build|kernel=aot=$aot', '--define', 'flutter_build|kernel=linkPlatformKernelIn=$linkPlatformKernelIn', '--define', 'flutter_build|kernel=trackWidgetCreation=$trackWidgetCreation', @@ -121,6 +97,9 @@ } finally { status.stop(); } + if (disableKernelGeneration) { + return const CodeGenerationResult(null, null); + } /// We don't check for this above because it might be generated for the /// first time by invoking the build. final Directory dartTool = flutterProject.dartTool; @@ -143,13 +122,10 @@ if (!packagesFile.existsSync() || !dillFile.existsSync()) { throw Exception('build_runner did not produce output at expected location: ${dillFile.path} missing'); } - return BuildResult(packagesFile, dillFile); + return CodeGenerationResult(packagesFile, dillFile); } - /// Invalidates a generated build script by deleting it. - /// - /// Must be called any time a pubspec file update triggers a corresponding change - /// in .packages. + @override Future<void> invalidateBuildScript() async { final FlutterProject flutterProject = await FlutterProject.current(); final File buildScript = flutterProject.dartTool @@ -162,8 +138,7 @@ await buildScript.delete(); } - // Generates a synthetic package under .dart_tool/flutter_tool which is in turn - // used to generate a build script. + @override Future<void> generateBuildScript() async { final FlutterProject flutterProject = await FlutterProject.current(); final String generatedDirectory = fs.path.join(flutterProject.dartTool.path, 'flutter_tool'); @@ -180,8 +155,10 @@ stringBuffer.writeln('name: synthetic_example'); stringBuffer.writeln('dependencies:'); - for (String builder in await flutterProject.builders) { - stringBuffer.writeln(' $builder: any'); + final YamlMap builders = await flutterProject.builders; + for (String name in builders.keys) { + final YamlNode node = builders[name]; + stringBuffer.writeln(' $name: $node'); } stringBuffer.writeln(' build_runner: any'); stringBuffer.writeln(' flutter_build:'); @@ -195,17 +172,92 @@ checkLastModified: false, ); final PackageGraph packageGraph = PackageGraph.forPath(syntheticPubspec.parent.path); - final BuildScriptGenerator buildScriptGenerator = buildScriptGeneratorFactory.create(flutterProject, packageGraph); + final BuildScriptGenerator buildScriptGenerator = const BuildScriptGeneratorFactory().create(flutterProject, packageGraph); await buildScriptGenerator.generateBuildScript(); } finally { status.stop(); } } + + @override + Future<CodegenDaemon> daemon({ + String mainPath, + bool linkPlatformKernelIn = false, + bool targetProductVm = false, + bool trackWidgetCreation = false, + List<String> extraFrontEndOptions = const <String> [], + }) async { + mainPath ??= findMainDartFile(); + await generateBuildScript(); + final FlutterProject flutterProject = await FlutterProject.current(); + final String frontendServerPath = artifacts.getArtifactPath( + Artifact.frontendServerSnapshotForEngineDartSdk + ); + final String sdkRoot = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath); + final String engineDartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary); + final String packagesPath = flutterProject.packagesFile.absolute.path; + final String buildScript = flutterProject + .dartTool + .childDirectory('build') + .childDirectory('entrypoint') + .childFile('build.dart') + .path; + final String scriptPackagesPath = flutterProject + .dartTool + .childDirectory('flutter_tool') + .childFile('.packages') + .path; + final String dartPath = fs.path.join(Cache.flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', 'dart'); + final Status status = logger.startProgress('starting build daemon...', timeout: null); + BuildDaemonClient buildDaemonClient; + try { + final List<String> command = <String>[ + dartPath, + '--packages=$scriptPackagesPath', + buildScript, + 'daemon', + '--define', 'flutter_build|kernel=disabled=false', + '--define', 'flutter_build|kernel=aot=false', + '--define', 'flutter_build|kernel=linkPlatformKernelIn=$linkPlatformKernelIn', + '--define', 'flutter_build|kernel=trackWidgetCreation=$trackWidgetCreation', + '--define', 'flutter_build|kernel=targetProductVm=$targetProductVm', + '--define', 'flutter_build|kernel=mainPath=$mainPath', + '--define', 'flutter_build|kernel=packagesPath=$packagesPath', + '--define', 'flutter_build|kernel=sdkRoot=$sdkRoot', + '--define', 'flutter_build|kernel=frontendServerPath=$frontendServerPath', + '--define', 'flutter_build|kernel=engineDartBinaryPath=$engineDartBinaryPath', + '--define', 'flutter_build|kernel=extraFrontEndOptions=${extraFrontEndOptions ?? const <String>[]}', + ]; + buildDaemonClient = await BuildDaemonClient.connect(flutterProject.directory.path, command, logHandler: (ServerLog log) => printTrace(log.toString())); + } finally { + status.stop(); + } + buildDaemonClient.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder builder) { + 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); + } } -class BuildResult { - const BuildResult(this.packagesFile, this.dillFile); +class _BuildRunnerCodegenDaemon implements CodegenDaemon { + _BuildRunnerCodegenDaemon(this.buildDaemonClient, this.packagesFile, this.dillFile); + final BuildDaemonClient buildDaemonClient; + @override final File packagesFile; + @override final File dillFile; + + @override + Stream<bool> get buildResults => buildDaemonClient.buildResults.map((build.BuildResults results) { + return results.results.first.status == build.BuildStatus.succeeded; + }); + + @override + void startBuild() { + buildDaemonClient.startBuild(); + } }
diff --git a/packages/flutter_tools/lib/src/build_runner/build_script_generator.dart b/packages/flutter_tools/lib/src/build_runner/build_script_generator.dart index 1467872..cb951ca 100644 --- a/packages/flutter_tools/lib/src/build_runner/build_script_generator.dart +++ b/packages/flutter_tools/lib/src/build_runner/build_script_generator.dart
@@ -12,12 +12,9 @@ import 'package:graphs/graphs.dart'; import '../base/common.dart'; -import '../base/context.dart'; import '../base/file_system.dart'; import '../project.dart'; -BuildScriptGeneratorFactory get buildScriptGeneratorFactory => context[BuildScriptGeneratorFactory]; - class BuildScriptGeneratorFactory { const BuildScriptGeneratorFactory(); @@ -227,14 +224,16 @@ return refer('toNoneByDefault', 'package:build_runner_core/build_runner_core.dart') .call(<Expression>[]); - case AutoApply.dependents: - return refer('toDependentsOf', - 'package:build_runner_core/build_runner_core.dart') - .call(<Expression>[literalString(definition.package)]); + // TODO(jonahwilliams): re-enabled when we have the builders strategy fleshed out. + // case AutoApply.dependents: + // return refer('toDependentsOf', + // 'package:build_runner_core/build_runner_core.dart') + // .call(<Expression>[literalString(definition.package)]); case AutoApply.allPackages: return refer('toAllPackages', 'package:build_runner_core/build_runner_core.dart') .call(<Expression>[]); + case AutoApply.dependents: case AutoApply.rootPackage: return refer('toRoot', 'package:build_runner_core/build_runner_core.dart') .call(<Expression>[]);
diff --git a/packages/flutter_tools/lib/src/bundle.dart b/packages/flutter_tools/lib/src/bundle.dart index 856d219..2d810a8 100644 --- a/packages/flutter_tools/lib/src/bundle.dart +++ b/packages/flutter_tools/lib/src/bundle.dart
@@ -99,7 +99,6 @@ if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty) printTrace('Extra front-end options: $extraFrontEndOptions'); ensureDirectoryExists(applicationKernelFilePath); - final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(); final CompilerOutput compilerOutput = await kernelCompiler.compile( sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), incrementalCompilerByteStorePath: compilationTraceFilePath != null ? null :
diff --git a/packages/flutter_tools/lib/src/codegen.dart b/packages/flutter_tools/lib/src/codegen.dart new file mode 100644 index 0000000..c0b32e6 --- /dev/null +++ b/packages/flutter_tools/lib/src/codegen.dart
@@ -0,0 +1,293 @@ +// Copyright 2019 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 'package:meta/meta.dart'; + +import 'artifacts.dart'; +import 'base/context.dart'; +import 'base/file_system.dart'; +import 'base/platform.dart'; +import 'compile.dart'; +import 'globals.dart'; +import 'project.dart'; + +const String _kMultiRootScheme = 'org-dartlang-app'; + +/// The [CodeGenerator] instance. +/// +/// If [experimentalBuildEnabled] is false, this will contain an unsupported +/// implementation. +CodeGenerator get codeGenerator => context[CodeGenerator]; + +/// Whether to attempt to build a flutter project using build* libraries. +/// +/// This requires both an experimental opt in via the environment variable +/// 'FLUTTER_EXPERIMENTAL_BUILD' and that the project itself has a +/// dependency on the package 'flutter_build' and 'build_runner.' +bool get experimentalBuildEnabled { + return _experimentalBuildEnabled ??= platform.environment['FLUTTER_EXPERIMENTAL_BUILD']?.toLowerCase() == 'true'; +} +bool _experimentalBuildEnabled; + +@visibleForTesting +set experimentalBuildEnabled(bool value) { + _experimentalBuildEnabled = value; +} + +/// A wrapper for a build_runner process which delegates to a generated +/// build script. +/// +/// This is only enabled if [experimentalBuildEnabled] is true, and only for +/// external flutter users. +abstract class CodeGenerator { + const CodeGenerator(); + + /// Run a partial build include code generators but not kernel. + Future<void> generate({@required String mainPath}) async { + await build( + mainPath: mainPath, + aot: false, + linkPlatformKernelIn: false, + trackWidgetCreation: false, + targetProductVm: false, + disableKernelGeneration: true, + ); + } + + /// Run a full build and return the resulting .packages and dill file. + /// + /// The defines of the build command are the arguments required in the + /// flutter_build kernel builder. + Future<CodeGenerationResult> build({ + @required String mainPath, + @required bool aot, + @required bool linkPlatformKernelIn, + @required bool trackWidgetCreation, + @required bool targetProductVm, + List<String> extraFrontEndOptions = const <String>[], + bool disableKernelGeneration = false, + }); + + /// Starts a persistent code generting daemon. + /// + /// The defines of the daemon command are the arguments required in the + /// flutter_build kernel builder. + Future<CodegenDaemon> daemon({ + @required String mainPath, + bool linkPlatformKernelIn = false, + bool targetProductVm = false, + bool trackWidgetCreation = false, + List<String> extraFrontEndOptions = const <String>[], + }); + + /// Invalidates a generated build script by deleting it. + /// + /// Must be called any time a pubspec file update triggers a corresponding change + /// in .packages. + Future<void> invalidateBuildScript(); + + // Generates a synthetic package under .dart_tool/flutter_tool which is in turn + // used to generate a build script. + Future<void> generateBuildScript(); +} + +class UnsupportedCodeGenerator extends CodeGenerator { + const UnsupportedCodeGenerator(); + + @override + Future<CodeGenerationResult> build({ + String mainPath, + bool aot, + bool linkPlatformKernelIn, + bool trackWidgetCreation, + bool targetProductVm, + List<String> extraFrontEndOptions = const <String> [], + bool disableKernelGeneration = false, + }) { + throw UnsupportedError('build_runner is not currently supported.'); + } + + @override + Future<void> generateBuildScript() { + throw UnsupportedError('build_runner is not currently supported.'); + } + + @override + Future<void> invalidateBuildScript() { + throw UnsupportedError('build_runner is not currently supported.'); + } + + @override + Future<CodegenDaemon> daemon({ + String mainPath, + bool linkPlatformKernelIn = false, + bool targetProductVm = false, + bool trackWidgetCreation = false, + List<String> extraFrontEndOptions = const <String> [], + }) { + throw UnsupportedError('build_runner is not currently supported.'); + } +} + +abstract class CodegenDaemon { + /// Whether the previously enqueued build was successful. + Stream<bool> get buildResults; + + /// Starts a new build. + void startBuild(); + + File get packagesFile; + + File get dillFile; +} + +/// The result of running a build through a [CodeGenerator]. +/// +/// If no dill or packages file is generated, they will be null. +class CodeGenerationResult { + const CodeGenerationResult(this.packagesFile, this.dillFile); + + final File packagesFile; + final File dillFile; +} + +/// An implementation of the [KernelCompiler] which delegates to build_runner. +/// +/// Only a subset of the arguments provided to the [KernelCompiler] are +/// supported here. Using the build pipeline implies a fixed multiroot +/// filesystem and requires a pubspec. +/// +/// This is only safe to use if [experimentalBuildEnabled] is true. +class CodeGeneratingKernelCompiler implements KernelCompiler { + const CodeGeneratingKernelCompiler(); + + @override + Future<CompilerOutput> compile({ + String mainPath, + String outputFilePath, + bool linkPlatformKernelIn = false, + bool aot = false, + bool trackWidgetCreation, + List<String> extraFrontEndOptions, + String incrementalCompilerByteStorePath, + bool targetProductVm = false, + // These arguments are currently unused. + String sdkRoot, + String packagesPath, + List<String> fileSystemRoots, + String fileSystemScheme, + String depFilePath, + TargetModel targetModel = TargetModel.flutter, + }) async { + if (fileSystemRoots != null || fileSystemScheme != null || depFilePath != null || targetModel != null || sdkRoot != null || packagesPath != null) { + printTrace('fileSystemRoots, fileSystemScheme, depFilePath, targetModel,' + 'sdkRoot, packagesPath are not supported when using the experimental ' + 'build* pipeline'); + } + try { + final CodeGenerationResult buildResult = await codeGenerator.build( + aot: aot, + linkPlatformKernelIn: linkPlatformKernelIn, + trackWidgetCreation: trackWidgetCreation, + mainPath: mainPath, + targetProductVm: targetProductVm, + extraFrontEndOptions: extraFrontEndOptions + ); + final File outputFile = fs.file(outputFilePath); + if (!await outputFile.exists()) { + await outputFile.create(); + } + await outputFile.writeAsBytes(await buildResult.dillFile.readAsBytes()); + return CompilerOutput(outputFilePath, 0); + } on Exception catch (err) { + printError('Compilation Failed: $err'); + return const CompilerOutput(null, 1); + } + } +} + +/// An implementation of a [ResidentCompiler] which runs a [BuildRunner] before +/// talking to the CFE. +class CodeGeneratingResidentCompiler implements ResidentCompiler { + CodeGeneratingResidentCompiler._(this._residentCompiler, this._codegenDaemon); + + /// Creates a new [ResidentCompiler] and configures a [BuildDaemonClient] to + /// run builds. + static Future<CodeGeneratingResidentCompiler> create({ + @required String mainPath, + bool trackWidgetCreation = false, + CompilerMessageConsumer compilerMessageConsumer = printError, + bool unsafePackageSerialization = false, + }) async { + final FlutterProject flutterProject = await FlutterProject.current(); + final CodegenDaemon codegenDaemon = await codeGenerator.daemon( + extraFrontEndOptions: <String>[], + linkPlatformKernelIn: false, + mainPath: mainPath, + targetProductVm: false, + trackWidgetCreation: trackWidgetCreation, + ); + codegenDaemon.startBuild(); + await codegenDaemon.buildResults.firstWhere((bool result) => result); + final ResidentCompiler residentCompiler = ResidentCompiler( + artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), + trackWidgetCreation: trackWidgetCreation, + packagesPath: codegenDaemon.packagesFile.path, + fileSystemRoots: <String>[ + flutterProject.generated.absolute.path, + flutterProject.directory.path, + ], + fileSystemScheme: _kMultiRootScheme, + targetModel: TargetModel.flutter, + unsafePackageSerialization: unsafePackageSerialization, + ); + return CodeGeneratingResidentCompiler._(residentCompiler, codegenDaemon); + } + + final ResidentCompiler _residentCompiler; + final CodegenDaemon _codegenDaemon; + + @override + void accept() { + _residentCompiler.accept(); + } + + @override + Future<CompilerOutput> compileExpression(String expression, List<String> definitions, List<String> typeDefinitions, String libraryUri, String klass, bool isStatic) { + return _residentCompiler.compileExpression(expression, definitions, typeDefinitions, libraryUri, klass, isStatic); + } + + @override + Future<CompilerOutput> recompile(String mainPath, List<String> invalidatedFiles, {String outputPath, String packagesFilePath}) async { + _codegenDaemon.startBuild(); + await _codegenDaemon.buildResults.first; + // Delete this file so that the frontend_server can handle multi-root. + // TODO(jonahwilliams): investigate frontend_server behavior in the presence + // of multi-root and initialize from dill. + if (await fs.file(outputPath).exists()) { + await fs.file(outputPath).delete(); + } + return _residentCompiler.recompile( + mainPath, + invalidatedFiles, + outputPath: outputPath, + packagesFilePath: _codegenDaemon.packagesFile.path, + ); + } + + @override + Future<CompilerOutput> reject() { + return _residentCompiler.reject(); + } + + @override + void reset() { + _residentCompiler.reset(); + } + + @override + Future<void> shutdown() { + return _residentCompiler.shutdown(); + } +}
diff --git a/packages/flutter_tools/lib/src/commands/generate.dart b/packages/flutter_tools/lib/src/commands/generate.dart new file mode 100644 index 0000000..71b0a8a --- /dev/null +++ b/packages/flutter_tools/lib/src/commands/generate.dart
@@ -0,0 +1,30 @@ +// Copyright 2019 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 '../base/common.dart'; +import '../codegen.dart'; +import '../runner/flutter_command.dart'; + +class GenerateCommand extends FlutterCommand { + GenerateCommand() { + usesTargetOption(); + } + @override + String get description => 'run code generators.'; + + @override + String get name => 'generate'; + + @override + bool get hidden => true; + + @override + Future<FlutterCommandResult> runCommand() async { + if (!experimentalBuildEnabled) { + throwToolExit('FLUTTER_EXPERIMENTAL_BUILD is not enabled, codegen is unsupported.'); + } + await codeGenerator.generate(mainPath: argResults['target']); + return null; + } +} \ No newline at end of file
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 1a31645..6c59ab07 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -10,6 +10,8 @@ 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'; @@ -345,6 +347,10 @@ expFlags = argResults[FlutterOptions.kEnableExperiment]; } + ResidentCompiler residentCompiler; + if (experimentalBuildEnabled) { + residentCompiler = await CodeGeneratingResidentCompiler.create(mainPath: argResults['target']); + } final List<FlutterDevice> flutterDevices = devices.map<FlutterDevice>((Device device) { return FlutterDevice( device, @@ -354,6 +360,7 @@ fileSystemScheme: argResults['filesystem-scheme'], viewFilter: argResults['isolate-filter'], experimentalFlags: expFlags, + generator: residentCompiler, ); }).toList();
diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart index 07aab13..5f7c5d8 100644 --- a/packages/flutter_tools/lib/src/commands/update_packages.dart +++ b/packages/flutter_tools/lib/src/commands/update_packages.dart
@@ -390,7 +390,7 @@ /// "dependency_overrides" sections, as well as the "name" and "version" fields /// in the pubspec header bucketed into [header]. The others are all bucketed /// into [other]. -enum Section { header, dependencies, devDependencies, dependencyOverrides, other } +enum Section { header, dependencies, devDependencies, dependencyOverrides, builders, other } /// The various kinds of dependencies we know and care about. enum DependencyKind { @@ -504,6 +504,11 @@ seenDev = true; } result.add(header); + } else if (section == Section.builders) { + // Do nothing. + // This line isn't a section header, and we're not in a section we care about. + // We just stick the line into the output unmodified. + result.add(PubspecLine(line)); } else if (section == Section.other) { if (line.contains(kDependencyChecksum)) { // This is the pubspec checksum. After computing it, we remove it from the output data @@ -878,6 +883,8 @@ return PubspecHeader(line, Section.devDependencies); case 'dependency_overrides': return PubspecHeader(line, Section.dependencyOverrides); + case 'builders': + return PubspecHeader(line, Section.builders); case 'name': case 'version': return PubspecHeader(line, Section.header, name: sectionName, value: value);
diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index e247969..8fa4ed7 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart
@@ -20,21 +20,10 @@ import 'dart/package_map.dart'; import 'globals.dart'; -KernelCompilerFactory get kernelCompilerFactory => context[KernelCompilerFactory]; +KernelCompiler get kernelCompiler => context[KernelCompiler]; typedef CompilerMessageConsumer = void Function(String message, {bool emphasis, TerminalColor color}); -/// Injectable factory to allow async construction of a [KernelCompiler]. -class KernelCompilerFactory { - const KernelCompilerFactory(); - - /// Return the correct [KernelCompiler] instance for the given project - /// configuration. - FutureOr<KernelCompiler> create() async { - return const KernelCompiler(); - } -} - /// The target model describes the set of core libraries that are availible within /// the SDK. class TargetModel {
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart index 7a4490b..af2976d 100644 --- a/packages/flutter_tools/lib/src/context_runner.dart +++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -22,6 +22,7 @@ import 'base/user_messages.dart'; import 'base/utils.dart'; import 'cache.dart'; +import 'codegen.dart'; import 'compile.dart'; import 'devfs.dart'; import 'device.dart'; @@ -80,7 +81,7 @@ IOSSimulatorUtils: () => IOSSimulatorUtils(), IOSWorkflow: () => const IOSWorkflow(), IOSValidator: () => const IOSValidator(), - KernelCompilerFactory: () => const KernelCompilerFactory(), + KernelCompiler: () => experimentalBuildEnabled ? const CodeGeneratingKernelCompiler() : const KernelCompiler(), LinuxWorkflow: () => const LinuxWorkflow(), Logger: () => platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(), MacOSWorkflow: () => const MacOSWorkflow(),
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index 04e47a8..dc7587c 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart
@@ -107,6 +107,13 @@ /// The `.dart-tool` directory of this project. Directory get dartTool => directory.childDirectory('.dart_tool'); + /// The directory containing the generated code for this project. + Directory get generated => directory + .childDirectory('.dart_tool') + .childDirectory('build') + .childDirectory('generated') + .childDirectory(manifest.appName); + /// The example sub-project of this project. FlutterProject get example => FlutterProject( _exampleDirectory(directory), @@ -147,15 +154,9 @@ } /// Return the set of builders used by this package. - Future<List<String>> get builders async { + Future<YamlMap> get builders async { final YamlMap pubspec = loadYaml(await pubspecFile.readAsString()); - final YamlList builders = pubspec['builders']; - if (builders == null) { - return <String>[]; - } - return builders.map<String>((Object node) { - return node.toString(); - }).toList(); + return pubspec['builders']; } }
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 788cdee..576240d 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -16,6 +16,7 @@ import 'base/terminal.dart'; import 'base/utils.dart'; import 'build_info.dart'; +import 'codegen.dart'; import 'compile.dart'; import 'dart/dependencies.dart'; import 'dart/package_map.dart'; @@ -895,6 +896,11 @@ } bool hasDirtyDependencies(FlutterDevice device) { + /// When using the build system, dependency analysis is handled by build + /// runner instead. + if (experimentalBuildEnabled) { + return false; + } final DartDependencySetBuilder dartDependencySetBuilder = DartDependencySetBuilder(mainPath, packagesFilePath); final DependencyChecker dependencyChecker =
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index 27541dd..116768a 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -15,6 +15,7 @@ import 'base/terminal.dart'; import 'base/utils.dart'; import 'build_info.dart'; +import 'codegen.dart'; import 'compile.dart'; import 'convert.dart'; import 'dart/dependencies.dart'; @@ -122,8 +123,12 @@ return false; } - final DartDependencySetBuilder dartDependencySetBuilder = - DartDependencySetBuilder(mainPath, packagesFilePath); + /// When using the build system, dependency analysis is handled by build + /// runner instead. + if (experimentalBuildEnabled) { + return true; + } + final DartDependencySetBuilder dartDependencySetBuilder = DartDependencySetBuilder(mainPath, packagesFilePath); try { _dartDependencies = Set<String>.from(dartDependencySetBuilder.build()); } on DartDependencyException catch (error) {