diff --git a/packages/flutter_tools/lib/src/base/user_messages.dart b/packages/flutter_tools/lib/src/base/user_messages.dart index ed92f208491..0b53f73e88f 100644 --- a/packages/flutter_tools/lib/src/base/user_messages.dart +++ b/packages/flutter_tools/lib/src/base/user_messages.dart @@ -139,8 +139,9 @@ class UserMessages { // Messages used in XcodeValidator String xcodeLocation(String location) => 'Xcode at $location'; - String xcodeOutdated(int versionMajor, int versionMinor, int versionPatch) => - 'Flutter requires a minimum Xcode version of $versionMajor.$versionMinor.$versionPatch.\n' + + String xcodeOutdated(String currentVersion, String recommendedVersion) => + 'Xcode $currentVersion out of date ($recommendedVersion is recommended).\n' '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 xcodeMissingSimct => diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index 5358cca3c9b..3473b715cf5 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart @@ -637,7 +637,7 @@ class XcodeBuildExecution { final Map buildSettings; } -const String _xcodeRequirement = 'Xcode $kXcodeRequiredVersionMajor.$kXcodeRequiredVersionMinor.$kXcodeRequiredVersionPatch or greater is required to develop for iOS.'; +final String _xcodeRequirement = 'Xcode $xcodeRequiredVersion or greater is required to develop for iOS.'; bool _checkXcodeVersion() { if (!globals.platform.isMacOS) { @@ -647,7 +647,7 @@ bool _checkXcodeVersion() { globals.printError('Cannot find "xcodebuild". $_xcodeRequirement'); return false; } - if (!globals.xcode.isVersionSatisfactory) { + if (!globals.xcode.isRequiredVersionSatisfactory) { globals.printError('Found "${globals.xcodeProjectInterpreter.versionText}". $_xcodeRequirement'); return false; } diff --git a/packages/flutter_tools/lib/src/ios/migrations/remove_framework_link_and_embedding_migration.dart b/packages/flutter_tools/lib/src/ios/migrations/remove_framework_link_and_embedding_migration.dart index 1dafbf63c1b..222d117004c 100644 --- a/packages/flutter_tools/lib/src/ios/migrations/remove_framework_link_and_embedding_migration.dart +++ b/packages/flutter_tools/lib/src/ios/migrations/remove_framework_link_and_embedding_migration.dart @@ -6,6 +6,7 @@ import '../../base/common.dart'; import '../../base/file_system.dart'; import '../../base/logger.dart'; import '../../base/project_migrator.dart'; +import '../../base/version.dart'; import '../../macos/xcode.dart'; import '../../project.dart'; import '../../reporting/reporting.dart'; @@ -99,7 +100,7 @@ class RemoveFrameworkLinkAndEmbeddingMigration extends ProjectMigrator { if (line.contains('/* App.framework ') || line.contains('/* Flutter.framework ')) { // Print scary message if the user is on Xcode 11.4 or greater, or if Xcode isn't installed. final bool xcodeIsInstalled = _xcode.isInstalled; - if(!xcodeIsInstalled || (_xcode.majorVersion > 11 || (_xcode.majorVersion == 11 && _xcode.minorVersion >= 4))) { + if(!xcodeIsInstalled || _xcode.currentVersion >= Version(11, 4, 0)) { UsageEvent('ios-migration', 'remove-frameworks', label: 'failure', flutterUsage: _usage).send(); throwToolExit('Your Xcode project requires migration. See https://flutter.dev/docs/development/ios-project-migration for details.'); } diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index 5e326157b53..98b729412ed 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart @@ -73,7 +73,6 @@ class IOSSimulatorUtils { name: device.name, simControl: _simControl, simulatorCategory: device.category, - xcode: _xcode, ); }).toList(); } @@ -309,9 +308,7 @@ class IOSSimulator extends Device { this.name, this.simulatorCategory, @required SimControl simControl, - @required Xcode xcode, }) : _simControl = simControl, - _xcode = xcode, super( id, category: Category.mobile, @@ -325,7 +322,6 @@ class IOSSimulator extends Device { final String simulatorCategory; final SimControl _simControl; - final Xcode _xcode; @override DevFSWriter createDevFSWriter(covariant ApplicationPackage app, String userIdentifier) { @@ -610,12 +606,8 @@ class IOSSimulator extends Device { } } - bool get _xcodeVersionSupportsScreenshot { - return _xcode.majorVersion > 8 || (_xcode.majorVersion == 8 && _xcode.minorVersion >= 2); - } - @override - bool get supportsScreenshot => _xcodeVersionSupportsScreenshot; + bool get supportsScreenshot => true; @override Future takeScreenshot(File outputFile) { diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart index 1e00ae124c4..26910982fa4 100644 --- a/packages/flutter_tools/lib/src/macos/xcode.dart +++ b/packages/flutter_tools/lib/src/macos/xcode.dart @@ -14,6 +14,7 @@ import '../base/io.dart'; import '../base/logger.dart'; import '../base/platform.dart'; import '../base/process.dart'; +import '../base/version.dart'; import '../build_info.dart'; import '../cache.dart'; import '../convert.dart'; @@ -25,9 +26,8 @@ import '../ios/mac.dart'; import '../ios/xcodeproj.dart'; import '../reporting/reporting.dart'; -const int kXcodeRequiredVersionMajor = 11; -const int kXcodeRequiredVersionMinor = 0; -const int kXcodeRequiredVersionPatch = 0; +Version get xcodeRequiredVersion => Version(11, 0, 0, text: '11.0'); +Version get xcodeRecommendedVersion => Version(12, 0, 1, text: '12.0.1'); /// SDK name passed to `xcrun --sdk`. Corresponds to undocumented Xcode /// SUPPORTED_PLATFORMS values. @@ -60,7 +60,7 @@ class Xcode { final FileSystem _fileSystem; final XcodeProjectInterpreter _xcodeProjectInterpreter; - bool get isInstalledAndMeetsVersionCheck => _platform.isMacOS && isInstalled && isVersionSatisfactory; + bool get isInstalledAndMeetsVersionCheck => _platform.isMacOS && isInstalled && isRequiredVersionSatisfactory; String _xcodeSelectPath; String get xcodeSelectPath { @@ -85,9 +85,13 @@ class Xcode { return _xcodeProjectInterpreter.isInstalled; } - int get majorVersion => _xcodeProjectInterpreter.majorVersion; - int get minorVersion => _xcodeProjectInterpreter.minorVersion; - int get patchVersion => _xcodeProjectInterpreter.patchVersion; + Version get currentVersion => Version( + _xcodeProjectInterpreter.majorVersion, + _xcodeProjectInterpreter.minorVersion, + _xcodeProjectInterpreter.patchVersion, + text: + '${_xcodeProjectInterpreter.majorVersion}.${_xcodeProjectInterpreter.minorVersion}.${_xcodeProjectInterpreter.patchVersion}', + ); String get versionText => _xcodeProjectInterpreter.versionText; @@ -132,20 +136,18 @@ class Xcode { return _isSimctlInstalled; } - bool get isVersionSatisfactory { + bool get isRequiredVersionSatisfactory { if (!_xcodeProjectInterpreter.isInstalled) { return false; } - if (majorVersion > kXcodeRequiredVersionMajor) { - return true; + return currentVersion >= xcodeRequiredVersion; + } + + bool get isRecommendedVersionSatisfactory { + if (!_xcodeProjectInterpreter.isInstalled) { + return false; } - if (majorVersion == kXcodeRequiredVersionMajor) { - if (minorVersion == kXcodeRequiredVersionMinor) { - return patchVersion >= kXcodeRequiredVersionPatch; - } - return minorVersion >= kXcodeRequiredVersionMinor; - } - return false; + return currentVersion >= xcodeRecommendedVersion; } /// See [XcodeProjectInterpreter.xcrunCommand]. diff --git a/packages/flutter_tools/lib/src/macos/xcode_validator.dart b/packages/flutter_tools/lib/src/macos/xcode_validator.dart index 72086adfc0a..500004117ff 100644 --- a/packages/flutter_tools/lib/src/macos/xcode_validator.dart +++ b/packages/flutter_tools/lib/src/macos/xcode_validator.dart @@ -29,18 +29,20 @@ class XcodeValidator extends DoctorValidator { xcodeStatus = ValidationType.installed; messages.add(ValidationMessage(_userMessages.xcodeLocation(_xcode.xcodeSelectPath))); - - xcodeVersionInfo = _xcode.versionText; - if (xcodeVersionInfo.contains(',')) { - xcodeVersionInfo = xcodeVersionInfo.substring(0, xcodeVersionInfo.indexOf(',')); - } messages.add(ValidationMessage(_xcode.versionText)); if (!_xcode.isInstalledAndMeetsVersionCheck) { xcodeStatus = ValidationType.partial; - messages.add(ValidationMessage.error( - _userMessages.xcodeOutdated(kXcodeRequiredVersionMajor, kXcodeRequiredVersionMinor, kXcodeRequiredVersionPatch) - )); + messages.add(ValidationMessage.error(_userMessages.xcodeOutdated( + _xcode.currentVersion.toString(), + xcodeRecommendedVersion.toString(), + ))); + } else if (!_xcode.isRecommendedVersionSatisfactory) { + xcodeStatus = ValidationType.partial; + messages.add(ValidationMessage.hint(_userMessages.xcodeOutdated( + _xcode.currentVersion.toString(), + xcodeRecommendedVersion.toString(), + ))); } if (!_xcode.eulaSigned) { diff --git a/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart index d657ebc3ba5..a7656a70acd 100644 --- a/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart @@ -101,7 +101,7 @@ void main() { } ); mockXcode = MockXcode(); - when(mockXcode.isVersionSatisfactory).thenReturn(true); + when(mockXcode.isRequiredVersionSatisfactory).thenReturn(true); when(mockXcode.xcrunCommand()).thenReturn(['xcrun']); fileSystem.file('foo/.packages') ..createSync(recursive: true) diff --git a/packages/flutter_tools/test/general.shard/ios/ios_project_migration_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_project_migration_test.dart index 4be588b2cdf..c0c591b7e18 100644 --- a/packages/flutter_tools/test/general.shard/ios/ios_project_migration_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/ios_project_migration_test.dart @@ -6,6 +6,7 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/project_migrator.dart'; +import 'package:flutter_tools/src/base/version.dart'; import 'package:flutter_tools/src/ios/migrations/project_base_configuration_migration.dart'; import 'package:flutter_tools/src/ios/migrations/project_build_location_migration.dart'; import 'package:flutter_tools/src/ios/migrations/remove_framework_link_and_embedding_migration.dart'; @@ -145,8 +146,7 @@ keep this 2 746232531E83B71900CC1A5E /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 746232521E83B71900CC1A5E /* App.framework */; }; '''); when(mockXcode.isInstalled).thenReturn(true); - when(mockXcode.majorVersion).thenReturn(11); - when(mockXcode.minorVersion).thenReturn(4); + when(mockXcode.currentVersion).thenReturn(Version(11, 4, 0)); final RemoveFrameworkLinkAndEmbeddingMigration iosProjectMigration = RemoveFrameworkLinkAndEmbeddingMigration( mockIosProject, @@ -164,8 +164,7 @@ keep this 2 9705A1C71CF904A300538480 /* Flutter.framework in Embed Frameworks */, '''); when(mockXcode.isInstalled).thenReturn(true); - when(mockXcode.majorVersion).thenReturn(11); - when(mockXcode.minorVersion).thenReturn(4); + when(mockXcode.currentVersion).thenReturn(Version(11, 4, 0)); final RemoveFrameworkLinkAndEmbeddingMigration iosProjectMigration = RemoveFrameworkLinkAndEmbeddingMigration( mockIosProject, @@ -198,8 +197,7 @@ keep this 2 746232531E83B71900CC1A5E /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 746232521E83B71900CC1A5E /* App.framework */; }; '''); when(mockXcode.isInstalled).thenReturn(true); - when(mockXcode.majorVersion).thenReturn(11); - when(mockXcode.minorVersion).thenReturn(3); + when(mockXcode.currentVersion).thenReturn(Version(11, 3, 0)); final RemoveFrameworkLinkAndEmbeddingMigration iosProjectMigration = RemoveFrameworkLinkAndEmbeddingMigration( mockIosProject, @@ -217,8 +215,7 @@ keep this 2 746232531E83B71900CC1A5E /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 746232521E83B71900CC1A5E /* App.framework */; }; '''); when(mockXcode.isInstalled).thenReturn(true); - when(mockXcode.majorVersion).thenReturn(11); - when(mockXcode.minorVersion).thenReturn(4); + when(mockXcode.currentVersion).thenReturn(Version(11, 4, 0)); final RemoveFrameworkLinkAndEmbeddingMigration iosProjectMigration = RemoveFrameworkLinkAndEmbeddingMigration( mockIosProject, @@ -235,8 +232,7 @@ keep this 2 746232531E83B71900CC1A5E /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 746232521E83B71900CC1A5E /* App.framework */; }; '''); when(mockXcode.isInstalled).thenReturn(true); - when(mockXcode.majorVersion).thenReturn(12); - when(mockXcode.minorVersion).thenReturn(0); + when(mockXcode.currentVersion).thenReturn(Version(12, 0, 0)); final RemoveFrameworkLinkAndEmbeddingMigration iosProjectMigration = RemoveFrameworkLinkAndEmbeddingMigration( mockIosProject, diff --git a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart index 6b921ec7b77..1ef1cbd913d 100644 --- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart @@ -70,7 +70,6 @@ void main() { final IOSSimulator simulator = IOSSimulator( '123', simControl: mockSimControl, - xcode: mockXcode, ); final DevicePortForwarder portForwarder = simulator.portForwarder; await portForwarder.forward(123); @@ -94,7 +93,6 @@ void main() { final IOSSimulator simulator = IOSSimulator( '123', simControl: MockSimControl(), - xcode: MockXcode(), ); expect(simulator.supportsRuntimeMode(BuildMode.debug), true); @@ -109,11 +107,9 @@ void main() { group('logFilePath', () { MockSimControl mockSimControl; - MockXcode mockXcode; setUp(() { mockSimControl = MockSimControl(); - mockXcode = MockXcode(); }); testUsingContext('defaults to rooted from HOME', () { @@ -121,7 +117,6 @@ void main() { final IOSSimulator simulator = IOSSimulator( '123', simControl: mockSimControl, - xcode: mockXcode, ); expect(simulator.logFilePath, '/foo/bar/Library/Logs/CoreSimulator/123/system.log'); }, overrides: { @@ -129,7 +124,6 @@ void main() { FileSystemUtils: () => fsUtils, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }, testOn: 'posix'); testUsingContext('respects IOS_SIMULATOR_LOG_FILE_PATH', () { @@ -138,7 +132,6 @@ void main() { final IOSSimulator simulator = IOSSimulator( '456', simControl: mockSimControl, - xcode: mockXcode, ); expect(simulator.logFilePath, '/baz/qux/456/system.log'); }, overrides: { @@ -146,7 +139,6 @@ void main() { FileSystemUtils: () => fsUtils, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); }); @@ -201,11 +193,9 @@ void main() { group('sdkMajorVersion', () { MockSimControl mockSimControl; - MockXcode mockXcode; setUp(() { mockSimControl = MockSimControl(); - mockXcode = MockXcode(); }); // This new version string appears in SimulatorApp-850 CoreSimulator-518.16 beta. @@ -215,7 +205,6 @@ void main() { name: 'iPhone SE', simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3', simControl: mockSimControl, - xcode: mockXcode, ); expect(await device.sdkMajorVersion, 11); @@ -227,7 +216,6 @@ void main() { name: 'iPhone SE', simulatorCategory: 'iOS 11.2', simControl: mockSimControl, - xcode: mockXcode, ); expect(await device.sdkMajorVersion, 11); @@ -239,7 +227,6 @@ void main() { name: 'iPhone SE', simulatorCategory: 'iOS 11.2', simControl: mockSimControl, - xcode: mockXcode, ); expect(device.category, Category.mobile); @@ -248,11 +235,9 @@ void main() { group('IOSSimulator.isSupported', () { MockSimControl mockSimControl; - MockXcode mockXcode; setUp(() { mockSimControl = MockSimControl(); - mockXcode = MockXcode(); }); testUsingContext('Apple TV is unsupported', () { @@ -260,14 +245,12 @@ void main() { 'x', name: 'Apple TV', simControl: mockSimControl, - xcode: mockXcode, ); expect(simulator.isSupported(), false); }, overrides: { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); testUsingContext('Apple Watch is unsupported', () { @@ -275,13 +258,11 @@ void main() { 'x', name: 'Apple Watch', simControl: mockSimControl, - xcode: mockXcode, ).isSupported(), false); }, overrides: { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); testUsingContext('iPad 2 is supported', () { @@ -289,13 +270,11 @@ void main() { 'x', name: 'iPad 2', simControl: mockSimControl, - xcode: mockXcode, ).isSupported(), true); }, overrides: { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); testUsingContext('iPad Retina is supported', () { @@ -303,13 +282,11 @@ void main() { 'x', name: 'iPad Retina', simControl: mockSimControl, - xcode: mockXcode, ).isSupported(), true); }, overrides: { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); testUsingContext('iPhone 5 is supported', () { @@ -317,13 +294,11 @@ void main() { 'x', name: 'iPhone 5', simControl: mockSimControl, - xcode: mockXcode, ).isSupported(), true); }, overrides: { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); testUsingContext('iPhone 5s is supported', () { @@ -331,13 +306,11 @@ void main() { 'x', name: 'iPhone 5s', simControl: mockSimControl, - xcode: mockXcode, ).isSupported(), true); }, overrides: { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); testUsingContext('iPhone SE is supported', () { @@ -345,13 +318,11 @@ void main() { 'x', name: 'iPhone SE', simControl: mockSimControl, - xcode: mockXcode, ).isSupported(), true); }, overrides: { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); testUsingContext('iPhone 7 Plus is supported', () { @@ -359,13 +330,11 @@ void main() { 'x', name: 'iPhone 7 Plus', simControl: mockSimControl, - xcode: mockXcode, ).isSupported(), true); }, overrides: { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); testUsingContext('iPhone X is supported', () { @@ -373,13 +342,11 @@ void main() { 'x', name: 'iPhone X', simControl: mockSimControl, - xcode: mockXcode, ).isSupported(), true); }, overrides: { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, }); }); @@ -410,26 +377,14 @@ void main() { 'x', name: 'iPhone SE', simControl: simControl, - xcode: mockXcode, ); when(mockXcode.xcrunCommand()).thenReturn(['xcrun']); }); - testWithoutContext( - "old Xcode doesn't support screenshot", - () { - when(mockXcode.majorVersion).thenReturn(7); - when(mockXcode.minorVersion).thenReturn(1); - expect(deviceUnderTest.supportsScreenshot, false); - }, - ); testWithoutContext( - 'Xcode 8.2+ supports screenshots', + 'supports screenshots', () async { - when(mockXcode.majorVersion).thenReturn(8); - when(mockXcode.minorVersion).thenReturn(2); - expect(deviceUnderTest.supportsScreenshot, true); final File screenshot = MemoryFileSystem.test().file('screenshot.png'); await deviceUnderTest.takeScreenshot(screenshot); verify(mockProcessManager.run( @@ -468,7 +423,6 @@ void main() { name: 'iPhone SE', simulatorCategory: 'iOS 9.3', simControl: mockSimControl, - xcode: mockXcode, ); await launchDeviceSystemLogTool(device); expect( @@ -484,7 +438,6 @@ void main() { fileSystem: fileSystem, platform: macosPlatform, ), - Xcode: () => mockXcode, }); testUsingContext('unified logging with app name', () async { @@ -493,7 +446,6 @@ void main() { name: 'iPhone SE', simulatorCategory: 'iOS 11.0', simControl: mockSimControl, - xcode: mockXcode, ); await launchDeviceUnifiedLogging(device, 'My Super Awesome App'); @@ -530,7 +482,6 @@ void main() { name: 'iPhone SE', simulatorCategory: 'iOS 11.0', simControl: mockSimControl, - xcode: mockXcode, ); await launchDeviceUnifiedLogging(device, null); @@ -557,7 +508,6 @@ void main() { overrides: { ProcessManager: () => mockProcessManager, FileSystem: () => fileSystem, - Xcode: () => mockXcode, }); }); @@ -597,7 +547,6 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' '123456', simulatorCategory: 'iOS 10.0', simControl: mockSimControl, - xcode: mockXcode, ); final DeviceLogReader logReader = device.getLogReader( app: await BuildableIOSApp.fromProject(mockIosProject, null), @@ -632,7 +581,6 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' '123456', simulatorCategory: 'iOS 10.3', simControl: mockSimControl, - xcode: mockXcode, ); final DeviceLogReader logReader = device.getLogReader( app: await BuildableIOSApp.fromProject(mockIosProject, null), @@ -680,7 +628,6 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' '123456', simulatorCategory: 'iOS 10.3', simControl: mockSimControl, - xcode: mockXcode, ); final DeviceLogReader logReader = device.getLogReader( app: await BuildableIOSApp.fromProject(mockIosProject, null), @@ -746,7 +693,6 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' '123456', simulatorCategory: 'iOS 11.0', simControl: mockSimControl, - xcode: mockXcode, ); final DeviceLogReader logReader = device.getLogReader( app: await BuildableIOSApp.fromProject(mockIosProject, null), @@ -761,7 +707,6 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' }, overrides: { ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, - Xcode: () => mockXcode, }); }); }); @@ -864,7 +809,6 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' name: 'Testo', simulatorCategory: 'NaN', simControl: simControl, - xcode: mockXcode, ); expect(await iosSimulatorA.sdkMajorVersion, 11); @@ -923,7 +867,6 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' name: 'iPhone SE', simulatorCategory: 'iOS 11.2', simControl: simControl, - xcode: mockXcode, ); when(globals.plistParser.getValueFromFile(any, any)).thenReturn('correct'); @@ -948,7 +891,6 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' name: 'iPhone SE', simulatorCategory: 'iOS 11.2', simControl: simControl, - xcode: mockXcode, ); final Directory mockDir = globals.fs.currentDirectory; @@ -991,7 +933,6 @@ flutter: final IOSSimulator simulator = IOSSimulator( 'test', simControl: mockSimControl, - xcode: mockXcode, ); expect(simulator.isSupportedForProject(flutterProject), true); }, overrides: { @@ -1010,7 +951,6 @@ flutter: final IOSSimulator simulator = IOSSimulator( 'test', simControl: mockSimControl, - xcode: mockXcode, ); expect(simulator.isSupportedForProject(flutterProject), true); }, overrides: { @@ -1027,7 +967,6 @@ flutter: final IOSSimulator simulator = IOSSimulator( 'test', simControl: mockSimControl, - xcode: mockXcode, ); expect(simulator.isSupportedForProject(flutterProject), false); }, overrides: { @@ -1040,7 +979,6 @@ flutter: final IOSSimulator simulator = IOSSimulator( 'test', simControl: mockSimControl, - xcode: mockXcode, ); expect(simulator.createDevFSWriter(null, ''), isA()); diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart index f6d2c256b19..68f4af6b9ef 100644 --- a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart @@ -221,13 +221,13 @@ void main() { when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0); - expect(xcode.isVersionSatisfactory, isFalse); + expect(xcode.isRequiredVersionSatisfactory, isFalse); }); testWithoutContext('xcodeVersionSatisfactory is false when xcodebuild tools are not installed', () { when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false); - expect(xcode.isVersionSatisfactory, isFalse); + expect(xcode.isRequiredVersionSatisfactory, isFalse); }); testWithoutContext('xcodeVersionSatisfactory is true when version meets minimum', () { @@ -236,7 +236,7 @@ void main() { when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0); - expect(xcode.isVersionSatisfactory, isTrue); + expect(xcode.isRequiredVersionSatisfactory, isTrue); }); testWithoutContext('xcodeVersionSatisfactory is true when major version exceeds minimum', () { @@ -245,7 +245,7 @@ void main() { when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0); - expect(xcode.isVersionSatisfactory, isTrue); + expect(xcode.isRequiredVersionSatisfactory, isTrue); }); testWithoutContext('xcodeVersionSatisfactory is true when minor version exceeds minimum', () { @@ -254,7 +254,7 @@ void main() { when(mockXcodeProjectInterpreter.minorVersion).thenReturn(3); when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0); - expect(xcode.isVersionSatisfactory, isTrue); + expect(xcode.isRequiredVersionSatisfactory, isTrue); }); testWithoutContext('xcodeVersionSatisfactory is true when patch version exceeds minimum', () { @@ -263,7 +263,58 @@ void main() { when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.patchVersion).thenReturn(1); - expect(xcode.isVersionSatisfactory, isTrue); + expect(xcode.isRequiredVersionSatisfactory, isTrue); + }); + + testWithoutContext('isRecommendedVersionSatisfactory is false when version is less than minimum', () { + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0); + + expect(xcode.isRecommendedVersionSatisfactory, isFalse); + }); + + testWithoutContext('isRecommendedVersionSatisfactory is false when xcodebuild tools are not installed', () { + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false); + + expect(xcode.isRecommendedVersionSatisfactory, isFalse); + }); + + testWithoutContext('isRecommendedVersionSatisfactory is true when version meets minimum', () { + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(12); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + when(mockXcodeProjectInterpreter.patchVersion).thenReturn(1); + + expect(xcode.isRecommendedVersionSatisfactory, isTrue); + }); + + testWithoutContext('isRecommendedVersionSatisfactory is true when major version exceeds minimum', () { + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(13); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0); + + expect(xcode.isRecommendedVersionSatisfactory, isTrue); + }); + + testWithoutContext('isRecommendedVersionSatisfactory is true when minor version exceeds minimum', () { + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(12); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(3); + when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0); + + expect(xcode.isRecommendedVersionSatisfactory, isTrue); + }); + + testWithoutContext('isRecommendedVersionSatisfactory is true when patch version exceeds minimum', () { + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(12); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + when(mockXcodeProjectInterpreter.patchVersion).thenReturn(2); + + expect(xcode.isRecommendedVersionSatisfactory, isTrue); }); testWithoutContext('isInstalledAndMeetsVersionCheck is false when not installed', () { diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_validator_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_validator_test.dart index 29675a350d2..d65c164c128 100644 --- a/packages/flutter_tools/test/general.shard/macos/xcode_validator_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/xcode_validator_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter_tools/src/base/user_messages.dart'; +import 'package:flutter_tools/src/base/version.dart'; import 'package:flutter_tools/src/doctor.dart'; import 'package:flutter_tools/src/macos/xcode.dart'; import 'package:flutter_tools/src/macos/xcode_validator.dart'; @@ -40,12 +41,32 @@ void main() { when(xcode.isInstalled).thenReturn(true); when(xcode.versionText) .thenReturn('Xcode 7.0.1\nBuild version 7C1002\n'); + when(xcode.currentVersion).thenReturn(Version(7, 0, 1)); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(false); + when(xcode.isRecommendedVersionSatisfactory).thenReturn(false); when(xcode.eulaSigned).thenReturn(true); when(xcode.isSimctlInstalled).thenReturn(true); final XcodeValidator validator = XcodeValidator(xcode: xcode, userMessages: UserMessages()); final ValidationResult result = await validator.validate(); expect(result.type, ValidationType.partial); + expect(result.messages.last.type, ValidationMessageType.error); + expect(result.messages.last.message, contains('Xcode 7.0.1 out of date (12.0.1 is recommended)')); + }); + + testWithoutContext('Emits partial status when Xcode below recommended version', () async { + when(xcode.isInstalled).thenReturn(true); + when(xcode.versionText) + .thenReturn('Xcode 11.0\nBuild version 11A420a\n'); + when(xcode.currentVersion).thenReturn(Version(11, 0, 0)); + when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); + when(xcode.isRecommendedVersionSatisfactory).thenReturn(false); + when(xcode.eulaSigned).thenReturn(true); + when(xcode.isSimctlInstalled).thenReturn(true); + final XcodeValidator validator = XcodeValidator(xcode: xcode, userMessages: UserMessages()); + final ValidationResult result = await validator.validate(); + expect(result.type, ValidationType.partial); + expect(result.messages.last.type, ValidationMessageType.hint); + expect(result.messages.last.message, contains('Xcode 11.0.0 out of date (12.0.1 is recommended)')); }); testWithoutContext('Emits partial status when Xcode EULA not signed', () async { @@ -53,6 +74,7 @@ void main() { when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); + when(xcode.isRecommendedVersionSatisfactory).thenReturn(true); when(xcode.eulaSigned).thenReturn(false); when(xcode.isSimctlInstalled).thenReturn(true); final XcodeValidator validator = XcodeValidator(xcode: xcode, userMessages: UserMessages()); @@ -65,6 +87,7 @@ void main() { when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); + when(xcode.isRecommendedVersionSatisfactory).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); when(xcode.isSimctlInstalled).thenReturn(false); final XcodeValidator validator = XcodeValidator(xcode: xcode, userMessages: UserMessages()); @@ -78,6 +101,7 @@ void main() { when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); + when(xcode.isRecommendedVersionSatisfactory).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); when(xcode.isSimctlInstalled).thenReturn(true); final XcodeValidator validator = XcodeValidator(xcode: xcode, userMessages: UserMessages());