From a8d1b6238219f585c462b11276574c323b8014f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Uek=C3=B6tter?= Date: Mon, 24 Feb 2025 21:17:08 +0100 Subject: [PATCH] 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 ## 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]. [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 --- packages/flutter/lib/services.dart | 1 + .../lib/src/services/flutter_version.dart | 49 +++++ .../test/services/flutter_version_test.dart | 35 +++ .../lib/src/runner/flutter_command.dart | 66 ++++++ .../hermetic/build_aar_test.dart | 2 +- .../hermetic/build_linux_test.dart | 2 +- .../hermetic/build_macos_test.dart | 2 +- .../hermetic/build_web_test.dart | 8 +- .../hermetic/build_windows_test.dart | 2 +- .../permeable/build_aar_test.dart | 1 + .../permeable/build_apk_test.dart | 6 + .../permeable/build_bundle_test.dart | 19 +- .../runner/flutter_command_test.dart | 202 ++++++++++++++++++ 13 files changed, 386 insertions(+), 9 deletions(-) create mode 100644 packages/flutter/lib/src/services/flutter_version.dart create mode 100644 packages/flutter/test/services/flutter_version_test.dart diff --git a/packages/flutter/lib/services.dart b/packages/flutter/lib/services.dart index acad47fa013..82f1ab79a49 100644 --- a/packages/flutter/lib/services.dart +++ b/packages/flutter/lib/services.dart @@ -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'; diff --git a/packages/flutter/lib/src/services/flutter_version.dart b/packages/flutter/lib/src/services/flutter_version.dart new file mode 100644 index 00000000000..04a4bc7a691 --- /dev/null +++ b/packages/flutter/lib/src/services/flutter_version.dart @@ -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; +} diff --git a/packages/flutter/test/services/flutter_version_test.dart b/packages/flutter/test/services/flutter_version_test.dart new file mode 100644 index 00000000000..ac2b1a20d38 --- /dev/null +++ b/packages/flutter/test/services/flutter_version_test.dart @@ -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')); + }); +} diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 267c03f8ed0..842a9a80c27 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -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 { /// 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 flutterVersionDartDefines = [ + flutterVersionDefine, + flutterChannelDefine, + flutterGitUrlDefine, + flutterFrameworkRevisionDefine, + flutterEngineRevisionDefine, + flutterDartVersionDefine, + ]; + @override ArgParser get argParser => _argParser; final ArgParser _argParser = ArgParser( @@ -1419,6 +1455,8 @@ abstract class FlutterCommand extends Command { 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 { ); } + // This adds the Dart defines used to access various Flutter version information at runtime. + void _addFlutterVersionToDartDefines(FlutterVersion version, List 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([ + '$flutterVersionDefine=${version.frameworkVersion}', + '$flutterChannelDefine=${version.channel}', + '$flutterGitUrlDefine=${version.repositoryUrl}', + '$flutterFrameworkRevisionDefine=${version.frameworkRevisionShort}', + '$flutterEngineRevisionDefine=${version.engineRevisionShort}', + '$flutterDartVersionDefine=${version.dartSdkVersion}', + ]); + } + void setupApplicationPackages() { applicationPackages ??= ApplicationPackageFactory.instance; } diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_aar_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_aar_test.dart index 351d2275d55..7af69e05f42 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_aar_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_aar_test.dart @@ -102,7 +102,7 @@ flutter: '-I=/flutter/packages/flutter_tools/gradle/aar_init_script.gradle', ...List.filled(4, RegExp(r'-P[a-zA-Z-]+=.*')), '-q', - ...List.filled(5, RegExp(r'-P[a-zA-Z-]+=.*')), + ...List.filled(6, RegExp(r'-P[a-zA-Z-]+=.*')), 'assembleAar$buildMode', ], onRun: (_) => fs.directory('/build/host/outputs/repo').createSync(recursive: true), diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart index 3fe5dbe2399..0d5a130c551 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart @@ -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"', diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart index c2438b690c9..2802a2b791e 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart @@ -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', diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart index a68a6c950c5..2ab09887360 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart @@ -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', diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart index 7066a8e42e5..cae29287ced 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart @@ -568,7 +568,7 @@ if %errorlevel% neq 0 goto :VCEnd 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"', diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart index dd28270322e..a0bdaed8987 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart @@ -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', diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart index aee44cbe2a4..86cf760960a 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart @@ -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', diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart index 34254e69902..12155e36989 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart @@ -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', diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart index f4cf2648337..0fae431f4dc 100644 --- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart @@ -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 runner = createTestCommandRunner( + _TestRunCommandThatOnlyValidates(), + ); + + await expectLater( + runner.run(['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: { + DeviceManager: + () => FakeDeviceManager()..attachedDevices = [FakeDevice('name', 'id')], + Platform: + () => FakePlatform(environment: {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 runner = createTestCommandRunner( + _TestRunCommandThatOnlyValidates(), + ); + + expect( + runner.run([ + '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([ + '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: { + DeviceManager: + () => FakeDeviceManager()..attachedDevices = [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: { + 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: { + 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: { + 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: { + 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: { + 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: { + 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 runCommand() async { + return FlutterCommandResult.success(); + } + + @override + bool get shouldRunPub => false; +}