Unverified Commit 4fba7747 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Support Xcode patch version comparison (#61484)

parent a1a5a8f6
...@@ -138,8 +138,8 @@ class UserMessages { ...@@ -138,8 +138,8 @@ class UserMessages {
// Messages used in XcodeValidator // Messages used in XcodeValidator
String xcodeLocation(String location) => 'Xcode at $location'; String xcodeLocation(String location) => 'Xcode at $location';
String xcodeOutdated(int versionMajor, int versionMinor) => String xcodeOutdated(int versionMajor, int versionMinor, int versionPatch) =>
'Flutter requires a minimum Xcode version of $versionMajor.$versionMinor.0.\n' 'Flutter requires a minimum Xcode version of $versionMajor.$versionMinor.$versionPatch.\n'
'Download the latest version or update via the Mac App Store.'; 'Download the latest version or update via the Mac App Store.';
String get xcodeEula => "Xcode end user license agreement not signed; open Xcode or run the command 'sudo xcodebuild -license'."; String get xcodeEula => "Xcode end user license agreement not signed; open Xcode or run the command 'sudo xcodebuild -license'.";
String get xcodeMissingSimct => String get xcodeMissingSimct =>
......
...@@ -600,7 +600,7 @@ class XcodeBuildExecution { ...@@ -600,7 +600,7 @@ class XcodeBuildExecution {
final Map<String, String> buildSettings; final Map<String, String> buildSettings;
} }
const String _xcodeRequirement = 'Xcode $kXcodeRequiredVersionMajor.$kXcodeRequiredVersionMinor or greater is required to develop for iOS.'; const String _xcodeRequirement = 'Xcode $kXcodeRequiredVersionMajor.$kXcodeRequiredVersionMinor.$kXcodeRequiredVersionPatch or greater is required to develop for iOS.';
bool _checkXcodeVersion() { bool _checkXcodeVersion() {
if (!globals.platform.isMacOS) { if (!globals.platform.isMacOS) {
......
...@@ -261,6 +261,7 @@ class XcodeProjectInterpreter { ...@@ -261,6 +261,7 @@ class XcodeProjectInterpreter {
return; return;
} }
try { try {
if (_versionText == null) {
final RunResult result = _processUtils.runSync( final RunResult result = _processUtils.runSync(
<String>[_executable, '-version'], <String>[_executable, '-version'],
); );
...@@ -268,6 +269,7 @@ class XcodeProjectInterpreter { ...@@ -268,6 +269,7 @@ class XcodeProjectInterpreter {
return; return;
} }
_versionText = result.stdout.trim().replaceAll('\n', ', '); _versionText = result.stdout.trim().replaceAll('\n', ', ');
}
final Match match = _versionRegex.firstMatch(versionText); final Match match = _versionRegex.firstMatch(versionText);
if (match == null) { if (match == null) {
return; return;
...@@ -275,7 +277,8 @@ class XcodeProjectInterpreter { ...@@ -275,7 +277,8 @@ class XcodeProjectInterpreter {
final String version = match.group(1); final String version = match.group(1);
final List<String> components = version.split('.'); final List<String> components = version.split('.');
_majorVersion = int.parse(components[0]); _majorVersion = int.parse(components[0]);
_minorVersion = components.length == 1 ? 0 : int.parse(components[1]); _minorVersion = components.length < 2 ? 0 : int.parse(components[1]);
_patchVersion = components.length < 3 ? 0 : int.parse(components[2]);
} on ProcessException { } on ProcessException {
// Ignored, leave values null. // Ignored, leave values null.
} }
...@@ -307,6 +310,14 @@ class XcodeProjectInterpreter { ...@@ -307,6 +310,14 @@ class XcodeProjectInterpreter {
return _minorVersion; return _minorVersion;
} }
int _patchVersion;
int get patchVersion {
if (_patchVersion == null) {
_updateVersion();
}
return _patchVersion;
}
/// Asynchronously retrieve xcode build settings. This one is preferred for /// Asynchronously retrieve xcode build settings. This one is preferred for
/// new call-sites. /// new call-sites.
/// ///
......
...@@ -27,6 +27,7 @@ import '../reporting/reporting.dart'; ...@@ -27,6 +27,7 @@ import '../reporting/reporting.dart';
const int kXcodeRequiredVersionMajor = 11; const int kXcodeRequiredVersionMajor = 11;
const int kXcodeRequiredVersionMinor = 0; const int kXcodeRequiredVersionMinor = 0;
const int kXcodeRequiredVersionPatch = 0;
enum SdkType { enum SdkType {
iPhone, iPhone,
...@@ -97,8 +98,8 @@ class Xcode { ...@@ -97,8 +98,8 @@ class Xcode {
} }
int get majorVersion => _xcodeProjectInterpreter.majorVersion; int get majorVersion => _xcodeProjectInterpreter.majorVersion;
int get minorVersion => _xcodeProjectInterpreter.minorVersion; int get minorVersion => _xcodeProjectInterpreter.minorVersion;
int get patchVersion => _xcodeProjectInterpreter.patchVersion;
String get versionText => _xcodeProjectInterpreter.versionText; String get versionText => _xcodeProjectInterpreter.versionText;
...@@ -151,6 +152,9 @@ class Xcode { ...@@ -151,6 +152,9 @@ class Xcode {
return true; return true;
} }
if (majorVersion == kXcodeRequiredVersionMajor) { if (majorVersion == kXcodeRequiredVersionMajor) {
if (minorVersion == kXcodeRequiredVersionMinor) {
return patchVersion >= kXcodeRequiredVersionPatch;
}
return minorVersion >= kXcodeRequiredVersionMinor; return minorVersion >= kXcodeRequiredVersionMinor;
} }
return false; return false;
......
...@@ -39,7 +39,7 @@ class XcodeValidator extends DoctorValidator { ...@@ -39,7 +39,7 @@ class XcodeValidator extends DoctorValidator {
if (!_xcode.isInstalledAndMeetsVersionCheck) { if (!_xcode.isInstalledAndMeetsVersionCheck) {
xcodeStatus = ValidationType.partial; xcodeStatus = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(
_userMessages.xcodeOutdated(kXcodeRequiredVersionMajor, kXcodeRequiredVersionMinor) _userMessages.xcodeOutdated(kXcodeRequiredVersionMajor, kXcodeRequiredVersionMinor, kXcodeRequiredVersionPatch)
)); ));
} }
......
...@@ -136,49 +136,38 @@ void main() { ...@@ -136,49 +136,38 @@ void main() {
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild majorVersion returns major version', () { testWithoutContext('xcodebuild version parts can be parsed', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: <String>[xcodebuild, '-version'], command: <String>[xcodebuild, '-version'],
stdout: 'Xcode 11.4.1\nBuild version 11N111s', stdout: 'Xcode 11.4.1\nBuild version 11N111s',
)); ));
expect(xcodeProjectInterpreter.majorVersion, 11); expect(xcodeProjectInterpreter.majorVersion, 11);
expect(xcodeProjectInterpreter.minorVersion, 4);
expect(xcodeProjectInterpreter.patchVersion, 1);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild majorVersion is null when version has unexpected format', () { testWithoutContext('xcodebuild minor and patch version default to 0', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: <String>[xcodebuild, '-version'], command: <String>[xcodebuild, '-version'],
stdout: 'Xcode Ultra5000\nBuild version 8E3004b', stdout: 'Xcode 11\nBuild version 11N111s',
));
expect(xcodeProjectInterpreter.majorVersion, isNull);
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
});
testWithoutContext('xcodebuild minorVersion returns minor version', () {
fakeProcessManager.addCommand(const FakeCommand(
command: <String>[xcodebuild, '-version'],
stdout: 'Xcode 8.3.3\nBuild version 8E3004b',
)); ));
expect(xcodeProjectInterpreter.minorVersion, 3);
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
});
testWithoutContext('xcodebuild minorVersion returns 0 when minor version is unspecified', () { expect(xcodeProjectInterpreter.majorVersion, 11);
fakeProcessManager.addCommand(const FakeCommand(
command: <String>[xcodebuild, '-version'],
stdout: 'Xcode 8\nBuild version 8E3004b',
));
expect(xcodeProjectInterpreter.minorVersion, 0); expect(xcodeProjectInterpreter.minorVersion, 0);
expect(xcodeProjectInterpreter.patchVersion, 0);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild minorVersion is null when version has unexpected format', () { testWithoutContext('xcodebuild version parts is null when version has unexpected format', () {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: <String>[xcodebuild, '-version'], command: <String>[xcodebuild, '-version'],
stdout: 'Xcode Ultra5000\nBuild version 8E3004b', stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
)); ));
expect(xcodeProjectInterpreter.majorVersion, isNull);
expect(xcodeProjectInterpreter.minorVersion, isNull); expect(xcodeProjectInterpreter.minorVersion, isNull);
expect(xcodeProjectInterpreter.patchVersion, isNull);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
......
...@@ -157,6 +157,7 @@ void main() { ...@@ -157,6 +157,7 @@ void main() {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
expect(xcode.isVersionSatisfactory, isFalse); expect(xcode.isVersionSatisfactory, isFalse);
}); });
...@@ -171,6 +172,7 @@ void main() { ...@@ -171,6 +172,7 @@ void main() {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
expect(xcode.isVersionSatisfactory, isTrue); expect(xcode.isVersionSatisfactory, isTrue);
}); });
...@@ -179,6 +181,7 @@ void main() { ...@@ -179,6 +181,7 @@ void main() {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(12); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(12);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
expect(xcode.isVersionSatisfactory, isTrue); expect(xcode.isVersionSatisfactory, isTrue);
}); });
...@@ -187,6 +190,16 @@ void main() { ...@@ -187,6 +190,16 @@ void main() {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(3); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(3);
when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
expect(xcode.isVersionSatisfactory, isTrue);
});
testWithoutContext('xcodeVersionSatisfactory is true when patch version exceeds minimum', () {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
when(mockXcodeProjectInterpreter.patchVersion).thenReturn(1);
expect(xcode.isVersionSatisfactory, isTrue); expect(xcode.isVersionSatisfactory, isTrue);
}); });
...@@ -219,6 +232,7 @@ void main() { ...@@ -219,6 +232,7 @@ void main() {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse); expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
...@@ -233,6 +247,7 @@ void main() { ...@@ -233,6 +247,7 @@ void main() {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse); expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
...@@ -247,6 +262,7 @@ void main() { ...@@ -247,6 +262,7 @@ void main() {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0);
expect(xcode.isInstalledAndMeetsVersionCheck, isTrue); expect(xcode.isInstalledAndMeetsVersionCheck, isTrue);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
......
...@@ -394,6 +394,9 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter { ...@@ -394,6 +394,9 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter {
@override @override
int get minorVersion => 0; int get minorVersion => 0;
@override
int get patchVersion => 0;
@override @override
Future<Map<String, String>> getBuildSettings( Future<Map<String, String>> getBuildSettings(
String projectPath, { String projectPath, {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment