mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Closes https://github.com/flutter/flutter/issues/167667. In this PR: - `FlutterFeaturesConfig` reads from the global config, current pubspec.yaml, and environment - `FeatureFlags` now delegates "was this configured" to `FlutterFeaturesConfig` - `featureFlags` (`Context`) now is created with knowledge of the current `pubpec.yaml`, if any Once this lands and we play with it a bit (i.e. migrate `disable-swift-package-manager`), we can publicize it and update website documentation (it won't make it into this beta/stable release anyway). I did a significant rewrite of `feature_test`, as lots of what was being tested no longer made sense.
169 lines
5.2 KiB
Dart
169 lines
5.2 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'base/common.dart';
|
|
import 'base/config.dart';
|
|
import 'base/platform.dart';
|
|
import 'features.dart';
|
|
import 'flutter_manifest.dart';
|
|
|
|
/// Reads configuration flags to possibly override the default flag value.
|
|
///
|
|
/// See [isEnabled] for details on how feature flag values are resolved.
|
|
interface class FlutterFeaturesConfig {
|
|
/// Creates a feature configuration reader from the provided sources.
|
|
///
|
|
/// [globalConfig] reads values stored by the `flutter config` tool, which
|
|
/// are normally in the user's `%HOME` directory (varies by system), while
|
|
/// [projectManifest] reads values from the _current_ Flutter project's
|
|
/// `pubspec.yaml`
|
|
const FlutterFeaturesConfig({
|
|
required Config globalConfig,
|
|
required Platform platform,
|
|
required FlutterManifest? projectManifest,
|
|
}) : _globalConfig = globalConfig,
|
|
_platform = platform,
|
|
_projectManifest = projectManifest;
|
|
|
|
final Config _globalConfig;
|
|
final Platform _platform;
|
|
|
|
// Can be null if no manifest file exists in the current directory.
|
|
final FlutterManifest? _projectManifest;
|
|
|
|
/// Returns whether [feature] has been turned on/off from configuration.
|
|
///
|
|
/// If the feature was not configured, or cannot be configured, returns `null`.
|
|
///
|
|
/// The value is resolved, if possible, in the following order, where if a
|
|
/// step resolves to a boolean value, no further steps are attempted:
|
|
///
|
|
///
|
|
/// ## 1. Local Project Configuration
|
|
///
|
|
/// If [Feature.configSetting] is `null`, this step is skipped.
|
|
///
|
|
/// If the value defined by the key `$configSetting` is set in `pubspec.yaml`,
|
|
/// it is returned as a boolean value.
|
|
///
|
|
/// Assuming there is a setting where `configSetting: 'enable-foo'`:
|
|
///
|
|
/// ```yaml
|
|
/// # true
|
|
/// flutter:
|
|
/// config:
|
|
/// enable-foo: true
|
|
///
|
|
/// # false
|
|
/// flutter:
|
|
/// config:
|
|
/// enable-foo: false
|
|
/// ```
|
|
///
|
|
/// ## 2. Global Tool Configuration
|
|
///
|
|
/// If [Feature.configSetting] is `null`, this step is skipped.
|
|
///
|
|
/// If the value defined by the key `$configSetting` is set in the global
|
|
/// (platform dependent) configuration file, it is returned as a boolean
|
|
/// value.
|
|
///
|
|
/// Assuming there is a setting where `configSetting: 'enable-foo'`:
|
|
///
|
|
/// ```sh
|
|
/// # future runs will treat the value as true
|
|
/// flutter config --enable-foo
|
|
///
|
|
/// # future runs will treat the value as false
|
|
/// flutter config --no-enable-foo
|
|
/// ```
|
|
///
|
|
/// ## 3. Environment Variable
|
|
///
|
|
/// If [Feature.environmentOverride] is `null`, this step is skipped.
|
|
///
|
|
/// If the value defined by the key `$environmentOverride` is equal to the
|
|
/// string `'true'` (case insensitive), returns `true`, or `false` otherwise.
|
|
///
|
|
/// Assuming there is a flag where `environmentOverride: 'ENABLE_FOO'`:
|
|
///
|
|
/// ```sh
|
|
/// # true
|
|
/// ENABLE_FOO=true flutter some-command
|
|
///
|
|
/// # true
|
|
/// ENABLE_FOO=TRUE flutter some-command
|
|
///
|
|
/// # false
|
|
/// ENABLE_FOO=false flutter some-command
|
|
///
|
|
/// # false
|
|
/// ENABLE_FOO=any-other-value flutter some-command
|
|
/// ```
|
|
bool? isEnabled(Feature feature) {
|
|
return _isEnabledByConfigValue(feature) ?? _isEnabledByPlatformEnvironment(feature);
|
|
}
|
|
|
|
bool? _isEnabledByConfigValue(Feature feature) {
|
|
// If the feature cannot be configured by local/global config settings, return null.
|
|
final String? featureName = feature.configSetting;
|
|
if (featureName == null) {
|
|
return null;
|
|
}
|
|
return _isEnabledAtProjectLevel(featureName) ?? _isEnabledByGlobalConfig(featureName);
|
|
}
|
|
|
|
bool? _isEnabledByPlatformEnvironment(Feature feature) {
|
|
// If the feature cannot be configured by an environment variable, return null.
|
|
final String? environmentName = feature.environmentOverride;
|
|
if (environmentName == null) {
|
|
return null;
|
|
}
|
|
final Object? environmentValue = _platform.environment[environmentName]?.toLowerCase();
|
|
if (environmentValue == null) {
|
|
return null;
|
|
}
|
|
return environmentValue == 'true';
|
|
}
|
|
|
|
bool? _isEnabledAtProjectLevel(String featureName) {
|
|
final Object? configSection = _projectManifest?.flutterDescriptor['config'];
|
|
if (configSection == null) {
|
|
return null;
|
|
}
|
|
if (configSection is! Map) {
|
|
throwToolExit(
|
|
'The "config" property of "flutter" in pubspec.yaml must be a map, but '
|
|
'got $configSection (${configSection.runtimeType})',
|
|
);
|
|
}
|
|
return _requireBoolOrNull(
|
|
configSection[featureName],
|
|
featureName: featureName,
|
|
source: '"flutter: config:" in pubspec.yaml',
|
|
);
|
|
}
|
|
|
|
bool? _isEnabledByGlobalConfig(String featureName) {
|
|
return _requireBoolOrNull(
|
|
_globalConfig.getValue(featureName),
|
|
featureName: featureName,
|
|
source: '"${_globalConfig.configPath}"',
|
|
);
|
|
}
|
|
|
|
static bool? _requireBoolOrNull(
|
|
Object? value, {
|
|
required String featureName,
|
|
required String source,
|
|
}) {
|
|
if (value is bool?) {
|
|
return value;
|
|
}
|
|
throwToolExit(
|
|
'The "$featureName" property in $source must be a boolean, but got $value (${value.runtimeType})',
|
|
);
|
|
}
|
|
}
|