| // 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. |
| |
| library fasta.tool.command_line; |
| |
| import 'dart:io' show exit; |
| |
| import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity; |
| import 'package:_fe_analyzer_shared/src/util/options.dart'; |
| |
| 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, isExperimentEnabled; |
| |
| 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/nnbd_mode.dart'; |
| |
| import 'package:front_end/src/base/processed_options.dart' |
| show ProcessedOptions; |
| |
| import 'package:front_end/src/compute_platform_binaries_location.dart' |
| show computePlatformBinariesLocation, computePlatformDillName; |
| |
| import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext; |
| |
| import 'package:front_end/src/base/command_line_options.dart'; |
| |
| import 'package:front_end/src/fasta/fasta_codes.dart' |
| show |
| Message, |
| PlainAndColorizedString, |
| messageFastaUsageLong, |
| messageFastaUsageShort, |
| templateUnspecified; |
| |
| import 'package:front_end/src/fasta/problems.dart' show DebugAbort; |
| |
| import 'package:_fe_analyzer_shared/src/util/resolve_input_uri.dart' |
| show resolveInputUri; |
| |
| 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.enableExperiment, |
| Options.enableUnscheduledExperiments, |
| Options.excludeSource, |
| Options.omitPlatform, |
| Options.fatal, |
| Options.fatalSkip, |
| Options.forceLateLowering, |
| Options.forceLateLoweringSentinel, |
| Options.forceStaticFieldLowering, |
| Options.forceNoExplicitGetterCalls, |
| Options.forceConstructorTearOffLowering, |
| Options.help, |
| Options.librariesJson, |
| Options.noDefines, |
| Options.output, |
| Options.packages, |
| Options.platform, |
| Options.sdk, |
| Options.singleRootBase, |
| Options.singleRootScheme, |
| Options.nnbdWeakMode, |
| Options.nnbdStrongMode, |
| Options.nnbdAgnosticMode, |
| Options.target, |
| Options.verbose, |
| Options.verbosity, |
| Options.verify, |
| Options.skipPlatformVerification, |
| Options.warnOnReachabilityCheck, |
| Options.linkDependencies, |
| Options.noDeps, |
| Options.invocationModes, |
| Options.defines, |
| ]; |
| |
| void throwCommandLineProblem(String message) { |
| throw new CommandLineProblem.deprecated(message); |
| } |
| |
| ProcessedOptions analyzeCommandLine(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), |
| enableNullSafety: isExperimentEnabled(ExperimentalFlag.nonNullable, |
| explicitExperimentalFlags: explicitExperimentalFlags)); |
| |
| 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 dumpIr = Options.dumpIr.read(parsedOptions); |
| |
| 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 bool nnbdStrongMode = Options.nnbdStrongMode.read(parsedOptions); |
| |
| final bool nnbdWeakMode = Options.nnbdWeakMode.read(parsedOptions); |
| |
| final bool nnbdAgnosticMode = Options.nnbdAgnosticMode.read(parsedOptions); |
| |
| final NnbdMode nnbdMode = nnbdAgnosticMode |
| ? NnbdMode.Agnostic |
| : (nnbdStrongMode ? NnbdMode.Strong : NnbdMode.Weak); |
| |
| final bool enableUnscheduledExperiments = |
| Options.enableUnscheduledExperiments.read(parsedOptions); |
| |
| final bool warnOnReachabilityCheck = |
| Options.warnOnReachabilityCheck.read(parsedOptions); |
| |
| final List<Uri> linkDependencies = |
| Options.linkDependencies.read(parsedOptions) ?? []; |
| |
| final String invocationModes = |
| Options.invocationModes.read(parsedOptions) ?? ''; |
| |
| final String verbosity = Options.verbosity.read(parsedOptions); |
| |
| if (nnbdStrongMode && nnbdWeakMode) { |
| return throw new CommandLineProblem.deprecated( |
| "Can't specify both '${Flags.nnbdStrongMode}' and " |
| "'${Flags.nnbdWeakMode}'."); |
| } |
| |
| if (nnbdStrongMode && nnbdAgnosticMode) { |
| return throw new CommandLineProblem.deprecated( |
| "Can't specify both '${Flags.nnbdStrongMode}' and " |
| "'${Flags.nnbdAgnosticMode}'."); |
| } |
| |
| if (nnbdWeakMode && nnbdAgnosticMode) { |
| return throw new CommandLineProblem.deprecated( |
| "Can't specify both '${Flags.nnbdWeakMode}' and " |
| "'${Flags.nnbdAgnosticMode}'."); |
| } |
| |
| FileSystem 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 |
| ..omitPlatform = omitPlatform |
| ..verbose = verbose |
| ..verify = verify |
| ..skipPlatformVerification = skipPlatformVerification |
| ..explicitExperimentalFlags = explicitExperimentalFlags |
| ..environmentDefines = environmentDefines |
| ..nnbdMode = nnbdMode |
| ..enableUnscheduledExperiments = enableUnscheduledExperiments |
| ..additionalDills = linkDependencies |
| ..emitDeps = !noDeps |
| ..warnOnReachabilityCheck = warnOnReachabilityCheck |
| ..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, nnbdMode, () { |
| 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)) { |
| ParsedOptions? parsedOptions; |
| ProcessedOptions options; |
| CommandLineProblem? problem; |
| try { |
| parsedOptions = ParsedOptions.parse(arguments, optionSpecification); |
| options = |
| analyzeCommandLine(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(), Severity.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 |
| ? messageFastaUsageLong.problemMessage |
| : messageFastaUsageShort.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_strong.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 [templateUnspecified]. |
| return templateUnspecified.withArguments("$sb"); |
| } |
| |
| Future<T> runProtectedFromAbort<T>(Future<T> Function() action, |
| [T? failingValue]) async { |
| if (CompilerContext.isActive) { |
| throw "runProtectedFromAbort should be called from 'main'," |
| " that is, outside a compiler context."; |
| } |
| 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); |
| } |
| } |