blob: 57a9485ac3501f2ee63e3b1098d355ae872736d2 [file] [log] [blame]
// Copyright (c) 2019, 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
import 'package:args/args.dart';
import 'package:dwds/dwds.dart';
import 'package:logging/logging.dart';
import '../logging.dart';
import 'shared.dart';
const autoOption = 'auto';
const chromeDebugPortFlag = 'chrome-debug-port';
const debugExtensionFlag = 'debug-extension';
const debugFlag = 'debug';
const enableInjectedClientFlag = 'injected-client';
const hostnameFlag = 'hostname';
const tlsCertChainFlag = 'tls-cert-chain';
const tlsCertKeyFlag = 'tls-cert-key';
const hotReloadFlag = 'hot-reload';
const hotRestartFlag = 'hot-restart';
const launchAppOption = 'launch-app';
const launchInChromeFlag = 'launch-in-chrome';
const userDataDirFlag = 'user-data-dir';
const liveReloadFlag = 'live-reload';
const logRequestsFlag = 'log-requests';
const outputFlag = 'output';
const outputNone = 'NONE';
const releaseFlag = 'release';
const requireBuildWebCompilersFlag = 'build-web-compilers';
const enableExpressionEvaluationFlag = 'enable-expression-evaluation';
const verboseFlag = 'verbose';
const nullSafetyFlag = 'null-safety';
const nullSafetySound = 'sound';
const nullSafetyUnsound = 'unsound';
const nullSafetyAuto = 'auto';
const disableDdsFlag = 'disable-dds';
ReloadConfiguration _parseReloadConfiguration(ArgResults argResults) {
var auto = argResults.options.contains(autoOption)
? argResults[autoOption] as String
: null;
void _handleDeprecatedFlag(String flag, String autoFallback) {
if (argResults.options.contains(flag) &&
argResults.wasParsed(flag) &&
argResults[flag] as bool == true) {
logWriter(
Level.WARNING,
'--$flag is deprecated please use --auto=$autoFallback instead '
'(this has been automatically selected).');
if (auto != null) {
logWriter(
Level.WARNING,
'Only one --auto option is allowed, got $flag which corresponds '
'to --auto=$autoFallback, but already got --auto=$auto');
} else {
auto = autoFallback;
}
}
}
_handleDeprecatedFlag(hotReloadFlag, 'reload');
_handleDeprecatedFlag(hotRestartFlag, 'restart');
_handleDeprecatedFlag(liveReloadFlag, 'refresh');
switch (auto) {
case 'reload':
logWriter(
Level.WARNING,
'Hot reload is not yet supported for web projects. '
'Please try --auto=restart instead.');
return ReloadConfiguration.none;
case 'restart':
return ReloadConfiguration.hotRestart;
case 'refresh':
return ReloadConfiguration.liveReload;
default:
return ReloadConfiguration.none;
}
}
class Configuration {
final bool _autoRun;
final int _chromeDebugPort;
final bool _debugExtension;
final bool _debug;
final bool _enableInjectedClient;
final String _hostname;
final String _tlsCertChain;
final String _tlsCertKey;
final List<String> _launchApps;
final bool _launchInChrome;
final String _userDataDir;
final bool _logRequests;
final String _output;
final String outputInput;
final String outputPath;
final bool _release;
final ReloadConfiguration _reload;
final bool _requireBuildWebCompilers;
final bool _enableExpressionEvaluation;
final bool _verbose;
final bool _disableDds;
final String _nullSafety;
Configuration({
bool autoRun,
int chromeDebugPort,
bool debugExtension,
bool debug,
bool enableInjectedClient,
String hostname,
String tlsCertChain,
String tlsCertKey,
List<String> launchApps,
bool launchInChrome,
String userDataDir,
bool logRequests,
String output,
this.outputInput,
this.outputPath,
ReloadConfiguration reload,
bool release,
bool requireBuildWebCompilers,
bool enableExpressionEvaluation,
bool verbose,
bool disableDds,
String nullSafety,
}) : _autoRun = autoRun,
_chromeDebugPort = chromeDebugPort,
_debugExtension = debugExtension,
_debug = debug,
_enableInjectedClient = enableInjectedClient,
_hostname = hostname,
_tlsCertChain = tlsCertChain,
_tlsCertKey = tlsCertKey,
_launchApps = launchApps,
_launchInChrome = launchInChrome,
_userDataDir = userDataDir,
_logRequests = logRequests,
_output = output,
_release = release,
_reload = reload,
_requireBuildWebCompilers = requireBuildWebCompilers,
_disableDds = disableDds,
_enableExpressionEvaluation = enableExpressionEvaluation,
_verbose = verbose,
_nullSafety = nullSafety {
_validateConfiguration();
}
void _validateConfiguration() {
// Both null and an empty string are valid values for outputInput. For any
// other value, we need to ensure it's a top-level dir.
if (outputInput?.isNotEmpty ?? false) ensureIsTopLevelDir(outputInput);
if ((tlsCertKey != null && tlsCertChain == null) ||
(tlsCertKey == null && tlsCertChain != null)) {
throw InvalidConfiguration(
'Must use --$tlsCertKey with --$tlsCertChain.');
}
if (debug && chromeDebugPort == 0 && !launchInChrome) {
throw InvalidConfiguration(
'Must either use --$chromeDebugPortFlag or --$launchInChromeFlag '
'with --$debugFlag.');
}
// Check that no debugging options were passed if the injected client is
// disabled.
if (!enableInjectedClient) {
if (debug) {
throw InvalidConfiguration(
'--$debugFlag cannot be used with --no-$enableInjectedClientFlag');
}
if (debugExtension) {
throw InvalidConfiguration('--$debugExtensionFlag cannot be used with '
'--no-$enableInjectedClientFlag');
}
if (chromeDebugPort != 0) {
throw InvalidConfiguration('--$chromeDebugPortFlag cannot be used '
'with --no-$enableInjectedClientFlag');
}
}
if (launchApps.isNotEmpty && !launchInChrome) {
throw InvalidConfiguration(
'--$launchAppOption can only be used with --$launchInChromeFlag');
}
if (userDataDir != null && !launchInChrome) {
throw InvalidConfiguration(
'--$userDataDir can only be used with --$launchInChromeFlag');
}
}
/// Creates a new [Configuration] with all non-null fields from
/// [other] overriding the values of `this`.
Configuration _override(Configuration other) => Configuration(
autoRun: other._autoRun ?? _autoRun,
chromeDebugPort: other._chromeDebugPort ?? _chromeDebugPort,
debugExtension: other._debugExtension ?? _debugExtension,
debug: other._debug ?? _debug,
enableInjectedClient:
other._enableInjectedClient ?? _enableInjectedClient,
hostname: other._hostname ?? _hostname,
tlsCertChain: other._tlsCertChain ?? _tlsCertChain,
tlsCertKey: other._tlsCertKey ?? _tlsCertKey,
launchApps: other._launchApps ?? _launchApps,
launchInChrome: other._launchInChrome ?? _launchInChrome,
userDataDir: other._userDataDir ?? _userDataDir,
logRequests: other._logRequests ?? _logRequests,
output: other._output ?? _output,
release: other._release ?? _release,
reload: other._reload ?? _reload,
requireBuildWebCompilers:
other._requireBuildWebCompilers ?? _requireBuildWebCompilers,
disableDds: other._disableDds ?? _disableDds,
enableExpressionEvaluation:
other._enableExpressionEvaluation ?? _enableExpressionEvaluation,
verbose: other._verbose ?? _verbose,
nullSafety: other._nullSafety ?? _nullSafety);
factory Configuration.noInjectedClientDefaults() =>
Configuration(autoRun: false, debug: false, debugExtension: false);
// Whether the application should automatically run when loaded.
bool get autoRun => _autoRun ?? true;
int get chromeDebugPort => _chromeDebugPort ?? 0;
bool get debugExtension => _debugExtension ?? false;
bool get debug => _debug ?? false;
bool get disableDds => _disableDds ?? false;
bool get enableInjectedClient => _enableInjectedClient ?? true;
String get hostname => _hostname ?? 'localhost';
String get tlsCertChain => _tlsCertChain;
String get tlsCertKey => _tlsCertKey;
List<String> get launchApps => _launchApps ?? [];
bool get launchInChrome => _launchInChrome ?? false;
String get userDataDir => _userDataDir;
bool get logRequests => _logRequests ?? false;
String get output => _output ?? outputNone;
bool get release => _release ?? false;
ReloadConfiguration get reload => _reload ?? ReloadConfiguration.none;
bool get requireBuildWebCompilers => _requireBuildWebCompilers ?? true;
bool get enableExpressionEvaluation => _enableExpressionEvaluation ?? true;
bool get verbose => _verbose ?? false;
/// Null safety mode:
///
/// 'sound', 'unsound', or 'auto'.
/// 'auto' indicates that the default `package:build_web_compilers`
/// behavior should be used.
String get nullSafety => _nullSafety;
/// Returns a new configuration with values updated from the parsed args.
static Configuration fromArgs(ArgResults argResults,
{Configuration defaultConfiguration}) {
defaultConfiguration ??= Configuration();
if (argResults == null) return defaultConfiguration;
var enableInjectedClient =
argResults.options.contains(enableInjectedClientFlag)
? argResults[enableInjectedClientFlag] as bool
: defaultConfiguration.enableInjectedClient;
// Change the defaults when we have no injected client to disable all
// debugging features.
//
// After parsing we check these defaults weren't overridden as well.
if (!enableInjectedClient) {
defaultConfiguration = defaultConfiguration
._override(Configuration.noInjectedClientDefaults());
}
var chromeDebugPort = argResults.options.contains(chromeDebugPortFlag)
? int.parse(argResults[chromeDebugPortFlag] as String)
: defaultConfiguration.chromeDebugPort;
var debugExtension = argResults.options.contains(debugExtensionFlag)
? argResults[debugExtensionFlag] as bool
: defaultConfiguration.debugExtension;
var debug = argResults.options.contains(debugFlag)
? argResults[debugFlag] as bool
: defaultConfiguration.debug;
var hostname = argResults.options.contains(hostnameFlag)
? argResults[hostnameFlag] as String
: defaultConfiguration.hostname;
var tlsCertChain = argResults.options.contains(tlsCertChainFlag)
? argResults[tlsCertChainFlag] as String
: defaultConfiguration.tlsCertChain;
var tlsCertKey = argResults.options.contains(tlsCertKeyFlag)
? argResults[tlsCertKeyFlag] as String
: defaultConfiguration.tlsCertKey;
var launchApps = argResults.options.contains(launchAppOption) &&
argResults.wasParsed(launchAppOption)
? argResults[launchAppOption] as List<String>
: defaultConfiguration.launchApps;
var launchInChrome = argResults.options.contains(launchInChromeFlag) &&
argResults.wasParsed(launchInChromeFlag)
? argResults[launchInChromeFlag] as bool
// We want to default to launch chrome if the user provides just --debug
// and not --chrome-debug-port.
: debug &&
!(argResults.options.contains(launchInChromeFlag) &&
argResults.wasParsed(chromeDebugPortFlag))
? true
: defaultConfiguration.launchInChrome;
var userDataDir = argResults.options.contains(userDataDirFlag)
? argResults[userDataDirFlag] as String
: defaultConfiguration.userDataDir;
var logRequests = argResults.options.contains(logRequestsFlag)
? argResults[logRequestsFlag] as bool
: defaultConfiguration.logRequests;
var output = argResults.options.contains(outputFlag)
? argResults[outputFlag] as String
: defaultConfiguration.output;
String outputPath;
String outputInput;
if (output == 'NONE') {
// The empty string means build everything.
outputInput = '';
} else {
var splitOutput = output.split(':');
if (splitOutput.length == 1) {
// The empty string means build everything.
outputInput = '';
outputPath = output;
} else {
outputInput = splitOutput.first;
outputPath = splitOutput.skip(1).join(':');
}
}
var release = argResults.options.contains(releaseFlag)
? argResults[releaseFlag] as bool
: defaultConfiguration.release;
var requireBuildWebCompilers =
argResults.options.contains(requireBuildWebCompilersFlag)
? argResults[requireBuildWebCompilersFlag] as bool
: defaultConfiguration.requireBuildWebCompilers;
var enableExpressionEvaluation =
argResults.options.contains(enableExpressionEvaluationFlag)
? argResults[enableExpressionEvaluationFlag] as bool
: defaultConfiguration.enableExpressionEvaluation;
var verbose = argResults.options.contains(verboseFlag)
? argResults[verboseFlag] as bool
: defaultConfiguration.verbose;
var nullSafety = argResults.options.contains(nullSafetyFlag)
? argResults[nullSafetyFlag] as String
: defaultConfiguration.nullSafety;
var disableDds = argResults.options.contains(disableDdsFlag)
? argResults[disableDdsFlag] as bool
: defaultConfiguration.disableDds;
return Configuration(
autoRun: defaultConfiguration.autoRun,
chromeDebugPort: chromeDebugPort,
debugExtension: debugExtension,
debug: debug,
enableInjectedClient: enableInjectedClient,
hostname: hostname,
tlsCertChain: tlsCertChain,
tlsCertKey: tlsCertKey,
launchApps: launchApps,
launchInChrome: launchInChrome,
userDataDir: userDataDir,
logRequests: logRequests,
output: output,
outputInput: outputInput,
outputPath: outputPath,
release: release,
reload: _parseReloadConfiguration(argResults),
requireBuildWebCompilers: requireBuildWebCompilers,
disableDds: disableDds,
enableExpressionEvaluation: enableExpressionEvaluation,
verbose: verbose,
nullSafety: nullSafety,
);
}
}
class InvalidConfiguration implements Exception {
final String details;
InvalidConfiguration(this.details);
@override
String toString() {
return 'Invalid configuration: $details';
}
}