| // Copyright (c) 2016, 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:io' show exit; | |
| import 'package:_fe_analyzer_shared/src/messages/severity.dart' | |
| show CfeSeverity; | |
| import 'package:_fe_analyzer_shared/src/util/options.dart'; | |
| import 'package:_fe_analyzer_shared/src/util/resolve_input_uri.dart' | |
| show resolveInputUri; | |
| import 'package:build_integration/file_system/single_root.dart' | |
| show SingleRootFileSystem; | |
| import 'package:front_end/src/api_prototype/compiler_options.dart'; | |
| import 'package:front_end/src/api_prototype/experimental_flags.dart' | |
| show ExperimentalFlag; | |
| import 'package:front_end/src/api_prototype/file_system.dart' show FileSystem; | |
| import 'package:front_end/src/api_prototype/standard_file_system.dart' | |
| show StandardFileSystem; | |
| import 'package:front_end/src/api_prototype/terminal_color_support.dart'; | |
| import 'package:front_end/src/base/command_line_options.dart'; | |
| import 'package:front_end/src/base/compiler_context.dart' show CompilerContext; | |
| import 'package:front_end/src/base/file_system_dependency_tracker.dart'; | |
| import 'package:front_end/src/base/problems.dart' show DebugAbort; | |
| import 'package:front_end/src/base/processed_options.dart' | |
| show ProcessedOptions; | |
| import 'package:front_end/src/codes/cfe_codes.dart' | |
| show Message, PlainAndColorizedString; | |
| import 'package:front_end/src/codes/diagnostic.dart' as diag; | |
| import 'package:front_end/src/compute_platform_binaries_location.dart' | |
| show computePlatformBinariesLocation, computePlatformDillName; | |
| import 'package:front_end/src/scheme_based_file_system.dart' | |
| show SchemeBasedFileSystem; | |
| import 'package:kernel/target/targets.dart' | |
| show Target, TargetFlags, TestTargetFlags, getTarget, targets; | |
| // Before adding new options here, you must: | |
| // * Document the option. | |
| // * Get an explicit approval from the front-end team. | |
| const List<Option> optionSpecification = [ | |
| Options.compileSdk, | |
| Options.dumpIr, | |
| Options.showOffsets, | |
| Options.enableExperiment, | |
| Options.excludeSource, | |
| Options.omitPlatform, | |
| Options.fatal, | |
| Options.fatalSkip, | |
| Options.forceLateLowering, | |
| Options.forceLateLoweringSentinel, | |
| Options.forceStaticFieldLowering, | |
| Options.forceNoExplicitGetterCalls, | |
| Options.forceConstructorTearOffLowering, | |
| Options.forceClosureContextLowering, | |
| Options.help, | |
| Options.librariesJson, | |
| Options.noDefines, | |
| Options.output, | |
| Options.packages, | |
| Options.platform, | |
| Options.sdk, | |
| Options.singleRootBase, | |
| Options.singleRootScheme, | |
| Options.nnbdWeakMode, | |
| Options.nnbdStrongMode, | |
| Options.target, | |
| Options.verbose, | |
| Options.verbosity, | |
| Options.verify, | |
| Options.skipPlatformVerification, | |
| Options.linkDependencies, | |
| Options.noDeps, | |
| Options.invocationModes, | |
| Options.defines, | |
| ]; | |
| void throwCommandLineProblem(String message) { | |
| throw new CommandLineProblem.deprecated(message); | |
| } | |
| ProcessedOptions analyzeCommandLine( | |
| FileSystemDependencyTracker? tracker, | |
| String programName, | |
| ParsedOptions parsedOptions, | |
| bool areRestArgumentsInputs, | |
| ) { | |
| final List<String> arguments = parsedOptions.arguments; | |
| final bool help = Options.help.read(parsedOptions); | |
| final bool verbose = Options.verbose.read(parsedOptions); | |
| if (help) { | |
| print(computeUsage(programName, verbose).problemMessage); | |
| exit(0); | |
| } | |
| if (parsedOptions.options.containsKey(Flags.compileSdk) && | |
| parsedOptions.options.containsKey(Flags.platform)) { | |
| return throw new CommandLineProblem.deprecated( | |
| "Can't specify both '${Flags.compileSdk}' and '${Flags.platform}'.", | |
| ); | |
| } | |
| final String targetName = Options.target.read(parsedOptions); | |
| Map<ExperimentalFlag, bool> explicitExperimentalFlags = | |
| parseExperimentalFlags( | |
| parseExperimentalArguments( | |
| Options.enableExperiment.read(parsedOptions), | |
| ), | |
| onError: throwCommandLineProblem, | |
| onWarning: print, | |
| ); | |
| final TargetFlags flags = new TestTargetFlags( | |
| forceLateLoweringsForTesting: Options.forceLateLowering.read(parsedOptions), | |
| forceStaticFieldLoweringForTesting: Options.forceStaticFieldLowering.read( | |
| parsedOptions, | |
| ), | |
| forceNoExplicitGetterCallsForTesting: Options.forceNoExplicitGetterCalls | |
| .read(parsedOptions), | |
| forceConstructorTearOffLoweringForTesting: Options | |
| .forceConstructorTearOffLowering | |
| .read(parsedOptions), | |
| forceLateLoweringSentinelForTesting: Options.forceLateLoweringSentinel.read( | |
| parsedOptions, | |
| ), | |
| isClosureContextLoweringEnabled: Options.forceClosureContextLowering.read( | |
| parsedOptions, | |
| ), | |
| ); | |
| final Target? target = getTarget(targetName, flags); | |
| if (target == null) { | |
| return throw new CommandLineProblem.deprecated( | |
| "Target '${targetName}' not recognized. " | |
| "Valid targets are:\n ${targets.keys.join("\n ")}", | |
| ); | |
| } | |
| final bool noDefines = Options.noDefines.read(parsedOptions); | |
| final bool noDeps = Options.noDeps.read(parsedOptions); | |
| final bool verify = Options.verify.read(parsedOptions); | |
| final bool skipPlatformVerification = Options.skipPlatformVerification.read( | |
| parsedOptions, | |
| ); | |
| final bool showOffsets = Options.showOffsets.read(parsedOptions); | |
| final bool dumpIr = Options.dumpIr.read(parsedOptions) || showOffsets; | |
| final bool excludeSource = Options.excludeSource.read(parsedOptions); | |
| final bool omitPlatform = Options.omitPlatform.read(parsedOptions); | |
| final Uri? packages = Options.packages.read(parsedOptions); | |
| final Set<String> fatal = new Set<String>.from( | |
| Options.fatal.read(parsedOptions) ?? <String>[], | |
| ); | |
| final bool errorsAreFatal = fatal.contains("errors"); | |
| final bool warningsAreFatal = fatal.contains("warnings"); | |
| final int fatalSkip = | |
| int.tryParse(Options.fatalSkip.read(parsedOptions) ?? "0") ?? -1; | |
| final bool compileSdk = Options.compileSdk.read(parsedOptions) != null; | |
| final String? singleRootScheme = Options.singleRootScheme.read(parsedOptions); | |
| final Uri? singleRootBase = Options.singleRootBase.read(parsedOptions); | |
| final List<Uri> linkDependencies = | |
| Options.linkDependencies.read(parsedOptions) ?? []; | |
| final String invocationModes = | |
| Options.invocationModes.read(parsedOptions) ?? ''; | |
| final String verbosity = Options.verbosity.read(parsedOptions); | |
| FileSystem fileSystem; | |
| if (tracker != null) { | |
| fileSystem = StandardFileSystem.instanceWithTracking(tracker); | |
| } else { | |
| fileSystem = StandardFileSystem.instance; | |
| } | |
| if (singleRootScheme != null) { | |
| fileSystem = new SchemeBasedFileSystem({ | |
| 'file': fileSystem, | |
| 'data': fileSystem, | |
| // TODO(askesc): remove also when fixing StandardFileSystem (empty schemes | |
| // should have been handled elsewhere). | |
| '': fileSystem, | |
| singleRootScheme: new SingleRootFileSystem( | |
| singleRootScheme, | |
| singleRootBase!, | |
| fileSystem, | |
| ), | |
| }); | |
| } | |
| // In order to facilitate the roll out of CFE constants on Dart2js, we need to | |
| // be able to support both passing '--no-defines' and also evaluating CFE | |
| // constants when the target supports it. This is so we can easily enable or | |
| // disable fully evaluating constants in the CFE with a flag. This can be | |
| // deleted when the CFE fully evaluates constants for Dart2js in all cases. | |
| Map<String, String>? environmentDefines; | |
| if (noDefines && target.constantsBackend.supportsUnevaluatedConstants) { | |
| // Pass a null environment. | |
| } else { | |
| environmentDefines = parsedOptions.defines; | |
| } | |
| CompilerOptions compilerOptions = new CompilerOptions() | |
| ..compileSdk = compileSdk | |
| ..fileSystem = fileSystem | |
| ..packagesFileUri = packages | |
| ..target = target | |
| ..throwOnErrorsForDebugging = errorsAreFatal | |
| ..throwOnWarningsForDebugging = warningsAreFatal | |
| ..skipForDebugging = fatalSkip | |
| ..embedSourceText = !excludeSource | |
| ..debugDump = dumpIr | |
| ..debugDumpShowOffsets = showOffsets | |
| ..omitPlatform = omitPlatform | |
| ..verbose = verbose | |
| ..verify = verify | |
| ..skipPlatformVerification = skipPlatformVerification | |
| ..explicitExperimentalFlags = explicitExperimentalFlags | |
| ..environmentDefines = environmentDefines | |
| ..additionalDillModules = linkDependencies | |
| ..emitDeps = !noDeps | |
| ..invocationModes = InvocationMode.parseArguments(invocationModes) | |
| ..verbosity = Verbosity.parseArgument(verbosity); | |
| if (programName == "compile_platform") { | |
| if (arguments.length != 5) { | |
| return throw new CommandLineProblem.deprecated( | |
| "Expected five arguments.", | |
| ); | |
| } | |
| if (compileSdk) { | |
| return throw new CommandLineProblem.deprecated( | |
| "Cannot specify '${Flags.compileSdk}' option to compile_platform.", | |
| ); | |
| } | |
| if (parsedOptions.options.containsKey(Flags.output)) { | |
| return throw new CommandLineProblem.deprecated( | |
| "Cannot specify '${Flags.output}' option to compile_platform.", | |
| ); | |
| } | |
| return new ProcessedOptions( | |
| options: compilerOptions | |
| ..sdkSummary = Options.platform.read(parsedOptions) | |
| ..librariesSpecificationUri = resolveInputUri(arguments[1]) | |
| ..setExitCodeOnProblem = true, | |
| inputs: arguments[0].split(',').map(Uri.parse).toList(), | |
| output: resolveInputUri(arguments[3]), | |
| ); | |
| } else if (arguments.isEmpty) { | |
| return throw new CommandLineProblem.deprecated("No Dart file specified."); | |
| } | |
| final Uri defaultOutput = resolveInputUri("${arguments.first}.dill"); | |
| final Uri output = Options.output.read(parsedOptions) ?? defaultOutput; | |
| final Uri? sdk = | |
| Options.sdk.read(parsedOptions) ?? Options.compileSdk.read(parsedOptions); | |
| final Uri? librariesJson = Options.librariesJson.read(parsedOptions); | |
| final Uri? platform = compileSdk | |
| ? null | |
| : (Options.platform.read(parsedOptions) ?? | |
| computePlatformBinariesLocation(forceBuildDir: true).resolve( | |
| computePlatformDillName(target, () { | |
| throwCommandLineProblem( | |
| "Target '${target.name}' requires an explicit " | |
| "'${Flags.platform}' option.", | |
| ); | |
| })!, | |
| )); | |
| compilerOptions | |
| ..sdkRoot = sdk | |
| ..sdkSummary = platform | |
| ..librariesSpecificationUri = librariesJson; | |
| List<Uri> inputs = <Uri>[]; | |
| if (areRestArgumentsInputs) { | |
| for (String argument in arguments) { | |
| inputs.add(resolveInputUri(argument)); | |
| } | |
| } | |
| return new ProcessedOptions( | |
| options: compilerOptions, | |
| inputs: inputs, | |
| output: output, | |
| ); | |
| } | |
| Future<T> withGlobalOptions<T>( | |
| String programName, | |
| List<String> arguments, | |
| bool areRestArgumentsInputs, | |
| Future<T> f(CompilerContext context, List<String> restArguments), { | |
| FileSystemDependencyTracker? tracker, | |
| }) { | |
| ParsedOptions? parsedOptions; | |
| ProcessedOptions options; | |
| CommandLineProblem? problem; | |
| try { | |
| parsedOptions = ParsedOptions.parse(arguments, optionSpecification); | |
| options = analyzeCommandLine( | |
| tracker, | |
| programName, | |
| parsedOptions, | |
| areRestArgumentsInputs, | |
| ); | |
| } on CommandLineProblem catch (e) { | |
| options = new ProcessedOptions(); | |
| problem = e; | |
| } | |
| return CompilerContext.runWithOptions<T>(options, (CompilerContext c) { | |
| if (problem != null) { | |
| print(computeUsage(programName, options.verbose).problemMessage); | |
| PlainAndColorizedString formatted = c.format( | |
| problem.message.withoutLocation(), | |
| CfeSeverity.error, | |
| ); | |
| String formattedText; | |
| if (enableColors) { | |
| formattedText = formatted.colorized; | |
| } else { | |
| formattedText = formatted.plain; | |
| } | |
| print(formattedText); | |
| exit(1); | |
| } | |
| return f(c, parsedOptions!.arguments); | |
| }, errorOnMissingInput: problem == null); | |
| } | |
| Message computeUsage(String programName, bool verbose) { | |
| String basicUsage = "Usage: $programName [options] dartfile\n"; | |
| String? summary; | |
| String options = | |
| (verbose | |
| ? diag.fastaUsageLong.problemMessage | |
| : diag.fastaUsageShort.problemMessage) | |
| .trim(); | |
| switch (programName) { | |
| case "outline": | |
| summary = | |
| "Creates an outline of a Dart program in the Dill/Kernel IR format."; | |
| break; | |
| case "compile": | |
| summary = "Compiles a Dart program to the Dill/Kernel IR format."; | |
| break; | |
| case "run": | |
| summary = "Runs a Dart program."; | |
| break; | |
| case "compile_platform": | |
| summary = "Compiles Dart SDK platform to the Dill/Kernel IR format."; | |
| basicUsage = | |
| "Usage: $programName [options]" | |
| " dart-library-uri libraries.json vm_outline.dill" | |
| " platform.dill outline.dill\n"; | |
| } | |
| StringBuffer sb = new StringBuffer(basicUsage); | |
| if (summary != null) { | |
| sb.writeln(); | |
| sb.writeln(summary); | |
| sb.writeln(); | |
| } | |
| sb.write(options); | |
| // TODO(ahe): Don't use [codeUnspecified]. | |
| return diag.unspecified.withArguments(message: "$sb"); | |
| } | |
| Future<T> runProtectedFromAbort<T>( | |
| Future<T> Function() action, [ | |
| T? failingValue, | |
| ]) async { | |
| try { | |
| return await action(); | |
| } on DebugAbort catch (e) { | |
| print(e.message.problemMessage); | |
| // DebugAbort should never happen in production code, so we want test.py to | |
| // treat this as a crash which is signalled by exiting with 255. | |
| exit(255); | |
| } | |
| } |