93f4ac0a83
The CFEs FormattedMessage always had two getters to get the text inside one that would supposedly give an ansi formated version of the message and one that would supposedly give a plaintext formated version of the message. They both returned the same string, though, which would either be with ansi escape codes or plain text depending on the environment at compile time. This CL fixes that by having both messages, and letting the reporting (i.e. whenever the message is read) decide which to use. That way we can - for instance - report errors with color if the terminal supports it correctly when reusing a dill (and reissuing problems, but where the terminal support changes) and if printing the problem to an html <pre> field (like observatory does). It also cleans up two different implementations of whether we think the terminal supports colors or not, by deleting one of them. This is the second try. Patchset #1 is the original. Patchset #2(and possibly beyond) is the changes. TEST=Existing test suites. Change-Id: I8e483049ce81ce1bd8e5396b588a31e0ad3a8630 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/187402 Commit-Queue: Jens Johansen <jensj@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
662 lines
22 KiB
Dart
662 lines
22 KiB
Dart
// 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.
|
|
|
|
// @dart=2.9
|
|
|
|
library fasta.tool.command_line;
|
|
|
|
import 'dart:io' show exit;
|
|
|
|
import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
|
|
|
|
import 'package:build_integration/file_system/single_root.dart'
|
|
show SingleRootFileSystem;
|
|
|
|
import 'package:front_end/src/api_prototype/compiler_options.dart'
|
|
show CompilerOptions, parseExperimentalFlags;
|
|
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,
|
|
templateFastaCLIArgumentRequired,
|
|
templateUnspecified;
|
|
|
|
import 'package:front_end/src/fasta/problems.dart' show DebugAbort;
|
|
|
|
import 'package:front_end/src/fasta/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 LateLowering, 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>[];
|
|
final Map<String, String> defines = <String, 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 following values:
|
|
/// * the type literal `Uri`, representing an option value of type [Uri],
|
|
/// * the type literal `int`, representing an option value of type [int],
|
|
/// * the bool literal `false`, representing a boolean option that is turned
|
|
/// off by default,
|
|
/// * the bool literal `true, representing a boolean option that is turned on
|
|
/// by default,
|
|
/// * or the string literal `","`, representing a comma-separated list of
|
|
/// values.
|
|
///
|
|
/// If [arguments] contains `"--"`, anything before is parsed as options, and
|
|
/// arguments; anything following is treated as arguments (even if starting
|
|
/// with, for example, a `-`).
|
|
///
|
|
/// If an option isn't found in [specification], an error is thrown.
|
|
///
|
|
/// Boolean options do not require an option value, but an optional value can
|
|
/// be provided using the forms `--option=value` where `value` can be `true`
|
|
/// or `yes` to turn on the option, or `false` or `no` to turn it off. If no
|
|
/// option value is specified, a boolean option is turned on.
|
|
///
|
|
/// All other options require an option value, either on the form `--option
|
|
/// value` or `--option=value`.
|
|
static ParsedArguments parse(
|
|
List<String> arguments, Map<String, ValueSpecification> specification) {
|
|
specification ??= const <String, ValueSpecification>{};
|
|
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("-") || argument == "/?" || argument == "/h") {
|
|
String value;
|
|
if (argument.startsWith("-D")) {
|
|
value = argument.substring("-D".length);
|
|
argument = "-D";
|
|
} else {
|
|
index = argument.indexOf("=");
|
|
if (index != -1) {
|
|
value = argument.substring(index + 1);
|
|
argument = argument.substring(0, index);
|
|
}
|
|
}
|
|
ValueSpecification valueSpecification = specification[argument];
|
|
if (valueSpecification == null) {
|
|
throw new CommandLineProblem.deprecated(
|
|
"Unknown option '$argument'.");
|
|
}
|
|
String canonicalArgument = argument;
|
|
if (valueSpecification.alias != null) {
|
|
canonicalArgument = valueSpecification.alias;
|
|
valueSpecification = specification[valueSpecification.alias];
|
|
}
|
|
if (valueSpecification == null) {
|
|
throw new CommandLineProblem.deprecated(
|
|
"Unknown option alias '$canonicalArgument'.");
|
|
}
|
|
final bool requiresValue = valueSpecification.requiresValue;
|
|
if (requiresValue && value == null) {
|
|
if (!iterator.moveNext()) {
|
|
throw new CommandLineProblem(
|
|
templateFastaCLIArgumentRequired.withArguments(argument));
|
|
}
|
|
value = iterator.current;
|
|
}
|
|
valueSpecification.processValue(
|
|
result, canonicalArgument, argument, value);
|
|
} else {
|
|
result.arguments.add(argument);
|
|
}
|
|
}
|
|
specification.forEach((String key, ValueSpecification value) {
|
|
if (value.defaultValue != null) {
|
|
result.options[key] ??= value.defaultValue;
|
|
}
|
|
});
|
|
result.arguments.addAll(nonOptions);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Before adding new options here, you must:
|
|
// * Document the option.
|
|
// * Get an explicit approval from the front-end team.
|
|
const Map<String, ValueSpecification> optionSpecification =
|
|
const <String, ValueSpecification>{
|
|
Flags.compileSdk: const UriValue(),
|
|
Flags.dumpIr: const BoolValue(false),
|
|
Flags.enableExperiment: const StringListValue(),
|
|
Flags.excludeSource: const BoolValue(false),
|
|
Flags.omitPlatform: const BoolValue(false),
|
|
Flags.fatal: const StringListValue(),
|
|
Flags.fatalSkip: const StringValue(),
|
|
Flags.forceLateLowering: const BoolValue(false),
|
|
Flags.forceStaticFieldLowering: const BoolValue(false),
|
|
Flags.forceNoExplicitGetterCalls: const BoolValue(false),
|
|
Flags.help: const BoolValue(false),
|
|
Flags.librariesJson: const UriValue(),
|
|
Flags.noDefines: const BoolValue(false),
|
|
Flags.output: const UriValue(),
|
|
Flags.packages: const UriValue(),
|
|
Flags.platform: const UriValue(),
|
|
Flags.sdk: const UriValue(),
|
|
Flags.singleRootBase: const UriValue(),
|
|
Flags.singleRootScheme: const StringValue(),
|
|
Flags.nnbdWeakMode: const BoolValue(false),
|
|
Flags.nnbdStrongMode: const BoolValue(false),
|
|
Flags.nnbdAgnosticMode: const BoolValue(false),
|
|
Flags.target: const StringValue(),
|
|
Flags.verbose: const BoolValue(false),
|
|
Flags.verbosity: const StringValue(),
|
|
Flags.verify: const BoolValue(false),
|
|
Flags.skipPlatformVerification: const BoolValue(false),
|
|
Flags.warnOnReachabilityCheck: const BoolValue(false),
|
|
Flags.linkDependencies: const UriListValue(),
|
|
Flags.noDeps: const BoolValue(false),
|
|
Flags.invocationModes: const StringValue(),
|
|
"-D": const DefineValue(),
|
|
"--define": const AliasValue("-D"),
|
|
"-h": const AliasValue(Flags.help),
|
|
"--out": const AliasValue(Flags.output),
|
|
"-o": const AliasValue(Flags.output),
|
|
"-t": const AliasValue(Flags.target),
|
|
"-v": const AliasValue(Flags.verbose),
|
|
"/?": const AliasValue(Flags.help),
|
|
"/h": const AliasValue(Flags.help),
|
|
};
|
|
|
|
void throwCommandLineProblem(String message) {
|
|
throw new CommandLineProblem.deprecated(message);
|
|
}
|
|
|
|
ProcessedOptions analyzeCommandLine(String programName,
|
|
ParsedArguments parsedArguments, bool areRestArgumentsInputs) {
|
|
final Map<String, dynamic> options = parsedArguments.options;
|
|
|
|
final List<String> arguments = parsedArguments.arguments;
|
|
|
|
final bool help = options[Flags.help];
|
|
|
|
final bool verbose = options[Flags.verbose];
|
|
|
|
if (help) {
|
|
print(computeUsage(programName, verbose).message);
|
|
exit(0);
|
|
}
|
|
|
|
if (options.containsKey(Flags.compileSdk) &&
|
|
options.containsKey(Flags.platform)) {
|
|
return throw new CommandLineProblem.deprecated(
|
|
"Can't specify both '${Flags.compileSdk}' and '${Flags.platform}'.");
|
|
}
|
|
|
|
final String targetName = options[Flags.target] ?? "vm";
|
|
|
|
Map<ExperimentalFlag, bool> explicitExperimentalFlags =
|
|
parseExperimentalFlags(
|
|
parseExperimentalArguments(options[Flags.enableExperiment]),
|
|
onError: throwCommandLineProblem,
|
|
onWarning: print);
|
|
|
|
final TargetFlags flags = new TargetFlags(
|
|
forceLateLoweringsForTesting: options[Flags.forceLateLowering]
|
|
? LateLowering.all
|
|
: LateLowering.none,
|
|
forceStaticFieldLoweringForTesting:
|
|
options[Flags.forceStaticFieldLowering],
|
|
forceNoExplicitGetterCallsForTesting:
|
|
options[Flags.forceNoExplicitGetterCalls],
|
|
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[Flags.noDefines];
|
|
|
|
final bool noDeps = options[Flags.noDeps];
|
|
|
|
final bool verify = options[Flags.verify];
|
|
|
|
final bool skipPlatformVerification = options[Flags.skipPlatformVerification];
|
|
|
|
final bool dumpIr = options[Flags.dumpIr];
|
|
|
|
final bool excludeSource = options[Flags.excludeSource];
|
|
|
|
final bool omitPlatform = options[Flags.omitPlatform];
|
|
|
|
final Uri packages = options[Flags.packages];
|
|
|
|
final Set<String> fatal =
|
|
new Set<String>.from(options[Flags.fatal] ?? <String>[]);
|
|
|
|
final bool errorsAreFatal = fatal.contains("errors");
|
|
|
|
final bool warningsAreFatal = fatal.contains("warnings");
|
|
|
|
final int fatalSkip = int.tryParse(options[Flags.fatalSkip] ?? "0") ?? -1;
|
|
|
|
final bool compileSdk = options.containsKey(Flags.compileSdk);
|
|
|
|
final String singleRootScheme = options[Flags.singleRootScheme];
|
|
final Uri singleRootBase = options[Flags.singleRootBase];
|
|
|
|
final bool nnbdStrongMode = options[Flags.nnbdStrongMode];
|
|
|
|
final bool nnbdWeakMode = options[Flags.nnbdWeakMode];
|
|
|
|
final bool nnbdAgnosticMode = options[Flags.nnbdAgnosticMode];
|
|
|
|
final NnbdMode nnbdMode = nnbdAgnosticMode
|
|
? NnbdMode.Agnostic
|
|
: (nnbdStrongMode ? NnbdMode.Strong : NnbdMode.Weak);
|
|
|
|
final bool warnOnReachabilityCheck = options[Flags.warnOnReachabilityCheck];
|
|
|
|
final List<Uri> linkDependencies = options[Flags.linkDependencies] ?? [];
|
|
|
|
final String invocationModes = options[Flags.invocationModes] ?? '';
|
|
|
|
final String verbosity = options[Flags.verbosity] ?? Verbosity.defaultValue;
|
|
|
|
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),
|
|
});
|
|
}
|
|
|
|
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 = noDefines ? null : parsedArguments.defines
|
|
..nnbdMode = nnbdMode
|
|
..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 (options.containsKey(Flags.output)) {
|
|
return throw new CommandLineProblem.deprecated(
|
|
"Cannot specify '${Flags.output}' option to compile_platform.");
|
|
}
|
|
|
|
return new ProcessedOptions(
|
|
options: compilerOptions
|
|
..sdkSummary = options[Flags.platform]
|
|
..librariesSpecificationUri = resolveInputUri(arguments[1])
|
|
..setExitCodeOnProblem = true,
|
|
inputs: <Uri>[Uri.parse(arguments[0])],
|
|
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[Flags.output] ?? defaultOutput;
|
|
|
|
final Uri sdk = options[Flags.sdk] ?? options[Flags.compileSdk];
|
|
|
|
final Uri librariesJson = options[Flags.librariesJson];
|
|
|
|
final Uri platform = compileSdk
|
|
? null
|
|
: (options[Flags.platform] ??
|
|
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)) {
|
|
ParsedArguments parsedArguments;
|
|
ProcessedOptions options;
|
|
CommandLineProblem problem;
|
|
try {
|
|
parsedArguments = ParsedArguments.parse(arguments, optionSpecification);
|
|
options = analyzeCommandLine(
|
|
programName, parsedArguments, 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).message);
|
|
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, parsedArguments.arguments);
|
|
}, errorOnMissingInput: problem == null);
|
|
}
|
|
|
|
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]"
|
|
" 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.message);
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
abstract class ValueSpecification {
|
|
const ValueSpecification();
|
|
|
|
String get alias => null;
|
|
|
|
dynamic get defaultValue => null;
|
|
|
|
bool get requiresValue => true;
|
|
|
|
void processValue(ParsedArguments result, String canonicalArgument,
|
|
String argument, String value);
|
|
}
|
|
|
|
class AliasValue extends ValueSpecification {
|
|
final String alias;
|
|
|
|
const AliasValue(this.alias);
|
|
|
|
bool get requiresValue =>
|
|
throw new UnsupportedError("AliasValue.requiresValue");
|
|
|
|
void processValue(ParsedArguments result, String canonicalArgument,
|
|
String argument, String value) {
|
|
throw new UnsupportedError("AliasValue.processValue");
|
|
}
|
|
}
|
|
|
|
class UriValue extends ValueSpecification {
|
|
const UriValue();
|
|
|
|
void processValue(ParsedArguments result, String canonicalArgument,
|
|
String argument, String value) {
|
|
if (result.options.containsKey(canonicalArgument)) {
|
|
throw new CommandLineProblem.deprecated(
|
|
"Multiple values for '$argument': "
|
|
"'${result.options[canonicalArgument]}' and '$value'.");
|
|
}
|
|
// TODO(ahe): resolve Uris lazily, so that schemes provided by
|
|
// other flags can be used for parsed command-line arguments too.
|
|
result.options[canonicalArgument] = resolveInputUri(value);
|
|
}
|
|
}
|
|
|
|
class StringValue extends ValueSpecification {
|
|
const StringValue();
|
|
|
|
void processValue(ParsedArguments result, String canonicalArgument,
|
|
String argument, String value) {
|
|
if (result.options.containsKey(canonicalArgument)) {
|
|
throw new CommandLineProblem.deprecated(
|
|
"Multiple values for '$argument': "
|
|
"'${result.options[canonicalArgument]}' and '$value'.");
|
|
}
|
|
result.options[canonicalArgument] = value;
|
|
}
|
|
}
|
|
|
|
class BoolValue extends ValueSpecification {
|
|
final bool defaultValue;
|
|
|
|
const BoolValue(this.defaultValue);
|
|
|
|
bool get requiresValue => false;
|
|
|
|
void processValue(ParsedArguments result, String canonicalArgument,
|
|
String argument, String value) {
|
|
if (result.options.containsKey(canonicalArgument)) {
|
|
throw new CommandLineProblem.deprecated(
|
|
"Multiple values for '$argument': "
|
|
"'${result.options[canonicalArgument]}' and '$value'.");
|
|
}
|
|
bool parsedValue;
|
|
if (value == null || value == "true" || value == "yes") {
|
|
parsedValue = true;
|
|
} else if (value == "false" || value == "no") {
|
|
parsedValue = false;
|
|
} else {
|
|
throw new CommandLineProblem.deprecated(
|
|
"Value for '$argument' is '$value', "
|
|
"but expected one of: 'true', 'false', 'yes', or 'no'.");
|
|
}
|
|
result.options[canonicalArgument] = parsedValue;
|
|
}
|
|
}
|
|
|
|
class IntValue extends ValueSpecification {
|
|
const IntValue();
|
|
|
|
void processValue(ParsedArguments result, String canonicalArgument,
|
|
String argument, String value) {
|
|
if (result.options.containsKey(canonicalArgument)) {
|
|
throw new CommandLineProblem.deprecated(
|
|
"Multiple values for '$argument': "
|
|
"'${result.options[canonicalArgument]}' and '$value'.");
|
|
}
|
|
int parsedValue = int.tryParse(value);
|
|
if (parsedValue == null) {
|
|
throw new CommandLineProblem.deprecated(
|
|
"Value for '$argument', '$value', isn't an int.");
|
|
}
|
|
result.options[canonicalArgument] = parsedValue;
|
|
}
|
|
}
|
|
|
|
class DefineValue extends ValueSpecification {
|
|
const DefineValue();
|
|
|
|
void processValue(ParsedArguments result, String canonicalArgument,
|
|
String argument, String value) {
|
|
int index = value.indexOf('=');
|
|
String name;
|
|
String expression;
|
|
if (index != -1) {
|
|
name = value.substring(0, index);
|
|
expression = value.substring(index + 1);
|
|
} else {
|
|
name = value;
|
|
expression = value;
|
|
}
|
|
result.defines[name] = expression;
|
|
}
|
|
}
|
|
|
|
class StringListValue extends ValueSpecification {
|
|
const StringListValue();
|
|
|
|
void processValue(ParsedArguments result, String canonicalArgument,
|
|
String argument, String value) {
|
|
result.options
|
|
.putIfAbsent(canonicalArgument, () => <String>[])
|
|
.addAll(value.split(","));
|
|
}
|
|
}
|
|
|
|
class UriListValue extends ValueSpecification {
|
|
const UriListValue();
|
|
|
|
void processValue(ParsedArguments result, String canonicalArgument,
|
|
String argument, String value) {
|
|
result.options
|
|
.putIfAbsent(canonicalArgument, () => <Uri>[])
|
|
.addAll(value.split(",").map(resolveInputUri));
|
|
}
|
|
}
|