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 'dart/package_map.dart';
import 'globals.dart';
import 'project.dart';
const String kMultiRootScheme = 'org-dartlang-app';
CodeGenerator get codeGenerator => context.get<CodeGenerator>();
abstract class CodeGenerator {
const CodeGenerator();
Future<CodegenDaemon> daemon(FlutterProject flutterProject);
Future<void> generateBuildScript(FlutterProject flutterProject);
void updatePackages(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 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 {
Stream<CodegenStatus> get buildResults;
CodegenStatus get lastStatus;
void startBuild();
}
class CodeGeneratingKernelCompiler implements KernelCompiler {
const CodeGeneratingKernelCompiler();
static const KernelCompiler _delegate = KernelCompiler();
@override
Future<CompilerOutput> compile({
String mainPath,
String outputFilePath,
bool linkPlatformKernelIn = false,
bool aot = false,
bool trackWidgetCreation,
List<String> extraFrontEndOptions,
bool targetProductVm = false,
String sdkRoot,
String packagesPath,
List<String> fileSystemRoots,
String fileSystemScheme,
String depFilePath,
TargetModel targetModel = TargetModel.flutter,
String initializeFromDill,
String platformDill,
}) 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 FlutterProject flutterProject = FlutterProject.current();
codeGenerator.updatePackages(flutterProject);
final CodegenDaemon codegenDaemon = await codeGenerator.daemon(flutterProject);
codegenDaemon.startBuild();
await for (CodegenStatus codegenStatus in codegenDaemon.buildResults) {
if (codegenStatus == CodegenStatus.Failed) {
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,
trackWidgetCreation: trackWidgetCreation,
extraFrontEndOptions: extraFrontEndOptions,
targetProductVm: targetProductVm,
sdkRoot: sdkRoot,
packagesPath: PackageMap.globalGeneratedPackagesPath,
fileSystemRoots: <String>[
fs.path.join(flutterProject.generated.path, 'lib${platform.pathSeparator}'),
fs.path.join(flutterProject.directory.path, 'lib${platform.pathSeparator}'),
],
fileSystemScheme: kMultiRootScheme,
depFilePath: depFilePath,
targetModel: targetModel,
initializeFromDill: initializeFromDill,
);
}
}
class CodeGeneratingResidentCompiler implements ResidentCompiler {
CodeGeneratingResidentCompiler._(this._residentCompiler, this._codegenDaemon, this._flutterProject);
static Future<ResidentCompiler> create({
@required FlutterProject flutterProject,
bool trackWidgetCreation = false,
CompilerMessageConsumer compilerMessageConsumer = printError,
bool unsafePackageSerialization = false,
String outputPath,
String initializeFromDill,
bool runCold = false,
}) async {
codeGenerator.updatePackages(flutterProject);
final ResidentCompiler residentCompiler = ResidentCompiler(
artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath),
trackWidgetCreation: trackWidgetCreation,
packagesPath: PackageMap.globalGeneratedPackagesPath,
fileSystemRoots: <String>[
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,
);
if (runCold) {
return residentCompiler;
}
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) {
printError('Code generation failed, build may have compile errors.');
}
return CodeGeneratingResidentCompiler._(residentCompiler, codegenDaemon, flutterProject);
}
final ResidentCompiler _residentCompiler;
final CodegenDaemon _codegenDaemon;
final FlutterProject _flutterProject;
@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<Uri> invalidatedFiles, {String outputPath, String packagesFilePath}) 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) {
printError('Code generation failed, build may have compile errors.');
}
if (fs.statSync(PackageMap.globalPackagesPath).modified.millisecondsSinceEpoch >
fs.statSync(PackageMap.globalGeneratedPackagesPath).modified.millisecondsSinceEpoch) {
codeGenerator.updatePackages(_flutterProject);
invalidatedFiles.add(fs.file(PackageMap.globalGeneratedPackagesPath).uri);
}
return _residentCompiler.recompile(
mainPath,
invalidatedFiles,
outputPath: outputPath,
packagesFilePath: PackageMap.globalGeneratedPackagesPath,
);
}
@override
Future<CompilerOutput> reject() {
return _residentCompiler.reject();
}
@override
void reset() {
_residentCompiler.reset();
}
@override
Future<void> shutdown() {
return _residentCompiler.shutdown();
}
}
enum CodegenStatus {
Started,
Succeeded,
Failed
}