V1.9.1 hotfixes (#42863)
* refactor cocoapods validator to detect broken install (#38560)
* Skip pod initialization if version >= 1.8.0. (#41491)
diff --git a/packages/flutter_tools/lib/src/base/user_messages.dart b/packages/flutter_tools/lib/src/base/user_messages.dart
index 1764483..a1f1d91 100644
--- a/packages/flutter_tools/lib/src/base/user_messages.dart
+++ b/packages/flutter_tools/lib/src/base/user_messages.dart
@@ -172,6 +172,11 @@
'$consequence\n'
'To upgrade:\n'
'$upgradeInstructions';
+ String cocoaPodsBrokenInstall(String consequence, String reinstallInstructions) =>
+ 'CocoaPods installed but not working.\n'
+ '$consequence\n'
+ 'To re-install CocoaPods, run:\n'
+ '$reinstallInstructions';
// Messages used in VsCodeValidator
String vsCodeVersion(String version) => 'version $version';
diff --git a/packages/flutter_tools/lib/src/macos/cocoapods.dart b/packages/flutter_tools/lib/src/macos/cocoapods.dart
index 9729387..10e5448 100644
--- a/packages/flutter_tools/lib/src/macos/cocoapods.dart
+++ b/packages/flutter_tools/lib/src/macos/cocoapods.dart
@@ -29,13 +29,16 @@
Flutter is unable to determine the installed CocoaPods's version.
Ensure that the output of 'pod --version' contains only digits and . to be recognized by Flutter.''';
+const String brokenCocoaPodsConsequence = '''
+ You appear to have CocoaPods installed but it is not working.
+ This can happen if the version of Ruby that CocoaPods was installed with is different from the one being used to invoke it.
+ This can usually be fixed by re-installing CocoaPods. For more info, see https://github.com/flutter/flutter/issues/14293.''';
+
const String cocoaPodsInstallInstructions = '''
- sudo gem install cocoapods
- pod setup''';
+ sudo gem install cocoapods''';
const String cocoaPodsUpgradeInstructions = '''
- sudo gem install cocoapods
- pod setup''';
+ sudo gem install cocoapods''';
CocoaPods get cocoaPods => context.get<CocoaPods>();
@@ -52,6 +55,8 @@
belowRecommendedVersion,
/// Everything should be fine.
recommended,
+ /// iOS plugins will not work, re-install required.
+ brokenInstall,
}
class CocoaPods {
@@ -60,6 +65,8 @@
String get cocoaPodsMinimumVersion => '1.6.0';
String get cocoaPodsRecommendedVersion => '1.6.0';
+ Future<bool> get isInstalled => exitsHappyAsync(<String>['which', 'pod']);
+
Future<String> get cocoaPodsVersionText {
_versionText ??= runAsync(<String>['pod', '--version']).then<String>((RunResult result) {
return result.exitCode == 0 ? result.stdout.trim() : null;
@@ -68,9 +75,13 @@
}
Future<CocoaPodsStatus> get evaluateCocoaPodsInstallation async {
- final String versionText = await cocoaPodsVersionText;
- if (versionText == null)
+ if (!(await isInstalled)) {
return CocoaPodsStatus.notInstalled;
+ }
+ final String versionText = await cocoaPodsVersionText;
+ if (versionText == null) {
+ return CocoaPodsStatus.brokenInstall;
+ }
try {
final Version installedVersion = Version.parse(versionText);
if (installedVersion == null)
@@ -89,12 +100,20 @@
/// Whether CocoaPods ran 'pod setup' once where the costly pods' specs are
/// cloned.
///
+ /// Versions >= 1.8.0 do not require 'pod setup' and default to a CDN instead
+ /// of a locally cloned repository.
+ /// See http://blog.cocoapods.org/CocoaPods-1.8.0-beta/
+ ///
/// A user can override the default location via the CP_REPOS_DIR environment
/// variable.
///
/// See https://github.com/CocoaPods/CocoaPods/blob/master/lib/cocoapods/config.rb#L138
/// for details of this variable.
- Future<bool> get isCocoaPodsInitialized {
+ Future<bool> get isCocoaPodsInitialized async {
+ final Version installedVersion = Version.parse(await cocoaPodsVersionText);
+ if (installedVersion != null && installedVersion >= Version.parse('1.8.0')) {
+ return true;
+ }
final String cocoapodsReposDir = platform.environment['CP_REPOS_DIR'] ?? fs.path.join(homeDirPath, '.cocoapods', 'repos');
return fs.isDirectory(fs.path.join(cocoapodsReposDir, 'master'));
}
diff --git a/packages/flutter_tools/lib/src/macos/cocoapods_validator.dart b/packages/flutter_tools/lib/src/macos/cocoapods_validator.dart
index aaee745..ce57632 100644
--- a/packages/flutter_tools/lib/src/macos/cocoapods_validator.dart
+++ b/packages/flutter_tools/lib/src/macos/cocoapods_validator.dart
@@ -34,6 +34,11 @@
status = ValidationType.missing;
messages.add(ValidationMessage.error(
userMessages.cocoaPodsMissing(noCocoaPodsConsequence, cocoaPodsInstallInstructions)));
+ } else if (cocoaPodsStatus == CocoaPodsStatus.brokenInstall) {
+ status = ValidationType.missing;
+ messages.add(ValidationMessage.error(
+ userMessages.cocoaPodsBrokenInstall(brokenCocoaPodsConsequence, cocoaPodsInstallInstructions)));
+
} else if (cocoaPodsStatus == CocoaPodsStatus.unknownVersion) {
status = ValidationType.partial;
messages.add(ValidationMessage.hint(
diff --git a/packages/flutter_tools/templates/cocoapods/Podfile-ios-objc b/packages/flutter_tools/templates/cocoapods/Podfile-ios-objc
index 1de1efc..64ba749 100644
--- a/packages/flutter_tools/templates/cocoapods/Podfile-ios-objc
+++ b/packages/flutter_tools/templates/cocoapods/Podfile-ios-objc
@@ -1,6 +1,3 @@
-# Using a CDN with CocoaPods 1.7.2 or later can save a lot of time on pod installation, but it's experimental rather than the default.
-# source 'https://cdn.cocoapods.org/'
-
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
diff --git a/packages/flutter_tools/templates/cocoapods/Podfile-ios-swift b/packages/flutter_tools/templates/cocoapods/Podfile-ios-swift
index e5e1440..e9286cb 100644
--- a/packages/flutter_tools/templates/cocoapods/Podfile-ios-swift
+++ b/packages/flutter_tools/templates/cocoapods/Podfile-ios-swift
@@ -1,6 +1,3 @@
-# Using a CDN with CocoaPods 1.7.2 or later can save a lot of time on pod installation, but it's experimental rather than the default.
-# source 'https://cdn.cocoapods.org/'
-
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
diff --git a/packages/flutter_tools/templates/cocoapods/Podfile-macos b/packages/flutter_tools/templates/cocoapods/Podfile-macos
index 23f5c12..6bb04df 100644
--- a/packages/flutter_tools/templates/cocoapods/Podfile-macos
+++ b/packages/flutter_tools/templates/cocoapods/Podfile-macos
@@ -1,6 +1,3 @@
-# Using a CDN with CocoaPods 1.7.2 or later can save a lot of time on pod installation, but it's experimental rather than the default.
-# source 'https://cdn.cocoapods.org/'
-
platform :osx, '10.11'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
diff --git a/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart b/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart
index 068b87a..dda5340 100644
--- a/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart
+++ b/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart
@@ -30,10 +30,6 @@
CocoaPods cocoaPodsUnderTest;
InvokeProcess resultOfPodVersion;
- void pretendPodIsNotInstalled() {
- resultOfPodVersion = () async => throw 'Executable does not exist';
- }
-
void pretendPodVersionFails() {
resultOfPodVersion = () async => exitsWithError();
}
@@ -93,6 +89,22 @@
)).thenAnswer((_) async => exitsHappy());
});
+ void pretendPodIsNotInstalled() {
+ when(mockProcessManager.run(
+ <String>['which', 'pod'],
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'),
+ )).thenAnswer((_) async => exitsWithError());
+ }
+
+ void pretendPodIsInstalled() {
+ when(mockProcessManager.run(
+ <String>['which', 'pod'],
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'),
+ )).thenAnswer((_) async => exitsHappy());
+ }
+
group('Evaluate installation', () {
testUsingContext('detects not installed, if pod exec does not exist', () async {
pretendPodIsNotInstalled();
@@ -101,14 +113,16 @@
ProcessManager: () => mockProcessManager,
});
- testUsingContext('detects not installed, if pod version fails', () async {
+ testUsingContext('detects not installed, if pod is installed but version fails', () async {
+ pretendPodIsInstalled();
pretendPodVersionFails();
- expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.notInstalled);
+ expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.brokenInstall);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('detects installed', () async {
+ pretendPodIsInstalled();
pretendPodVersionIs('0.0.1');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, isNot(CocoaPodsStatus.notInstalled));
}, overrides: <Type, Generator>{
@@ -116,6 +130,7 @@
});
testUsingContext('detects unknown version', () async {
+ pretendPodIsInstalled();
pretendPodVersionIs('Plugin loaded.\n1.5.3');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.unknownVersion);
}, overrides: <Type, Generator>{
@@ -123,6 +138,7 @@
});
testUsingContext('detects below minimum version', () async {
+ pretendPodIsInstalled();
pretendPodVersionIs('1.5.0');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.belowMinimumVersion);
}, overrides: <Type, Generator>{
@@ -130,6 +146,7 @@
});
testUsingContext('detects at recommended version', () async {
+ pretendPodIsInstalled();
pretendPodVersionIs('1.6.0');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.recommended);
}, overrides: <Type, Generator>{
@@ -137,11 +154,22 @@
});
testUsingContext('detects above recommended version', () async {
+ pretendPodIsInstalled();
pretendPodVersionIs('1.6.1');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.recommended);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
+
+ testUsingContext('detects initialized over 1.8.0', () async {
+ pretendPodIsInstalled();
+ pretendPodVersionIs('1.8.0');
+ expect(await cocoaPodsUnderTest.isCocoaPodsInitialized, isTrue);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ Platform: () => FakePlatform(),
+ FileSystem: () => fs,
+ });
});
group('Setup Podfile', () {
@@ -279,6 +307,7 @@
});
testUsingContext('throws, if Podfile is missing.', () async {
+ pretendPodIsInstalled();
try {
await cocoaPodsUnderTest.processPods(
xcodeProject: projectUnderTest.ios,
@@ -299,6 +328,7 @@
});
testUsingContext('throws, if specs repo is outdated.', () async {
+ pretendPodIsInstalled();
fs.file(fs.path.join('project', 'ios', 'Podfile'))
..createSync()
..writeAsStringSync('Existing Podfile');
@@ -345,6 +375,7 @@
});
testUsingContext('run pod install, if Podfile.lock is missing', () async {
+ pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
@@ -368,6 +399,7 @@
});
testUsingContext('runs pod install, if Manifest.lock is missing', () async {
+ pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
@@ -394,6 +426,7 @@
});
testUsingContext('runs pod install, if Manifest.lock different from Podspec.lock', () async {
+ pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
@@ -423,6 +456,7 @@
});
testUsingContext('runs pod install, if flutter framework changed', () async {
+ pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
@@ -452,6 +486,7 @@
});
testUsingContext('runs pod install, if Podfile.lock is older than Podfile', () async {
+ pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
@@ -483,6 +518,7 @@
});
testUsingContext('skips pod install, if nothing changed', () async {
+ pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
@@ -509,6 +545,7 @@
});
testUsingContext('a failed pod install deletes Pods/Manifest.lock', () async {
+ pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
..writeAsStringSync('Existing Podfile');
@@ -559,6 +596,7 @@
});
testUsingContext('succeeds, if specs repo is in CP_REPOS_DIR.', () async {
+ pretendPodIsInstalled();
fs.file(fs.path.join('project', 'ios', 'Podfile'))
..createSync()
..writeAsStringSync('Existing Podfile');