Add basic codegen app to be used for integration testing and benchmarks (#27257)


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) {