98e29db45c
We want to avoid users passing `--enable-experiment=native-assets` on stable and beta, as we'd like to move fast and break things on the experiment. This aligns the experiment with how the experiment is working in Flutter: main and dev branch only. Before this CL, dartdev did not check experiment flags. Unknown experiments would fail in the VM. After this CL, dartdev checks the experiment flags and errors out early. Change-Id: I875ea3272f4b67342da19ea2e4be329a4b380573 Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-arm64-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-arm64-try,pkg-win-release-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406660 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
130 lines
4.7 KiB
Dart
130 lines
4.7 KiB
Dart
// Copyright (c) 2020, 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';
|
|
|
|
import 'package:analyzer/src/dart/analysis/experiments.dart';
|
|
import 'package:args/args.dart';
|
|
import 'package:collection/collection.dart' show IterableExtension;
|
|
import 'package:dartdev/src/sdk.dart';
|
|
|
|
const experimentFlagName = 'enable-experiment';
|
|
|
|
/// Return a list of all the non-expired Dart experiments.
|
|
List<ExperimentalFeature> get experimentalFeatures =>
|
|
ExperimentStatus.knownFeatures.values
|
|
.where((feature) => !feature.isExpired)
|
|
.toList()
|
|
..sort((a, b) => a.enableString.compareTo(b.enableString));
|
|
|
|
extension ArgParserExtensions on ArgParser {
|
|
void addExperimentalFlags({bool verbose = false}) {
|
|
List<ExperimentalFeature> features = experimentalFeatures;
|
|
|
|
Map<String, String> allowedHelp = {};
|
|
for (ExperimentalFeature feature in features) {
|
|
String suffix =
|
|
feature.isEnabledByDefault ? ' (no-op - enabled by default)' : '';
|
|
allowedHelp[feature.enableString] = '${feature.documentation}$suffix';
|
|
}
|
|
|
|
addMultiOption(
|
|
experimentFlagName,
|
|
valueHelp: 'experiment',
|
|
allowedHelp: verbose ? allowedHelp : null,
|
|
help: 'Enable one or more experimental features '
|
|
'(see dart.dev/go/experiments).',
|
|
hide: !verbose,
|
|
);
|
|
}
|
|
}
|
|
|
|
extension ArgResultsExtensions on ArgResults {
|
|
List<String> get enabledExperiments {
|
|
List<String> enabledExperiments = [];
|
|
// Check to see if the ArgParser which generated this result accepts
|
|
// `--enable-experiment` as an option. If so, return the result if it was
|
|
// provided.
|
|
if (options.contains(experimentFlagName)) {
|
|
if (wasParsed(experimentFlagName)) {
|
|
enabledExperiments = multiOption(experimentFlagName);
|
|
}
|
|
} else {
|
|
// In the case where a command uses `ArgParser.allowAnything()` as its
|
|
// parser, the valid set of options for the command isn't specified and
|
|
// isn't enforced. Instead, we have to manually parse the arguments to
|
|
// look for `--enable-experiment=`. Currently, this path is only taken for
|
|
// the `pub` and `test` commands, as well as when we are trying to send
|
|
// analytics.
|
|
final experiments = arguments.firstWhereOrNull(
|
|
(e) => e.startsWith('--$experimentFlagName='),
|
|
);
|
|
if (experiments == null) {
|
|
return [];
|
|
}
|
|
enabledExperiments = experiments.split('=')[1].split(',');
|
|
}
|
|
|
|
for (final feature in experimentalFeatures) {
|
|
// We allow default true flags, but complain when they are passed in.
|
|
if (feature.isEnabledByDefault &&
|
|
enabledExperiments.contains(feature.enableString)) {
|
|
stderr.writeln("'${feature.enableString}' is now enabled by default; "
|
|
'this flag is no longer required.');
|
|
}
|
|
}
|
|
return enabledExperiments;
|
|
}
|
|
}
|
|
|
|
List<String> parseVmEnabledExperiments(List<String> vmArgs) {
|
|
var experiments = <String>[];
|
|
var itr = vmArgs.iterator;
|
|
while (itr.moveNext()) {
|
|
var arg = itr.current;
|
|
if (arg == '--$experimentFlagName') {
|
|
if (!itr.moveNext()) break;
|
|
experiments.add(itr.current);
|
|
} else if (arg.startsWith('--$experimentFlagName=')) {
|
|
var parts = arg.split('=');
|
|
if (parts.length == 2) {
|
|
experiments.addAll(parts[1].split(','));
|
|
}
|
|
}
|
|
}
|
|
return experiments;
|
|
}
|
|
|
|
bool nativeAssetsEnabled(List<String> vmEnabledExperiments) =>
|
|
vmEnabledExperiments
|
|
.contains(ExperimentalFeatures.native_assets.enableString);
|
|
|
|
bool recordUseEnabled(List<String> vmEnabledExperiments) =>
|
|
vmEnabledExperiments.contains(ExperimentalFeatures.record_use.enableString);
|
|
|
|
List<String> validateExperiments(List<String> vmEnabledExperiments) {
|
|
final errors = <String>[];
|
|
for (final enabledExperiment in vmEnabledExperiments) {
|
|
final experiment = experimentalFeatures.firstWhereOrNull(
|
|
(feature) => feature.enableString == enabledExperiment);
|
|
if (experiment == null) {
|
|
errors.add('Unknown experiment: $enabledExperiment');
|
|
} else if (!_availableOnCurrentChannel(experiment.channels)) {
|
|
final availableChannels = experiment.channels.join(', ');
|
|
final s = experiment.channels.length >= 2 ? 's' : '';
|
|
errors.add(
|
|
'Unavailable experiment: ${experiment.enableString} (this experiment '
|
|
'is only available on the $availableChannels channel$s, '
|
|
'this current channel is ${Runtime.runtime.channel})',
|
|
);
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
bool _availableOnCurrentChannel(List<String> channels) {
|
|
final channel = Runtime.runtime.channel;
|
|
return channels.contains(channel);
|
|
}
|