Reland "Make Flutter version information accessible at runtime (#140783)" (#163761)

Reverts https://github.com/flutter/flutter/pull/163753
Relands https://github.com/flutter/flutter/pull/140783

Original PR description below

------

This makes various Flutter version information available at runtime.
It's basically the same as executing `flutter --version`. This is
especially useful for tools like Crashlytics or Sentry (see for example
https://github.com/getsentry/sentry-dart/issues/416).

Usage example:
```dart
FlutterVersion.version;           // 3.16.5
FlutterVersion.channel;           // stable
FlutterVersion.gitUrl;            // https://github.com/flutter/flutter.git
FlutterVersion.frameworkRevision; // 78666c8dc5
FlutterVersion.engineRevision;    // 3f3e560236
FlutterVersion.dartVersion;       // 3.2.3
```

This approach has prior art as seen in #134179.

Fixes https://github.com/flutter/flutter/issues/61814

<!-- *If you had to change anything in the [flutter/tests] repo, include
a link to the migration guide as per the [breaking change policy].* -->

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
[test-exempt]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
[Discord]: https://github.com/flutter/flutter/wiki/Chat
This commit is contained in:
Jonas Uekötter 2025-02-24 21:17:08 +01:00 committed by GitHub
parent 2bf32d855e
commit a8d1b62382
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 386 additions and 9 deletions

View File

@ -20,6 +20,7 @@ export 'src/services/clipboard.dart';
export 'src/services/debug.dart';
export 'src/services/deferred_component.dart';
export 'src/services/flavor.dart';
export 'src/services/flutter_version.dart';
export 'src/services/font_loader.dart';
export 'src/services/haptic_feedback.dart';
export 'src/services/hardware_keyboard.dart';

View File

@ -0,0 +1,49 @@
// 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.
// @docImport 'dart:io'
/// Details about the Flutter version this app was compiled with,
/// corresponding to the output of `flutter --version`.
///
/// When this Flutter version was build from a fork, or when Flutter runs in a
/// custom embedder, these values might be unreliable.
///
/// See also:
/// - [Platform.version]
abstract final class FlutterVersion {
const FlutterVersion._();
/// The Flutter version used to compile the app.
static const String? version =
bool.hasEnvironment('FLUTTER_VERSION') ? String.fromEnvironment('FLUTTER_VERSION') : null;
/// The Flutter channel used to compile the app.
static const String? channel =
bool.hasEnvironment('FLUTTER_CHANNEL') ? String.fromEnvironment('FLUTTER_CHANNEL') : null;
/// The URL of the Git repository from which Flutter was obtained.
static const String? gitUrl =
bool.hasEnvironment('FLUTTER_GIT_URL') ? String.fromEnvironment('FLUTTER_GIT_URL') : null;
/// The Flutter framework revision, as a (short) Git commit ID.
static const String? frameworkRevision =
bool.hasEnvironment('FLUTTER_FRAMEWORK_REVISION')
? String.fromEnvironment('FLUTTER_FRAMEWORK_REVISION')
: null;
/// The Flutter engine revision.
static const String? engineRevision =
bool.hasEnvironment('FLUTTER_ENGINE_REVISION')
? String.fromEnvironment('FLUTTER_ENGINE_REVISION')
: null;
// This is included since [Platform.version](https://api.dart.dev/stable/dart-io/Platform/version.html)
// is not included on web platforms.
/// The Dart version used to compile the app.
static const String? dartVersion =
bool.hasEnvironment('FLUTTER_DART_VERSION')
? String.fromEnvironment('FLUTTER_DART_VERSION')
: null;
}

View File

@ -0,0 +1,35 @@
// 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 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('FlutterVersion.version contains the current version', () {
expect(FlutterVersion.version, const String.fromEnvironment('FLUTTER_VERSION'));
});
test('FlutterVersion.channel contains the current channel', () {
expect(FlutterVersion.channel, const String.fromEnvironment('FLUTTER_CHANNEL'));
});
test('FlutterVersion.gitUrl contains the current git URL', () {
expect(FlutterVersion.gitUrl, const String.fromEnvironment('FLUTTER_GIT_URL'));
});
test('FlutterVersion.frameworkRevision contains the current framework revision', () {
expect(
FlutterVersion.frameworkRevision,
const String.fromEnvironment('FLUTTER_FRAMEWORK_REVISION'),
);
});
test('FlutterVersion.engineRevision contains the current engine revision', () {
expect(FlutterVersion.engineRevision, const String.fromEnvironment('FLUTTER_ENGINE_REVISION'));
});
test('FlutterVersion.dartVersion contains the current Dart version', () {
expect(FlutterVersion.dartVersion, const String.fromEnvironment('FLUTTER_DART_VERSION'));
});
}

