| // 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 |
| } |