Index: pkg/front_end/tool/_fasta/command_line.dart |
diff --git a/pkg/front_end/tool/_fasta/command_line.dart b/pkg/front_end/tool/_fasta/command_line.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..84162d8ea957f8d834b964e917ce19776a9b8935 |
--- /dev/null |
+++ b/pkg/front_end/tool/_fasta/command_line.dart |
@@ -0,0 +1,366 @@ |
+// 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:front_end/compiler_options.dart' show CompilerOptions; |
+ |
+import 'package:front_end/src/base/processed_options.dart' |
+ show ProcessedOptions; |
+ |
+import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext; |
+ |
+import 'package:front_end/src/fasta/fasta_codes.dart' |
+ show |
+ Message, |
+ templateFastaCLIArgumentRequired, |
+ messageFastaUsageLong, |
+ messageFastaUsageShort, |
+ templateUnspecified; |
+ |
+import 'package:front_end/src/fasta/problems.dart' show unhandled; |
+ |
+import 'package:front_end/src/fasta/severity.dart' show Severity; |
+ |
+import 'package:kernel/target/targets.dart' |
+ show Target, getTarget, TargetFlags, targets; |
+ |
+class CommandLineProblem { |
+ final Message message; |
+ |
+ CommandLineProblem(this.message); |
+ |
+ CommandLineProblem.deprecated(String message) |
+ : this(templateUnspecified.withArguments(message)); |
+} |
+ |
+class ParsedArguments { |
+ final Map<String, dynamic> options = <String, dynamic>{}; |
+ final List<String> arguments = <String>[]; |
+ |
+ toString() => "ParsedArguments($options, $arguments)"; |
+ |
+ /// Parses a list of command-line [arguments] into options and arguments. |
+ /// |
+ /// An /option/ is something that, normally, starts with `-` or `--` (one or |
+ /// two dashes). However, as a special case `/?` and `/h` are also recognized |
+ /// as options for increased compatibility with Windows. An option can have a |
+ /// value. |
+ /// |
+ /// An /argument/ is something that isn't an option, for example, a file name. |
+ /// |
+ /// The specification is a map of options to one of the type literals `Uri`, |
+ /// `int`, `bool`, or `String`, or a comma (`","`) that represents option |
+ /// values of type [Uri], [int], [bool], [String], or a comma-separated list |
+ /// of [String], respectively. |
+ /// |
+ /// If [arguments] contains `"--"`, anything before is parsed as options, and |
+ /// arguments; anything following is treated as arguments (even if starting |
+ /// with, for example, a `-`). |
+ /// |
+ /// Anything that looks like an option is assumed to be a `bool` option set |
+ /// to true, unless it's mentioned in [specification] in which case the |
+ /// option requires a value, either on the form `--option value` or |
+ /// `--option=value`. |
+ /// |
+ /// This method performs only a limited amount of validation, but if an error |
+ /// occurs, it will print [usage] along with a specific error message. |
+ static ParsedArguments parse( |
+ List<String> arguments, Map<String, dynamic> specification) { |
+ specification ??= const <String, dynamic>{}; |
+ ParsedArguments result = new ParsedArguments(); |
+ int index = arguments.indexOf("--"); |
+ Iterable<String> nonOptions = const <String>[]; |
+ Iterator<String> iterator = arguments.iterator; |
+ if (index != -1) { |
+ nonOptions = arguments.skip(index + 1); |
+ iterator = arguments.take(index).iterator; |
+ } |
+ while (iterator.moveNext()) { |
+ String argument = iterator.current; |
+ if (argument.startsWith("-")) { |
+ var valueSpecification = specification[argument]; |
+ String value; |
+ if (valueSpecification != null) { |
+ if (!iterator.moveNext()) { |
+ throw new CommandLineProblem( |
+ templateFastaCLIArgumentRequired.withArguments(argument)); |
+ } |
+ value = iterator.current; |
+ } else { |
+ index = argument.indexOf("="); |
+ if (index != -1) { |
+ value = argument.substring(index + 1); |
+ argument = argument.substring(0, index); |
+ valueSpecification = specification[argument]; |
+ } |
+ } |
+ if (valueSpecification == null) { |
+ if (value != null) { |
+ throw new CommandLineProblem.deprecated( |
+ "Argument '$argument' doesn't take a value: '$value'."); |
+ } |
+ result.options[argument] = true; |
+ } else { |
+ if (valueSpecification is! String && valueSpecification is! Type) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Unrecognized type of value " |
+ "specification: ${valueSpecification.runtimeType}."); |
+ } |
+ switch ("$valueSpecification") { |
+ case ",": |
+ result.options |
+ .putIfAbsent(argument, () => <String>[]) |
+ .addAll(value.split(",")); |
+ break; |
+ |
+ case "int": |
+ case "bool": |
+ case "String": |
+ case "Uri": |
+ if (result.options.containsKey(argument)) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Multiple values for '$argument': " |
+ "'${result.options[argument]}' and '$value'."); |
+ } |
+ var parsedValue; |
+ if (valueSpecification == int) { |
+ parsedValue = int.parse(value, onError: (_) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Value for '$argument', '$value', isn't an int."); |
+ }); |
+ } else if (valueSpecification == bool) { |
+ if (value == "true" || value == "yes") { |
+ parsedValue = true; |
+ } else if (value == "false" || value == "no") { |
+ parsedValue = false; |
+ } else { |
+ return throw new CommandLineProblem.deprecated( |
+ "Value for '$argument' is '$value', " |
+ "but expected one of: 'true', 'false', 'yes', or 'no'."); |
+ } |
+ } else if (valueSpecification == Uri) { |
+ parsedValue = Uri.base.resolve(value); |
+ } else if (valueSpecification == String) { |
+ parsedValue = value; |
+ } else if (valueSpecification is String) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Unrecognized value specification: " |
+ "'$valueSpecification', try using a type literal instead."); |
+ } else { |
+ // All possible cases should have been handled above. |
+ return unhandled("${valueSpecification.runtimeType}", |
+ "CommandLine.parse", -1, null); |
+ } |
+ result.options[argument] = parsedValue; |
+ break; |
+ |
+ default: |
+ return throw new CommandLineProblem.deprecated( |
+ "Unrecognized value specification: '$valueSpecification'."); |
+ } |
+ } |
+ } else if (argument == "/?" || argument == "/h") { |
+ result.options[argument] = true; |
+ } else { |
+ result.arguments.add(argument); |
+ } |
+ } |
+ result.arguments.addAll(nonOptions); |
+ return result; |
+ } |
+} |
+ |
+const Map<String, dynamic> optionSpecification = const <String, dynamic>{ |
+ "--compile-sdk": Uri, |
+ "--fatal": ",", |
+ "--output": Uri, |
+ "-o": Uri, |
+ "--packages": Uri, |
+ "--platform": Uri, |
+ "--sdk": Uri, |
+ "--target": String, |
+ "-t": String, |
+}; |
+ |
+ProcessedOptions analyzeCommandLine( |
+ String programName, |
+ ParsedArguments parsedArguments, |
+ bool areRestArgumentsInputs, |
+ bool verbose) { |
+ final Map<String, dynamic> options = parsedArguments.options; |
+ |
+ final List<String> arguments = parsedArguments.arguments; |
+ |
+ final bool help = options.containsKey("--help") || |
+ options.containsKey("-h") || |
+ options.containsKey("/h") || |
+ options.containsKey("/?"); |
+ |
+ if (help) { |
+ print(computeUsage(programName, verbose).message); |
+ exit(0); |
+ } |
+ |
+ if (options.containsKey("-o") && options.containsKey("--output")) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Can't specify both '-o' and '--output'."); |
+ } |
+ |
+ if (options.containsKey("-t") && options.containsKey("--target")) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Can't specify both '-t' and '--target'."); |
+ } |
+ |
+ if (options.containsKey("--compile-sdk") && |
+ options.containsKey("--platform")) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Can't specify both '--compile-sdk' and '--platform'."); |
+ } |
+ |
+ if (programName == "compile_platform") { |
+ if (arguments.length != 3) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Expected three arguments."); |
+ } |
+ if (options.containsKey("--compile-sdk")) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Cannot specify '--compile-sdk' option to compile_platform."); |
+ } |
+ options['--compile-sdk'] = Uri.base.resolveUri(new Uri.file(arguments[0])); |
+ } else if (arguments.isEmpty) { |
+ return throw new CommandLineProblem.deprecated("No Dart file specified."); |
+ } |
+ |
+ final bool strongMode = options.containsKey("--strong-mode"); |
+ |
+ final String targetName = options["-t"] ?? options["--target"] ?? "vm_fasta"; |
+ |
+ final Target target = |
+ getTarget(targetName, new TargetFlags(strongMode: strongMode)); |
+ if (target == null) { |
+ return throw new CommandLineProblem.deprecated( |
+ "Target '${targetName}' not recognized. " |
+ "Valid targets are:\n ${targets.keys.join("\n ")}"); |
+ } |
+ |
+ final bool verify = options.containsKey("--verify"); |
+ |
+ final bool dumpIr = options.containsKey("--dump-ir"); |
+ |
+ final bool excludeSource = options.containsKey("--exclude-source"); |
+ |
+ final Uri defaultOutput = Uri.base.resolve("${arguments.first}.dill"); |
+ |
+ final Uri output = options["-o"] ?? options["--output"] ?? defaultOutput; |
+ |
+ final Uri platform = options.containsKey("--compile-sdk") |
+ ? null |
+ : options["--platform"] ?? Uri.base.resolve("platform.dill"); |
+ |
+ final Uri packages = options["--packages"]; |
+ |
+ final Uri sdk = options["--sdk"] ?? options["--compile-sdk"]; |
+ |
+ final Set<String> fatal = |
+ new Set<String>.from(options["--fatal"] ?? <String>[]); |
+ |
+ final bool errorsAreFatal = fatal.contains("errors"); |
+ |
+ final bool warningsAreFatal = fatal.contains("warnings"); |
+ |
+ final bool nitsAreFatal = fatal.contains("nits"); |
+ |
+ CompilerOptions compilerOptions = new CompilerOptions() |
+ ..compileSdk = options.containsKey("--compile-sdk") |
+ ..sdkRoot = sdk |
+ ..sdkSummary = platform |
+ ..packagesFileUri = packages |
+ ..strongMode = strongMode |
+ ..target = target |
+ ..throwOnErrorsForDebugging = errorsAreFatal |
+ ..throwOnWarningsForDebugging = warningsAreFatal |
+ ..throwOnNitsForDebugging = nitsAreFatal |
+ ..embedSourceText = !excludeSource |
+ ..debugDump = dumpIr |
+ ..verbose = verbose |
+ ..verify = verify; |
+ |
+ // TODO(ahe): What about chase dependencies? |
+ |
+ var inputs = <Uri>[]; |
+ if (areRestArgumentsInputs) { |
+ inputs = arguments.map(Uri.base.resolve).toList(); |
+ } |
+ return new ProcessedOptions(compilerOptions, false, inputs, output); |
+} |
+ |
+dynamic withGlobalOptions( |
+ String programName, |
+ List<String> arguments, |
+ bool areRestArgumentsInputs, |
+ dynamic f(CompilerContext context, List<String> restArguments)) { |
+ ParsedArguments parsedArguments; |
+ ProcessedOptions options; |
+ bool verbose = true; |
+ CommandLineProblem problem; |
+ try { |
+ parsedArguments = ParsedArguments.parse(arguments, optionSpecification); |
+ verbose = parsedArguments.options.containsKey("-v") || |
+ parsedArguments.options.containsKey("--verbose"); |
+ options = analyzeCommandLine( |
+ programName, parsedArguments, areRestArgumentsInputs, verbose); |
+ } on CommandLineProblem catch (e) { |
+ options = new ProcessedOptions(new CompilerOptions()); |
+ problem = e; |
+ } |
+ |
+ return CompilerContext.runWithOptions(options, (c) { |
+ if (problem != null) { |
+ print(computeUsage(programName, verbose).message); |
+ print(c.formatWithoutLocation(problem.message, Severity.error)); |
+ exit(1); |
+ } |
+ |
+ return f(c, parsedArguments.arguments); |
+ }); |
+} |
+ |
+Message computeUsage(String programName, bool verbose) { |
+ String basicUsage = "Usage: $programName [options] dartfile\n"; |
+ String summary; |
+ String options = |
+ (verbose ? messageFastaUsageLong.message : messageFastaUsageShort.message) |
+ .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] patched_sdk fullOutput " |
+ "outlineOutput\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"); |
+} |