View File

@ -30,6 +30,7 @@ import '../globals.dart' as globals;
import '../project.dart';
import '../reporting/reporting.dart';
import '../reporting/unified_analytics.dart';
import '../version.dart';
import 'flutter_command_runner.dart';
import 'target_devices.dart';
@ -182,6 +183,41 @@ abstract class FlutterCommand extends Command<void> {
/// The flag name for whether or not to use ipv6.
static const String ipv6Flag = 'ipv6';
/// The dart define used for adding the Flutter version at runtime.
@visibleForTesting
static const String flutterVersionDefine = 'FLUTTER_VERSION';
/// The dart define used for adding the Flutter channel at runtime.
@visibleForTesting
static const String flutterChannelDefine = 'FLUTTER_CHANNEL';
/// The dart define used for adding the Flutter git URL at runtime.
@visibleForTesting
static const String flutterGitUrlDefine = 'FLUTTER_GIT_URL';
/// The dart define used for adding the Flutter framework revision at runtime.
@visibleForTesting
static const String flutterFrameworkRevisionDefine = 'FLUTTER_FRAMEWORK_REVISION';
/// The dart define used for adding the Flutter engine revision at runtime.
@visibleForTesting
static const String flutterEngineRevisionDefine = 'FLUTTER_ENGINE_REVISION';
/// The dart define used for adding the Dart version at runtime.
@visibleForTesting
static const String flutterDartVersionDefine = 'FLUTTER_DART_VERSION';
/// List of all dart defines used for adding Flutter version information at runtime
@visibleForTesting
static const List<String> flutterVersionDartDefines = <String>[
flutterVersionDefine,
flutterChannelDefine,
flutterGitUrlDefine,
flutterFrameworkRevisionDefine,
flutterEngineRevisionDefine,
flutterDartVersionDefine,
];
@override
ArgParser get argParser => _argParser;
final ArgParser _argParser = ArgParser(
@ -1419,6 +1455,8 @@ abstract class FlutterCommand extends Command<void> {
final String? cliFlavor = argParser.options.containsKey('flavor') ? stringArg('flavor') : null;
final String? flavor = cliFlavor ?? defaultFlavor;
_addFlutterVersionToDartDefines(globals.flutterVersion, dartDefines);
return BuildInfo(
buildMode,
flavor,
@ -1456,6 +1494,34 @@ abstract class FlutterCommand extends Command<void> {
);
}
// This adds the Dart defines used to access various Flutter version information at runtime.
void _addFlutterVersionToDartDefines(FlutterVersion version, List<String> dartDefines) {
for (final String dartDefine in flutterVersionDartDefines) {
if (globals.platform.environment[dartDefine] != null) {
throwToolExit(
'$dartDefine is used by the framework and cannot be set in the environment. '
'Use FlutterVersion to access it in Flutter code',
);
}
if (dartDefines.any((String define) => define.startsWith(dartDefine))) {
throwToolExit(
'$dartDefine is used by the framework and cannot be '
'set using --${FlutterOptions.kDartDefinesOption} or --${FlutterOptions.kDartDefineFromFileOption}. '
'Use FlutterVersion to access it in Flutter code',
);
}
}
dartDefines.addAll(<String>[
'$flutterVersionDefine=${version.frameworkVersion}',
'$flutterChannelDefine=${version.channel}',
'$flutterGitUrlDefine=${version.repositoryUrl}',
'$flutterFrameworkRevisionDefine=${version.frameworkRevisionShort}',
'$flutterEngineRevisionDefine=${version.engineRevisionShort}',
'$flutterDartVersionDefine=${version.dartSdkVersion}',
]);
}
void setupApplicationPackages() {
applicationPackages ??= ApplicationPackageFactory.instance;
}

View File

@ -102,7 +102,7 @@ flutter:
'-I=/flutter/packages/flutter_tools/gradle/aar_init_script.gradle',
...List<RegExp>.filled(4, RegExp(r'-P[a-zA-Z-]+=.*')),
'-q',
...List<RegExp>.filled(5, RegExp(r'-P[a-zA-Z-]+=.*')),
...List<RegExp>.filled(6, RegExp(r'-P[a-zA-Z-]+=.*')),
'assembleAar$buildMode',
],
onRun: (_) => fs.directory('/build/host/outputs/repo').createSync(recursive: true),

View File

@ -651,7 +651,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
'set(FLUTTER_VERSION_MINOR 0 PARENT_SCOPE)',
'set(FLUTTER_VERSION_PATCH 0 PARENT_SCOPE)',
'set(FLUTTER_VERSION_BUILD 0 PARENT_SCOPE)',
' "DART_DEFINES=Zm9vLmJhcj0y,Zml6ei5mYXI9Mw=="',
' "DART_DEFINES=Zm9vLmJhcj0y,Zml6ei5mYXI9Mw==,RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI="',
' "DART_OBFUSCATION=true"',
' "EXTRA_FRONT_END_OPTIONS=--enable-experiment=non-nullable"',
' "EXTRA_GEN_SNAPSHOT_OPTIONS=--enable-experiment=non-nullable"',

View File

@ -485,7 +485,7 @@ STDERR STUFF
'FLUTTER_BUILD_DIR=build',
'FLUTTER_BUILD_NAME=1.0.0',
'FLUTTER_BUILD_NUMBER=1',
'DART_DEFINES=Zm9vLmJhcj0y,Zml6ei5mYXI9Mw==',
'DART_DEFINES=Zm9vLmJhcj0y,Zml6ei5mYXI9Mw==,RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'DART_OBFUSCATION=true',
'EXTRA_FRONT_END_OPTIONS=--enable-experiment=non-nullable',
'EXTRA_GEN_SNAPSHOT_OPTIONS=--enable-experiment=non-nullable',

View File

@ -143,7 +143,8 @@ void main() {
'HasWebPlugins': 'true',
'ServiceWorkerStrategy': 'offline-first',
'BuildMode': 'release',
'DartDefines': 'Zm9vPWE=',
'DartDefines':
'Zm9vPWE=,RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'DartObfuscation': 'false',
'TrackWidgetCreation': 'false',
'TreeShakeIcons': 'true',
@ -204,7 +205,8 @@ void main() {
'Dart2jsNoFrequencyBasedMinification': 'false',
'Dart2jsOptimization': 'O3',
'BuildMode': 'release',
'DartDefines': 'Zm9vPWE=,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==',
'DartDefines':
'Zm9vPWE=,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'DartObfuscation': 'false',
'TrackWidgetCreation': 'false',
'TreeShakeIcons': 'true',
@ -258,6 +260,8 @@ void main() {
'HasWebPlugins': 'true',
'ServiceWorkerStrategy': 'offline-first',
'BuildMode': 'release',
'DartDefines':
'RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'DartObfuscation': 'false',
'TrackWidgetCreation': 'false',
'TreeShakeIcons': 'true',

View File

@ -568,7 +568,7 @@ if %errorlevel% neq 0 goto :VCEnd</Command>
r'set(FLUTTER_VERSION_MINOR 0 PARENT_SCOPE)',
r'set(FLUTTER_VERSION_PATCH 0 PARENT_SCOPE)',
r'set(FLUTTER_VERSION_BUILD 0 PARENT_SCOPE)',
r' "DART_DEFINES=Zm9vPWE=,YmFyPWI="',
r' "DART_DEFINES=Zm9vPWE=,YmFyPWI=,RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI="',
r' "DART_OBFUSCATION=true"',
r' "EXTRA_FRONT_END_OPTIONS=--enable-experiment=non-nullable"',
r' "EXTRA_GEN_SNAPSHOT_OPTIONS=--enable-experiment=non-nullable"',

View File

@ -381,6 +381,7 @@ void main() {
'-PbuildNumber=1.0',
'-q',
'-Ptarget=${globals.fs.path.join('lib', 'main.dart')}',
'-Pdart-defines=RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'-Pdart-obfuscation=false',
'-Pextra-front-end-options=foo,bar',
'-Ptrack-widget-creation=true',

View File

@ -508,6 +508,7 @@ void main() {
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=${globals.fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Pbase-application-name=android.app.Application',
'-Pdart-defines=RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=true',
'-Ptree-shake-icons=true',
@ -547,6 +548,7 @@ void main() {
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=${globals.fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Pbase-application-name=android.app.Application',
'-Pdart-defines=RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'-Pdart-obfuscation=false',
'-Psplit-debug-info=${tempDir.path}',
'-Ptrack-widget-creation=true',
@ -590,6 +592,7 @@ void main() {
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=${globals.fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Pbase-application-name=android.app.Application',
'-Pdart-defines=RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'-Pdart-obfuscation=false',
'-Pextra-front-end-options=foo,bar',
'-Ptrack-widget-creation=true',
@ -633,6 +636,7 @@ void main() {
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=${globals.fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Pbase-application-name=android.app.Application',
'-Pdart-defines=RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=true',
'-Ptree-shake-icons=true',
@ -678,6 +682,7 @@ void main() {
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=${globals.fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Pbase-application-name=android.app.Application',
'-Pdart-defines=RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=true',
'-Ptree-shake-icons=true',
@ -731,6 +736,7 @@ void main() {
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=${globals.fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Pbase-application-name=android.app.Application',
'-Pdart-defines=RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=true',
'-Ptree-shake-icons=true',

View File

@ -301,6 +301,8 @@ void main() {
kBuildMode: 'debug',
kTargetPlatform: 'android-arm',
kTargetFile: globals.fs.path.join('lib', 'main.dart'),
kDartDefines:
'RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
kTrackWidgetCreation: 'true',
kFileSystemScheme: 'org-dartlang-root',
kIconTreeShakerFlag: 'false',
@ -343,7 +345,8 @@ void main() {
kTargetFile: globals.fs.path.join('lib', 'main.dart'),
kTrackWidgetCreation: 'true',
kFileSystemScheme: 'org-dartlang-root',
kDartDefines: 'Zm9vPWJhcg==',
kDartDefines:
'Zm9vPWJhcg==,RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
kIconTreeShakerFlag: 'false',
kDeferredComponents: 'false',
kDartObfuscation: 'false',
@ -382,6 +385,8 @@ void main() {
kBuildMode: 'debug',
kTargetPlatform: 'android-arm',
kTargetFile: globals.fs.path.join('lib', 'main.dart'),
kDartDefines:
'RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
kTrackWidgetCreation: 'true',
kFileSystemScheme: 'org-dartlang-root2',
kIconTreeShakerFlag: 'false',
@ -422,6 +427,8 @@ void main() {
kBuildMode: 'debug',
kTargetPlatform: 'android-arm',
kTargetFile: globals.fs.path.join('lib', 'main.dart'),
kDartDefines:
'RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
kTrackWidgetCreation: 'true',
kFileSystemScheme: 'org-dartlang-root',
kFileSystemRoots: 'test1,test2',
@ -463,6 +470,8 @@ void main() {
kBuildMode: 'debug',
kTargetPlatform: 'android-arm',
kTargetFile: globals.fs.path.join('lib', 'main.dart'),
kDartDefines:
'RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
kTrackWidgetCreation: 'true',
kFileSystemScheme: 'org-dartlang-root',
kExtraFrontEndOptions: '--testflag,--testflag2',
@ -504,6 +513,8 @@ void main() {
kBuildMode: 'debug',
kTargetPlatform: 'android-arm',
kTargetFile: globals.fs.path.join('lib', 'main.dart'),
kDartDefines:
'RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
kTrackWidgetCreation: 'true',
kFileSystemScheme: 'org-dartlang-root',
kExtraGenSnapshotOptions: '--testflag,--testflag2',
@ -550,7 +561,8 @@ void main() {
kBuildMode: 'profile',
kTargetPlatform: 'android-arm',
kTargetFile: globals.fs.path.join('lib', 'main.dart'),
kDartDefines: 'Zm9vPWJhcg==',
kDartDefines:
'Zm9vPWJhcg==,RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
kTrackWidgetCreation: 'true',
kFileSystemScheme: 'org-dartlang-root',
kFileSystemRoots: 'test1,test2',
@ -599,7 +611,8 @@ void main() {
kBuildMode: 'release',
kTargetPlatform: 'android-arm',
kTargetFile: globals.fs.path.join('lib', 'main.dart'),
kDartDefines: 'Zm9vPWJhcg==',
kDartDefines:
'Zm9vPWJhcg==,RkxVVFRFUl9WRVJTSU9OPTAuMC4w,RkxVVFRFUl9DSEFOTkVMPW1hc3Rlcg==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049MTExMTE=,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049YWJjZGU=,RkxVVFRFUl9EQVJUX1ZFUlNJT049MTI=',
kTrackWidgetCreation: 'true',
kFileSystemScheme: 'org-dartlang-root',
kFileSystemRoots: 'test1,test2',

View File

@ -18,10 +18,12 @@ import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/run.dart' show RunCommand;
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/pre_run_validator.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:meta/meta.dart';
import 'package:test/fake.dart';
import 'package:unified_analytics/testing.dart';
@ -1267,6 +1269,196 @@ flutter:
},
);
});
group('Flutter version', () {
for (final String dartDefine in FlutterCommand.flutterVersionDartDefines) {
testUsingContext(
"tool exits when $dartDefine is already set in user's environment",
() async {
final CommandRunner<void> runner = createTestCommandRunner(
_TestRunCommandThatOnlyValidates(),
);
await expectLater(
runner.run(<String>['run', '--no-pub', '--no-hot']),
throwsToolExit(
message:
'$dartDefine is used by the framework and cannot be set in the environment. '
'Use FlutterVersion to access it in Flutter code',
),
);
},
overrides: <Type, Generator>{
DeviceManager:
() => FakeDeviceManager()..attachedDevices = <Device>[FakeDevice('name', 'id')],
Platform:
() => FakePlatform(environment: <String, String>{dartDefine: 'I was already set'}),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () {
return MemoryFileSystem.test()
..file('lib/main.dart').createSync(recursive: true)
..file('pubspec.yaml').createSync()
..file('.packages').createSync();
},
ProcessManager: () => FakeProcessManager.any(),
FlutterVersion: () => FakeFlutterVersion(),
},
);
testUsingContext(
'tool exits when $dartDefine is set in --dart-define or --dart-define-from-file',
() async {
final CommandRunner<void> runner = createTestCommandRunner(
_TestRunCommandThatOnlyValidates(),
);
expect(
runner.run(<String>[
'run',
'--dart-define=$dartDefine=AlreadySet',
'--no-pub',
'--no-hot',
]),
throwsToolExit(
message:
'$dartDefine is used by the framework and cannot be set using --dart-define or --dart-define-from-file. '
'Use FlutterVersion to access it in Flutter code',
),
);
expect(
runner.run(<String>[
'run',
'--dart-define-from-file=config.json',
'--no-pub',
'--no-hot',
]),
throwsToolExit(
message:
'$dartDefine is used by the framework and cannot be set using --dart-define or --dart-define-from-file. '
'Use FlutterVersion to access it in Flutter code',
),
);
},
overrides: <Type, Generator>{
DeviceManager:
() => FakeDeviceManager()..attachedDevices = <Device>[FakeDevice('name', 'id')],
Platform: () => FakePlatform(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () {
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('.packages').createSync();
fileSystem.file('config.json')
..createSync()
..writeAsStringSync('{"$dartDefine": "AlreadySet"}');
return fileSystem;
},
ProcessManager: () => FakeProcessManager.any(),
FlutterVersion: () => FakeFlutterVersion(),
},
);
}
testUsingContext(
'FLUTTER_VERSION is set in dartDefines',
() async {
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(packagesPath: 'foo');
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(
forcedBuildMode: BuildMode.debug,
);
expect(buildInfo.dartDefines, contains('FLUTTER_VERSION=0.0.0'));
},
overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(),
FlutterVersion: () => FakeFlutterVersion(),
},
);
testUsingContext(
'FLUTTER_CHANNEL is set in dartDefines',
() async {
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(packagesPath: 'foo');
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(
forcedBuildMode: BuildMode.debug,
);
expect(buildInfo.dartDefines, contains('FLUTTER_CHANNEL=master'));
},
overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(),
FlutterVersion: () => FakeFlutterVersion(),
},
);
testUsingContext(
'FLUTTER_GIT_URL is set in dartDefines',
() async {
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(packagesPath: 'foo');
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(
forcedBuildMode: BuildMode.debug,
);
expect(
buildInfo.dartDefines,
contains('FLUTTER_GIT_URL=https://github.com/flutter/flutter.git'),
);
},
overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(),
FlutterVersion: () => FakeFlutterVersion(),
},
);
testUsingContext(
'FLUTTER_FRAMEWORK_REVISION is set in dartDefines',
() async {
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(packagesPath: 'foo');
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(
forcedBuildMode: BuildMode.debug,
);
expect(buildInfo.dartDefines, contains('FLUTTER_FRAMEWORK_REVISION=11111'));
},
overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(),
FlutterVersion: () => FakeFlutterVersion(),
},
);
testUsingContext(
'FLUTTER_ENGINE_REVISION is set in dartDefines',
() async {
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(packagesPath: 'foo');
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(
forcedBuildMode: BuildMode.debug,
);
expect(buildInfo.dartDefines, contains('FLUTTER_ENGINE_REVISION=abcde'));
},
overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(),
FlutterVersion: () => FakeFlutterVersion(),
},
);
testUsingContext(
'FLUTTER_DART_VERSION is set in dartDefines',
() async {
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(packagesPath: 'foo');
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(
forcedBuildMode: BuildMode.debug,
);
expect(buildInfo.dartDefines, contains('FLUTTER_DART_VERSION=12'));
},
overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(),
FlutterVersion: () => FakeFlutterVersion(),
},
);
});
});
}
@ -1378,3 +1570,13 @@ class FakeClock extends Fake implements SystemClock {
return DateTime.fromMillisecondsSinceEpoch(times.removeAt(0));
}
}
class _TestRunCommandThatOnlyValidates extends RunCommand {
@override
Future<FlutterCommandResult> runCommand() async {
return FlutterCommandResult.success();
}
@override
bool get shouldRunPub => false;
}