
* add trailing commas on list/map/parameters * add trailing commas on Invocation with nb of arg>1 * add commas for widget containing widgets * add trailing commas if instantiation contains trailing comma * revert bad change
320 lines
10 KiB
Dart
320 lines
10 KiB
Dart
// 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<CodegenStatus> get buildResults;
|
|
|
|
CodegenStatus get lastStatus;
|
|
|
|
/// 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();
|
|
final CodegenStatus status = await codegenDaemon.buildResults.firstWhere((CodegenStatus status) {
|
|
return status ==CodegenStatus.Succeeded || status == CodegenStatus.Failed;
|
|
});
|
|
if (status == CodegenStatus.Failed) {
|
|
printError('Codegeneration failed, halting build.');
|
|
}
|
|
final ResidentCompiler residentCompiler = ResidentCompiler(
|
|
artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath),
|
|
trackWidgetCreation: trackWidgetCreation,
|
|
packagesPath: codegenDaemon.packagesFile.path,
|
|
fileSystemRoots: <String>[
|
|
fs.path.join(flutterProject.generated.absolute.path, 'lib${platform.pathSeparator}'),
|
|
fs.path.join(flutterProject.directory.path, 'lib${platform.pathSeparator}'),
|
|
],
|
|
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 {
|
|
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('Codegeneration failed, halting build.');
|
|
}
|
|
// 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();
|
|
}
|
|
}
|
|
|
|
/// 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
|
|
}
|