blob: 8f64f9badceb2151d0350fba6117d91d2280aa59 [file] [log] [blame]
// Copyright 2014 The Flutter 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 'package:package_config/package_config.dart';
import 'package:process/process.dart';
import 'artifacts.dart';
import 'base/context.dart';
import 'base/file_system.dart';
import 'base/logger.dart';
import 'build_info.dart';
import 'compile.dart';
import 'globals.dart' as globals;
import 'project.dart';
/// The [CodeGenerator] instance.
///
/// If [experimentalBuildEnabled] is false, this will contain an unsupported
/// implementation.
CodeGenerator get codeGenerator => context.get<CodeGenerator>();
/// A wrapper for a build_runner process which delegates to a generated
/// build script.
abstract class CodeGenerator {
const CodeGenerator();
/// Starts a persistent code generating daemon.
///
/// The defines of the daemon command are the arguments required in the
/// flutter_build kernel builder.
Future<CodegenDaemon> daemon(FlutterProject flutterProject);
// Generates a synthetic package under .dart_tool/flutter_tool which is in turn
// used to generate a build script.
Future<void> generateBuildScript(FlutterProject flutterProject);
}
class UnsupportedCodeGenerator extends CodeGenerator {
const UnsupportedCodeGenerator();
@override
Future<void> generateBuildScript(FlutterProject flutterProject) {
throw UnsupportedError('build_runner is not currently supported.');
}
@override
Future<CodegenDaemon> daemon(FlutterProject flutterProject) {
throw UnsupportedError('build_runner is not currently supported.');
}
}
abstract class CodegenDaemon {
/// Whether the previously enqueued build was successful.
Stream<CodegenStatus> get buildResults;
CodegenStatus get lastStatus;
/// Starts a new build.
void startBuild();
}
/// 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 multi-root
/// filesystem and requires a pubspec.
class CodeGeneratingKernelCompiler implements KernelCompiler {
CodeGeneratingKernelCompiler({
@required FileSystem fileSystem,
@required Artifacts artifacts,
@required ProcessManager processManager,
@required Logger logger,
}) : _delegate = KernelCompiler(
logger: logger,
artifacts: artifacts,
processManager: processManager,
fileSystem: fileSystem,
);
final KernelCompiler _delegate;
@override
Future<CompilerOutput> compile({
String mainPath,
String outputFilePath,
bool linkPlatformKernelIn = false,
bool aot = false,
bool trackWidgetCreation,
List<String> extraFrontEndOptions,
String sdkRoot,
String packagesPath,
List<String> fileSystemRoots,
String fileSystemScheme,
String depFilePath,
TargetModel targetModel = TargetModel.flutter,
String initializeFromDill,
String platformDill,
List<String> dartDefines,
@required BuildMode buildMode,
@required PackageConfig packageConfig,
}) async {
final FlutterProject flutterProject = FlutterProject.current();
final CodegenDaemon codegenDaemon = await codeGenerator.daemon(flutterProject);
codegenDaemon.startBuild();
await for (final CodegenStatus codegenStatus in codegenDaemon.buildResults) {
if (codegenStatus == CodegenStatus.Failed) {
globals.printError('Code generation failed, build may have compile errors.');
break;
}
if (codegenStatus == CodegenStatus.Succeeded) {
break;
}
}
return _delegate.compile(
mainPath: mainPath,
outputFilePath: outputFilePath,
linkPlatformKernelIn: linkPlatformKernelIn,
aot: aot,
buildMode: buildMode,
trackWidgetCreation: trackWidgetCreation,
extraFrontEndOptions: extraFrontEndOptions,
sdkRoot: sdkRoot,
packagesPath: packagesPath,
fileSystemRoots: fileSystemRoots,
fileSystemScheme: fileSystemScheme,
depFilePath: depFilePath,
targetModel: targetModel,
initializeFromDill: initializeFromDill,
dartDefines: dartDefines,
packageConfig: packageConfig,
);
}
}
/// 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.
///
/// If `runCold` is true, then no codegen daemon will be created. Instead the
/// compiler will only be initialized with the correct configuration for
/// codegen mode.
static Future<ResidentCompiler> create({
@required ResidentCompiler residentCompiler,
@required FlutterProject flutterProject,
bool runCold = false,
}) async {
if (runCold) {
return residentCompiler;
}
globals.printError(<String>[
'"flutter generate" is deprecated, use "dart pub run build_runner" instead. ',
'The following dependencies must be added to dev_dependencies in pubspec.yaml:',
'build_runner: 1.10.0',
for (Object dependency in flutterProject.builders?.keys ?? const <Object>[])
'$dependency: ${flutterProject.builders[dependency]}'
].join('\n'));
final CodegenDaemon codegenDaemon = await codeGenerator.daemon(flutterProject);
codegenDaemon.startBuild();
final CodegenStatus status = await codegenDaemon.buildResults.firstWhere((CodegenStatus status) {
return status == CodegenStatus.Succeeded || status == CodegenStatus.Failed;
});
if (status == CodegenStatus.Failed) {
globals.printError('Code generation failed, build may have compile errors.');
}
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> compileExpressionToJs(
String libraryUri, int line, int column, Map<String, String> jsModules,
Map<String, String> jsFrameValues, String moduleName, String expression
) {
return _residentCompiler.compileExpressionToJs(
libraryUri, line, column, jsModules, jsFrameValues, moduleName, expression);
}
@override
Future<CompilerOutput> recompile(
Uri mainUri,
List<Uri> invalidatedFiles, {
String outputPath,
PackageConfig packageConfig,
bool suppressErrors = false,
}) async {
if (_codegenDaemon.lastStatus != CodegenStatus.Succeeded && _codegenDaemon.lastStatus != CodegenStatus.Failed) {
await _codegenDaemon.buildResults.firstWhere((CodegenStatus status) {
return status == CodegenStatus.Succeeded || status == CodegenStatus.Failed;
});
}
if (_codegenDaemon.lastStatus == CodegenStatus.Failed) {
globals.printError('Code generation failed, build may have compile errors.');
}
return _residentCompiler.recompile(
mainUri,
invalidatedFiles,
outputPath: outputPath,
packageConfig: packageConfig,
suppressErrors: suppressErrors,
);
}
@override
Future<CompilerOutput> reject() {
return _residentCompiler.reject();
}
@override
void reset() {
_residentCompiler.reset();
}
@override
Future<void> shutdown() {
return _residentCompiler.shutdown();
}
@override
void addFileSystemRoot(String root) {
_residentCompiler.addFileSystemRoot(root);
}
}
/// The current status of a codegen build.
enum CodegenStatus {
/// The build has started running.
///
/// If this is the current status when running a hot reload, an additional build does
/// not need to be started.
Started,
/// The build succeeded.
Succeeded,
/// The build failed.
Failed
}