| // Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:async'; |
| import 'dart:io' as io show exitCode, File, IOSink; |
| |
| import 'package:args/args.dart' show ArgParser, ArgResults; |
| import 'package:front_end/src/api_unstable/vm.dart' |
| show |
| CompilerOptions, |
| InvocationMode, |
| DiagnosticMessage, |
| Verbosity, |
| parseExperimentalArguments, |
| parseExperimentalFlags, |
| resolveInputUri; |
| import 'package:kernel/ast.dart' show Component; |
| import 'package:vm/kernel_front_end.dart' |
| show |
| badUsageExitCode, |
| compileTimeErrorExitCode, |
| compileToKernel, |
| convertToPackageUri, |
| createFrontEndFileSystem, |
| createFrontEndTarget, |
| ErrorDetector, |
| ErrorPrinter, |
| KernelCompilationArguments, |
| parseCommandLineDefines, |
| successExitCode, |
| writeDepfile; |
| |
| import 'bytecode_serialization.dart' show BytecodeSizeStatistics; |
| import 'bytecode_generator.dart' show generateBytecode; |
| import 'options.dart' show BytecodeOptions; |
| |
| final ArgParser _argParser = ArgParser(allowTrailingOptions: true) |
| ..addOption('platform', |
| help: 'Path to vm_platform.dill file', defaultsTo: null) |
| ..addOption('packages', |
| help: 'Path to .dart_tool/package_config.json file', defaultsTo: null) |
| ..addOption('output', |
| abbr: 'o', help: 'Path to resulting bytecode file', defaultsTo: null) |
| ..addOption('depfile', help: 'Path to output Ninja depfile') |
| ..addOption( |
| 'depfile-target', |
| help: 'Override the target in the generated depfile', |
| hide: true, |
| ) |
| ..addMultiOption('filesystem-root', |
| help: 'A base path for the multi-root virtual file system.' |
| ' If multi-root file system is used, the input script and .dart_tool/package_config.json file should be specified using URI.') |
| ..addOption('filesystem-scheme', |
| help: 'The URI scheme for the multi-root virtual filesystem.') |
| ..addOption('target', |
| help: 'Target model that determines what core libraries are available', |
| allowed: <String>['vm', 'flutter', 'flutter_runner', 'dart_runner'], |
| defaultsTo: 'vm') |
| ..addMultiOption('define', |
| abbr: 'D', |
| help: 'The values for the environment constants (e.g. -Dkey=value).') |
| ..addOption('import-dill', |
| help: 'Import libraries from existing dill file', defaultsTo: null) |
| ..addFlag('enable-asserts', |
| help: 'Whether asserts will be enabled.', defaultsTo: false) |
| ..addMultiOption('bytecode-options', |
| help: 'Specify options for bytecode generation:', |
| valueHelp: 'opt1,opt2,...', |
| allowed: BytecodeOptions.commandLineFlags.keys, |
| allowedHelp: BytecodeOptions.commandLineFlags) |
| ..addMultiOption('enable-experiment', |
| help: 'Comma separated list of experimental features to enable.') |
| ..addFlag('help', |
| abbr: 'h', negatable: false, help: 'Print this help message.') |
| ..addFlag('track-widget-creation', |
| help: 'Run a kernel transformer to track creation locations for widgets.', |
| defaultsTo: false) |
| ..addOption('invocation-modes', |
| help: 'Provides information to the front end about how it is invoked.', |
| defaultsTo: '') |
| ..addOption('validate', |
| help: |
| 'Validate dynamic module against specified dynamic interface YAML file', |
| defaultsTo: null) |
| ..addOption('verbosity', |
| help: 'Sets the verbosity level used for filtering messages during ' |
| 'compilation.', |
| defaultsTo: Verbosity.defaultValue); |
| |
| final String _usage = ''' |
| Usage: dart2bytecode --platform vm_platform.dill [--import-dill host_app.dill] [--validate dynamic_interface.yaml] [options] input.dart |
| Compiles Dart sources to Dart bytecode. |
| |
| Options: |
| ${_argParser.usage} |
| '''; |
| |
| Future<void> main(List<String> arguments) async { |
| io.exitCode = await runCompilerWithCommandLineArguments(arguments); |
| } |
| |
| /// Run bytecode compiler tool with given [arguments] |
| /// and return exit code (0 on success, non-zero on failure). |
| Future<int> runCompilerWithCommandLineArguments(List<String> arguments) async { |
| final ArgResults options = _argParser.parse(arguments); |
| final String? platformKernel = options['platform']; |
| |
| if (options['help']) { |
| print(_usage); |
| return successExitCode; |
| } |
| |
| final String? input = options.rest.singleOrNull; |
| if (input == null || platformKernel == null) { |
| print(_usage); |
| return badUsageExitCode; |
| } |
| |
| final String outputFileName = options['output'] ?? "$input.bytecode"; |
| final String? packages = options['packages']; |
| final String targetName = options['target']; |
| final String? fileSystemScheme = options['filesystem-scheme']; |
| final String? depfile = options['depfile']; |
| final String? depfileTarget = options['depfile-target']; |
| final List<String>? fileSystemRoots = options['filesystem-root']; |
| final bool enableAsserts = options['enable-asserts']; |
| final List<String>? experimentalFlags = options['enable-experiment']; |
| final Map<String, String> environmentDefines = {}; |
| |
| if (!parseCommandLineDefines(options['define'], environmentDefines, _usage)) { |
| return badUsageExitCode; |
| } |
| |
| final String? importDill = options['import-dill']; |
| final String? validateDynamicInterface = options['validate']; |
| final String messageVerbosity = options['verbosity']; |
| final String cfeInvocationModes = options['invocation-modes']; |
| final bool trackWidgetCreation = options['track-widget-creation']; |
| final List<String>? bytecodeGeneratorOptions = options['bytecode-options']; |
| |
| return await runCompilerWithOptions( |
| input: input, |
| platformKernel: platformKernel, |
| outputFileName: outputFileName, |
| targetName: targetName, |
| packages: packages, |
| importDill: importDill, |
| validateDynamicInterface: validateDynamicInterface, |
| enableAsserts: enableAsserts, |
| experimentalFlags: experimentalFlags, |
| environmentDefines: environmentDefines, |
| fileSystemScheme: fileSystemScheme, |
| fileSystemRoots: fileSystemRoots, |
| messageVerbosity: messageVerbosity, |
| cfeInvocationModes: cfeInvocationModes, |
| trackWidgetCreation: trackWidgetCreation, |
| bytecodeGeneratorOptions: bytecodeGeneratorOptions, |
| depfile: depfile, |
| depfileTarget: depfileTarget, |
| ); |
| } |
| |
| /// Run bytecode compiler tool with given options |
| /// and return exit code (0 on success, non-zero on failure). |
| Future<int> runCompilerWithOptions({ |
| required String input, |
| required String platformKernel, |
| required String outputFileName, |
| required String targetName, |
| String? packages, |
| String? importDill, |
| String? validateDynamicInterface, |
| bool enableAsserts = false, |
| List<String>? experimentalFlags, |
| Map<String, String> environmentDefines = const {}, |
| String? fileSystemScheme, |
| List<String>? fileSystemRoots, |
| String messageVerbosity = Verbosity.defaultValue, |
| void Function(String) printMessage = print, |
| String cfeInvocationModes = '', |
| bool trackWidgetCreation = false, |
| List<String>? bytecodeGeneratorOptions, |
| String? depfile, |
| String? depfileTarget, |
| }) async { |
| final fileSystem = |
| createFrontEndFileSystem(fileSystemScheme, fileSystemRoots); |
| |
| final Uri? packagesUri = packages != null ? resolveInputUri(packages) : null; |
| |
| final platformKernelUri = Uri.base.resolveUri(new Uri.file(platformKernel)); |
| |
| final List<Uri> additionalDills = <Uri>[]; |
| if (importDill != null) { |
| additionalDills.add(Uri.base.resolveUri(new Uri.file(importDill))); |
| } |
| |
| final Uri? dynamicInterfaceSpecificationUri = |
| (validateDynamicInterface != null) |
| ? resolveInputUri(validateDynamicInterface) |
| : null; |
| |
| final verbosity = Verbosity.parseArgument(messageVerbosity); |
| final errorPrinter = ErrorPrinter(verbosity, println: printMessage); |
| final errorDetector = ErrorDetector(previousErrorHandler: errorPrinter.call); |
| |
| Uri mainUri = resolveInputUri(input); |
| if (packagesUri != null) { |
| mainUri = await convertToPackageUri(fileSystem, mainUri, packagesUri); |
| } |
| |
| final CompilerOptions compilerOptions = CompilerOptions() |
| ..sdkSummary = platformKernelUri |
| ..fileSystem = fileSystem |
| ..additionalDills = additionalDills |
| ..packagesFileUri = packagesUri |
| ..dynamicInterfaceSpecificationUri = dynamicInterfaceSpecificationUri |
| ..explicitExperimentalFlags = parseExperimentalFlags( |
| parseExperimentalArguments(experimentalFlags), |
| onError: printMessage) |
| ..onDiagnostic = (DiagnosticMessage m) { |
| errorDetector(m); |
| } |
| ..embedSourceText = false |
| ..invocationModes = InvocationMode.parseArguments(cfeInvocationModes) |
| ..verbosity = verbosity; |
| |
| compilerOptions.target = createFrontEndTarget(targetName, |
| trackWidgetCreation: trackWidgetCreation, supportMirrors: false); |
| if (compilerOptions.target == null) { |
| printMessage('Failed to create front-end target $targetName.'); |
| return badUsageExitCode; |
| } |
| |
| final results = await compileToKernel(KernelCompilationArguments( |
| source: mainUri, |
| options: compilerOptions, |
| requireMain: false, |
| includePlatform: false, |
| environmentDefines: Map.of(environmentDefines), |
| enableAsserts: enableAsserts)); |
| |
| errorPrinter.printCompilationMessages(); |
| |
| final Component? component = results.component; |
| if (errorDetector.hasCompilationErrors || component == null) { |
| return compileTimeErrorExitCode; |
| } |
| |
| final BytecodeOptions bytecodeOptions = |
| BytecodeOptions(enableAsserts: enableAsserts) |
| ..parseCommandLineFlags(bytecodeGeneratorOptions); |
| |
| if (bytecodeOptions.showBytecodeSizeStatistics) { |
| BytecodeSizeStatistics.reset(); |
| } |
| final io.IOSink sink = io.File(outputFileName).openWrite(); |
| generateBytecode(component, sink, |
| libraries: component.libraries |
| .where((lib) => !results.loadedLibraries.contains(lib)) |
| .toList(), |
| hierarchy: results.classHierarchy!, |
| coreTypes: results.coreTypes!, |
| options: bytecodeOptions, |
| target: compilerOptions.target!); |
| await sink.close(); |
| if (bytecodeOptions.showBytecodeSizeStatistics) { |
| BytecodeSizeStatistics.dump(); |
| } |
| |
| if (depfile != null) { |
| await writeDepfile( |
| fileSystem, |
| results.compiledSources!, |
| depfileTarget ?? outputFileName, |
| depfile, |
| ); |
| } |
| |
| return successExitCode; |
| } |