Ensure Xcode major, minor version always return non-null (#10980)
Previously, xcodeMajorVersion and xcodeMinorVersion returned null unless
xcodeVersionSatisfactory had been called first. We now compute them on
demand, and cache the resultant values.
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 3c961e7..2e3661f 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -121,37 +121,48 @@
return _eulaSigned;
}
+ final RegExp xcodeVersionRegex = new RegExp(r'Xcode ([0-9.]+)');
+ void _updateXcodeVersion() {
+ try {
+ _xcodeVersionText = processManager.runSync(<String>['/usr/bin/xcodebuild', '-version']).stdout.replaceAll('\n', ', ');
+ final Match match = xcodeVersionRegex.firstMatch(xcodeVersionText);
+ if (match == null)
+ return;
+
+ final String version = match.group(1);
+ final List<String> components = version.split('.');
+ _xcodeMajorVersion = int.parse(components[0]);
+ _xcodeMinorVersion = components.length == 1 ? 0 : int.parse(components[1]);
+ } on ProcessException {
+ // Ignore: leave values null.
+ }
+ }
+
String _xcodeVersionText;
String get xcodeVersionText {
- if (_xcodeVersionText == null) {
- try {
- _xcodeVersionText = processManager.runSync(<String>['/usr/bin/xcodebuild', '-version']).stdout.replaceAll('\n', ', ');
- } on ProcessException {
- // Ignore: return null below.
- }
- }
+ if (_xcodeVersionText == null)
+ _updateXcodeVersion();
return _xcodeVersionText;
}
int _xcodeMajorVersion;
- int get xcodeMajorVersion => _xcodeMajorVersion;
+ int get xcodeMajorVersion {
+ if (_xcodeMajorVersion == null)
+ _updateXcodeVersion();
+ return _xcodeMajorVersion;
+ }
int _xcodeMinorVersion;
- int get xcodeMinorVersion => _xcodeMinorVersion;
-
- final RegExp xcodeVersionRegex = new RegExp(r'Xcode ([0-9.]+)');
+ int get xcodeMinorVersion {
+ if (_xcodeMinorVersion == null)
+ _updateXcodeVersion();
+ return _xcodeMinorVersion;
+ }
bool get xcodeVersionSatisfactory {
if (xcodeVersionText == null || !xcodeVersionRegex.hasMatch(xcodeVersionText))
return false;
-
- final String version = xcodeVersionRegex.firstMatch(xcodeVersionText).group(1);
- final List<String> components = version.split('.');
-
- _xcodeMajorVersion = int.parse(components[0]);
- _xcodeMinorVersion = components.length == 1 ? 0 : int.parse(components[1]);
-
- return _xcodeVersionCheckValid(_xcodeMajorVersion, _xcodeMinorVersion);
+ return _xcodeVersionCheckValid(xcodeMajorVersion, xcodeMinorVersion);
}
Future<String> getAvailableDevices() async {
diff --git a/packages/flutter_tools/test/ios/mac_test.dart b/packages/flutter_tools/test/ios/mac_test.dart
index 9313030..c23b708 100644
--- a/packages/flutter_tools/test/ios/mac_test.dart
+++ b/packages/flutter_tools/test/ios/mac_test.dart
@@ -116,6 +116,46 @@
ProcessManager: () => mockProcessManager,
});
+ testUsingContext('xcodeMajorVersion returns major version', () {
+ when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version']))
+ .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', ''));
+ expect(xcode.xcodeMajorVersion, 8);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ });
+
+ testUsingContext('xcodeMajorVersion is null when version has unexpected format', () {
+ when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version']))
+ .thenReturn(new ProcessResult(1, 0, 'Xcode Ultra5000\nBuild version 8E3004b', ''));
+ expect(xcode.xcodeMajorVersion, isNull);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ });
+
+ testUsingContext('xcodeMinorVersion returns minor version', () {
+ when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version']))
+ .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', ''));
+ expect(xcode.xcodeMinorVersion, 3);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ });
+
+ testUsingContext('xcodeMinorVersion returns 0 when minor version is unspecified', () {
+ when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version']))
+ .thenReturn(new ProcessResult(1, 0, 'Xcode 8\nBuild version 8E3004b', ''));
+ expect(xcode.xcodeMinorVersion, 0);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ });
+
+ testUsingContext('xcodeMajorVersion is null when version has unexpected format', () {
+ when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version']))
+ .thenReturn(new ProcessResult(1, 0, 'Xcode Ultra5000\nBuild version 8E3004b', ''));
+ expect(xcode.xcodeMinorVersion, isNull);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ });
+
testUsingContext('eulaSigned is false when clang is not installed', () {
when(mockProcessManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
.thenThrow(const ProcessException('/usr/bin/xcrun', const <String>['clang']));