diff --git a/packages/flutter_tools/lib/src/base/user_messages.dart b/packages/flutter_tools/lib/src/base/user_messages.dart index e2c9089472602..f9c952c49ddf5 100644 --- a/packages/flutter_tools/lib/src/base/user_messages.dart +++ b/packages/flutter_tools/lib/src/base/user_messages.dart @@ -24,6 +24,7 @@ class UserMessages { String flutterGitUrl(String url) => 'FLUTTER_GIT_URL = $url'; String engineRevision(String revision) => 'Engine revision $revision'; String dartRevision(String revision) => 'Dart version $revision'; + String devToolsVersion(String version) => 'DevTools version $version'; String pubMirrorURL(String url) => 'Pub download mirror $url'; String flutterMirrorURL(String url) => 'Flutter download mirror $url'; String get flutterBinariesDoNotRun => diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index 82c8c2c6c9fa5..9247e86c47f49 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart @@ -361,6 +361,35 @@ class Cache { } } + String get devToolsVersion { + if (_devToolsVersion == null) { + const String devToolsDirPath = 'dart-sdk/bin/resources/devtools'; + final Directory devToolsDir = getCacheDir(devToolsDirPath, shouldCreate: false); + if (!devToolsDir.existsSync()) { + throw Exception('Could not find directory at ${devToolsDir.path}'); + } + final String versionFilePath = '${devToolsDir.path}/version.json'; + final File versionFile = _fileSystem.file(versionFilePath); + if (!versionFile.existsSync()) { + throw Exception('Could not find file at $versionFilePath'); + } + final dynamic data = jsonDecode(versionFile.readAsStringSync()); + if (data is! Map) { + throw Exception("Expected object of type 'Map' but got one of type '${data.runtimeType}'"); + } + final dynamic version = data['version']; + if (version == null) { + throw Exception('Could not parse DevTools version from $version'); + } + if (version is! String) { + throw Exception("Could not parse DevTools version. Expected object of type 'String', but got one of type '${version.runtimeType}'"); + } + return _devToolsVersion = version; + } + return _devToolsVersion!; + } + String ? _devToolsVersion; + /// The current version of Dart used to build Flutter and run the tool. String get dartSdkVersion { if (_dartSdkVersion == null) { @@ -444,9 +473,12 @@ class Cache { } /// Return a directory in the cache dir. For `pkg`, this will return `bin/cache/pkg`. - Directory getCacheDir(String name) { + /// + /// When [shouldCreate] is true, the cache directory at [name] will be created + /// if it does not already exist. + Directory getCacheDir(String name, { bool shouldCreate = true }) { final Directory dir = _fileSystem.directory(_fileSystem.path.join(getRoot().path, name)); - if (!dir.existsSync()) { + if (!dir.existsSync() && shouldCreate) { dir.createSync(recursive: true); _osUtils.chmod(dir, '755'); } diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart index 3a13436f097a1..d47a55c7c6d47 100644 --- a/packages/flutter_tools/lib/src/doctor.dart +++ b/packages/flutter_tools/lib/src/doctor.dart @@ -91,6 +91,7 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { fileSystem: globals.fs, platform: globals.platform, flutterVersion: () => globals.flutterVersion, + devToolsVersion: () => globals.cache.devToolsVersion, processManager: globals.processManager, userMessages: userMessages, artifacts: globals.artifacts!, @@ -394,6 +395,7 @@ class FlutterValidator extends DoctorValidator { FlutterValidator({ required Platform platform, required FlutterVersion Function() flutterVersion, + required String Function() devToolsVersion, required UserMessages userMessages, required FileSystem fileSystem, required Artifacts artifacts, @@ -401,6 +403,7 @@ class FlutterValidator extends DoctorValidator { required String Function() flutterRoot, required OperatingSystemUtils operatingSystemUtils, }) : _flutterVersion = flutterVersion, + _devToolsVersion = devToolsVersion, _platform = platform, _userMessages = userMessages, _fileSystem = fileSystem, @@ -412,6 +415,7 @@ class FlutterValidator extends DoctorValidator { final Platform _platform; final FlutterVersion Function() _flutterVersion; + final String Function() _devToolsVersion; final String Function() _flutterRoot; final UserMessages _userMessages; final FileSystem _fileSystem; @@ -446,6 +450,7 @@ class FlutterValidator extends DoctorValidator { ))); messages.add(ValidationMessage(_userMessages.engineRevision(version.engineRevisionShort))); messages.add(ValidationMessage(_userMessages.dartRevision(version.dartSdkVersion))); + messages.add(ValidationMessage(_userMessages.devToolsVersion(_devToolsVersion()))); final String? pubUrl = _platform.environment['PUB_HOSTED_URL']; if (pubUrl != null) { messages.add(ValidationMessage(_userMessages.pubMirrorURL(pubUrl))); diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart index ef9ed71955785..15a648d3b0f22 100644 --- a/packages/flutter_tools/lib/src/version.dart +++ b/packages/flutter_tools/lib/src/version.dart @@ -144,6 +144,8 @@ class FlutterVersion { late String _frameworkVersion; String get frameworkVersion => _frameworkVersion; + String get devToolsVersion => globals.cache.devToolsVersion; + String get dartSdkVersion => globals.cache.dartSdkVersion; String get engineRevision => globals.cache.engineRevision; @@ -159,10 +161,10 @@ class FlutterVersion { final String flutterText = 'Flutter$versionText • channel $channel • ${repositoryUrl ?? 'unknown source'}'; final String frameworkText = 'Framework • revision $frameworkRevisionShort ($frameworkAge) • $frameworkCommitDate'; final String engineText = 'Engine • revision $engineRevisionShort'; - final String toolsText = 'Tools • Dart $dartSdkVersion'; + final String toolsText = 'Tools • Dart $dartSdkVersion • DevTools $devToolsVersion'; // Flutter 1.10.2-pre.69 • channel master • https://github.com/flutter/flutter.git - // Framework • revision 340c158f32 (84 minutes ago) • 2018-10-26 11:27:22 -0400 + // Framework • revision 340c158f32 (85 minutes ago) • 2018-10-26 11:27:22 -0400 // Engine • revision 9c46333e14 // Tools • Dart 2.1.0 (build 2.1.0-dev.8.0 bf26f760b1) @@ -177,6 +179,7 @@ class FlutterVersion { 'frameworkCommitDate': frameworkCommitDate, 'engineRevision': engineRevision, 'dartSdkVersion': dartSdkVersion, + 'devToolsVersion': devToolsVersion, }; String get frameworkDate => frameworkCommitDate; diff --git a/packages/flutter_tools/test/general.shard/cache_test.dart b/packages/flutter_tools/test/general.shard/cache_test.dart index 8640af83bcbc0..b95033e7d6f85 100644 --- a/packages/flutter_tools/test/general.shard/cache_test.dart +++ b/packages/flutter_tools/test/general.shard/cache_test.dart @@ -894,6 +894,12 @@ void main() { expect(pub.calledGet, 1); }); + testUsingContext('Check current DevTools version', () async { + final String currentDevToolsVersion = globals.cache.devToolsVersion; + final RegExp devToolsVersionFormat = RegExp(r'\d+\.\d+\.\d+(?:-\S+)?'); + expect(devToolsVersionFormat.allMatches(currentDevToolsVersion).length, 1,); + }); + // Check that the build number matches the format documented here: // https://dart.dev/get-dart#release-channels testUsingContext('Check current Dart SDK build number', () async { @@ -1060,7 +1066,7 @@ class FakeSecondaryCache extends Fake implements Cache { Directory getArtifactDirectory(String name) => artifactDirectory; @override - Directory getCacheDir(String name) { + Directory getCacheDir(String name, { bool shouldCreate = true }) { return artifactDirectory.childDirectory(name); } diff --git a/packages/flutter_tools/test/general.shard/flutter_validator_test.dart b/packages/flutter_tools/test/general.shard/flutter_validator_test.dart index 05abf80a10dff..fa8db110cc471 100644 --- a/packages/flutter_tools/test/general.shard/flutter_validator_test.dart +++ b/packages/flutter_tools/test/general.shard/flutter_validator_test.dart @@ -44,6 +44,7 @@ void main() { environment: {}, ), flutterVersion: () => flutterVersion, + devToolsVersion: () => '2.8.0', userMessages: UserMessages(), artifacts: artifacts, fileSystem: fileSystem, @@ -85,6 +86,7 @@ void main() { environment: {}, ), flutterVersion: () => flutterVersion, + devToolsVersion: () => '2.8.0', userMessages: UserMessages(), artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), @@ -106,6 +108,7 @@ void main() { final FlutterValidator flutterValidator = FlutterValidator( platform: FakePlatform(operatingSystem: 'windows', localeName: 'en_US.UTF-8'), flutterVersion: () => FakeThrowingFlutterVersion(), + devToolsVersion: () => '2.8.0', userMessages: UserMessages(), artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), @@ -141,6 +144,7 @@ void main() { final FlutterValidator flutterValidator = FlutterValidator( platform: platform, flutterVersion: () => flutterVersion, + devToolsVersion: () => '2.8.0', userMessages: UserMessages(), artifacts: artifacts, fileSystem: fileSystem, @@ -168,6 +172,7 @@ void main() { }, ), flutterVersion: () => FakeFlutterVersion(frameworkVersion: '1.0.0'), + devToolsVersion: () => '2.8.0', userMessages: UserMessages(), artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), @@ -188,6 +193,7 @@ void main() { final FlutterValidator flutterValidator = FlutterValidator( platform: FakePlatform(localeName: 'en_US.UTF-8'), flutterVersion: () => FakeFlutterVersion(frameworkVersion: '1.0.0'), + devToolsVersion: () => '2.8.0', userMessages: UserMessages(), artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), @@ -210,6 +216,7 @@ void main() { frameworkVersion: '1.0.0', repositoryUrl: null, ), + devToolsVersion: () => '2.8.0', userMessages: UserMessages(), artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), diff --git a/packages/flutter_tools/test/general.shard/runner/runner_test.dart b/packages/flutter_tools/test/general.shard/runner/runner_test.dart index 752ffd13d01f1..42e8b7383b5c3 100644 --- a/packages/flutter_tools/test/general.shard/runner/runner_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/runner_test.dart @@ -141,6 +141,16 @@ void main() { }); testUsingContext('create local report', () async { + // Since crash reporting calls the doctor, which checks for the devtools + // version file in the cache, write a version file to the memory fs. + Cache.flutterRoot = '/path/to/flutter'; + final Directory devtoolsDir = globals.fs.directory( + '${Cache.flutterRoot}/bin/cache/dart-sdk/bin/resources/devtools', + )..createSync(recursive: true); + devtoolsDir.childFile('version.json').writeAsStringSync( + '{"version": "1.2.3"}', + ); + final Completer completer = Completer(); // runner.run() asynchronously calls the exit function set above, so we // catch it in a zone. diff --git a/packages/flutter_tools/test/general.shard/version_test.dart b/packages/flutter_tools/test/general.shard/version_test.dart index 9eede4b139483..cbcaecf782af4 100644 --- a/packages/flutter_tools/test/general.shard/version_test.dart +++ b/packages/flutter_tools/test/general.shard/version_test.dart @@ -126,7 +126,7 @@ void main() { 'Flutter • channel $channel • unknown source\n' 'Framework • revision 1234abcd (1 second ago) • ${getChannelUpToDateVersion()}\n' 'Engine • revision abcdefg\n' - 'Tools • Dart 2.12.0', + 'Tools • Dart 2.12.0 • DevTools 2.8.0', ); expect(flutterVersion.frameworkAge, '1 second ago'); expect(flutterVersion.getVersionString(), '$channel/1234abcd'); @@ -605,6 +605,9 @@ class FakeCache extends Fake implements Cache { @override String get engineRevision => 'abcdefg'; + @override + String get devToolsVersion => '2.8.0'; + @override String get dartSdkVersion => '2.12.0'; diff --git a/packages/flutter_tools/test/src/fakes.dart b/packages/flutter_tools/test/src/fakes.dart index 85be1b1fb700c..716508336e6bc 100644 --- a/packages/flutter_tools/test/src/fakes.dart +++ b/packages/flutter_tools/test/src/fakes.dart @@ -320,6 +320,7 @@ class FakeFlutterVersion implements FlutterVersion { FakeFlutterVersion({ this.channel = 'unknown', this.dartSdkVersion = '12', + this.devToolsVersion = '2.8.0', this.engineRevision = 'abcdefghijklmnopqrstuvwxyz', this.engineRevisionShort = 'abcde', this.repositoryUrl = 'https://github.com/flutter/flutter.git', @@ -340,6 +341,9 @@ class FakeFlutterVersion implements FlutterVersion { @override final String channel; + @override + final String devToolsVersion; + @override final String dartSdkVersion;