Rerun pod install on changed Xcode project, Podfile (#17274)
If the developer changes their Xcode build settings and their project
has plugins, pod install is required, (e.g. to pick up changes to the
target architecture).
Similarly, manual edits to the Podfile should trigger a pod install.
diff --git a/packages/flutter_tools/lib/src/ios/cocoapods.dart b/packages/flutter_tools/lib/src/ios/cocoapods.dart
index 665c0b6..e6c9b09 100644
--- a/packages/flutter_tools/lib/src/ios/cocoapods.dart
+++ b/packages/flutter_tools/lib/src/ios/cocoapods.dart
@@ -58,21 +58,23 @@
/// Whether CocoaPods ran 'pod setup' once where the costly pods' specs are cloned.
Future<bool> get isCocoaPodsInitialized => fs.isDirectory(fs.path.join(homeDirPath, '.cocoapods', 'repos', 'master'));
- Future<Null> processPods({
+ Future<bool> processPods({
@required Directory appIosDirectory,
// For backward compatibility with previously created Podfile only.
@required String iosEngineDir,
bool isSwift: false,
- bool flutterPodChanged: true,
+ bool dependenciesChanged: true,
}) async {
if (!(await appIosDirectory.childFile('Podfile').exists())) {
throwToolExit('Podfile missing');
}
if (await _checkPodCondition()) {
- if (_shouldRunPodInstall(appIosDirectory, flutterPodChanged)) {
+ if (_shouldRunPodInstall(appIosDirectory, dependenciesChanged)) {
await _runPodInstall(appIosDirectory, iosEngineDir);
+ return true;
}
}
+ return false;
}
/// Make sure the CocoaPods tools are in the right states.
@@ -157,8 +159,8 @@
// 2. The podfile.lock doesn't exist
// 3. The Pods/Manifest.lock doesn't exist (It is deleted when plugins change)
// 4. The podfile.lock doesn't match Pods/Manifest.lock.
- bool _shouldRunPodInstall(Directory appIosDirectory, bool flutterPodChanged) {
- if (flutterPodChanged)
+ bool _shouldRunPodInstall(Directory appIosDirectory, bool dependenciesChanged) {
+ if (dependenciesChanged)
return true;
// Check if podfile.lock and Pods/Manifest.lock exist and match.
final File podfileLockFile = appIosDirectory.childFile('Podfile.lock');
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index dc1320a..2ea5189 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -11,6 +11,7 @@
import '../base/common.dart';
import '../base/context.dart';
import '../base/file_system.dart';
+import '../base/fingerprint.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/os.dart';
@@ -193,7 +194,7 @@
bool codesign: true,
bool usesTerminalUi: true,
}) async {
- if (!await upgradePbxProjWithFlutterAssets(app.name))
+ if (!await upgradePbxProjWithFlutterAssets(app.name, app.appDirectory))
return new XcodeBuildResult(success: false);
if (!_checkXcodeVersion())
@@ -241,7 +242,6 @@
// copied over to a location that is suitable for Xcodebuild to find them.
final Directory appDirectory = fs.directory(app.appDirectory);
await _addServicesToBundle(appDirectory);
- final String previousGeneratedXcconfig = readGeneratedXcconfig(app.appDirectory);
updateGeneratedXcodeProperties(
projectPath: fs.currentDirectory.path,
@@ -251,13 +251,26 @@
);
if (hasPlugins()) {
- final String currentGeneratedXcconfig = readGeneratedXcconfig(app.appDirectory);
- await cocoaPods.processPods(
+ final String iosPath = fs.path.join(fs.currentDirectory.path, app.appDirectory);
+ // If the Xcode project, Podfile, or Generated.xcconfig have changed since
+ // last run, pods should be updated.
+ final Fingerprinter fingerprinter = new Fingerprinter(
+ fingerprintPath: fs.path.join(getIosBuildDirectory(), 'pod_inputs.fingerprint'),
+ paths: <String>[
+ _getPbxProjPath(app.appDirectory),
+ fs.path.join(iosPath, 'Podfile'),
+ fs.path.join(iosPath, 'Flutter', 'Generated.xcconfig'),
+ ],
+ properties: <String, String>{},
+ );
+ final bool didPodInstall = await cocoaPods.processPods(
appIosDirectory: appDirectory,
iosEngineDir: flutterFrameworkDir(buildInfo.mode),
isSwift: app.isSwift,
- flutterPodChanged: previousGeneratedXcconfig != currentGeneratedXcconfig,
+ dependenciesChanged: !await fingerprinter.doesFingerprintMatch()
);
+ if (didPodInstall)
+ await fingerprinter.writeFingerprint();
}
// If buildNumber is not specified, keep the project untouched.
@@ -614,6 +627,9 @@
}
}
+/// The path of the Xcode project file.
+String _getPbxProjPath(String appPath) => fs.path.join(fs.currentDirectory.path, appPath, 'Runner.xcodeproj', 'project.pbxproj');
+
void _copyServiceDefinitionsManifest(List<Map<String, String>> services, File manifest) {
printTrace("Creating service definitions manifest at '${manifest.path}'");
final List<Map<String, String>> jsonServices = services.map((Map<String, String> service) => <String, String>{
@@ -626,9 +642,8 @@
manifest.writeAsStringSync(json.encode(jsonObject), mode: FileMode.write, flush: true);
}
-Future<bool> upgradePbxProjWithFlutterAssets(String app) async {
- final File xcodeProjectFile = fs.file(fs.path.join('ios', 'Runner.xcodeproj',
- 'project.pbxproj'));
+Future<bool> upgradePbxProjWithFlutterAssets(String app, String appPath) async {
+ final File xcodeProjectFile = fs.file(_getPbxProjPath(appPath));
assert(await xcodeProjectFile.exists());
final List<String> lines = await xcodeProjectFile.readAsLines();
@@ -651,7 +666,7 @@
if (!lines.contains(l1) || !lines.contains(l3) ||
!lines.contains(l5) || !lines.contains(l7)) {
printError('Automatic upgrade of project.pbxproj failed.');
- printError(' To manually upgrade, open ios/Runner.xcodeproj/project.pbxproj:');
+ printError(' To manually upgrade, open ${xcodeProjectFile.path}:');
printError(' Add the following line in the "PBXBuildFile" section');
printError(l2);
printError(' Add the following line in the "PBXFileReference" section');
diff --git a/packages/flutter_tools/test/ios/cocoapods_test.dart b/packages/flutter_tools/test/ios/cocoapods_test.dart
index 5b37675..41a68a1 100644
--- a/packages/flutter_tools/test/ios/cocoapods_test.dart
+++ b/packages/flutter_tools/test/ios/cocoapods_test.dart
@@ -128,7 +128,7 @@
testUsingContext('prints error, if CocoaPods is not installed', () async {
projectUnderTest.childFile('Podfile').createSync();
cocoaPodsUnderTest = const TestCocoaPods(false);
- await cocoaPodsUnderTest.processPods(
+ final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosEngineDir: 'engine/path',
);
@@ -139,6 +139,7 @@
));
expect(testLogger.errorText, contains('not installed'));
expect(testLogger.errorText, contains('Skipping pod install'));
+ expect(didInstall, isFalse);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
@@ -221,11 +222,12 @@
projectUnderTest.childFile('Pods/Manifest.lock')
..createSync(recursive: true)
..writeAsString('Existing lock file.');
- await cocoaPodsUnderTest.processPods(
+ final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosEngineDir: 'engine/path',
- flutterPodChanged: false,
+ dependenciesChanged: false,
);
+ expect(didInstall, isTrue);
verify(mockProcessManager.run(
<String>['pod', 'install', '--verbose'],
workingDirectory: 'project/ios',
@@ -243,11 +245,12 @@
projectUnderTest.childFile('Podfile.lock')
..createSync()
..writeAsString('Existing lock file.');
- await cocoaPodsUnderTest.processPods(
+ final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosEngineDir: 'engine/path',
- flutterPodChanged: false,
+ dependenciesChanged: false,
);
+ expect(didInstall, isTrue);
verify(mockProcessManager.run(
<String>['pod', 'install', '--verbose'],
workingDirectory: 'project/ios',
@@ -271,11 +274,12 @@
projectUnderTest.childFile('Pods/Manifest.lock')
..createSync(recursive: true)
..writeAsString('Different lock file.');
- await cocoaPodsUnderTest.processPods(
+ final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosEngineDir: 'engine/path',
- flutterPodChanged: false,
+ dependenciesChanged: false,
);
+ expect(didInstall, isTrue);
verify(mockProcessManager.run(
<String>['pod', 'install', '--verbose'],
workingDirectory: 'project/ios',
@@ -299,11 +303,12 @@
projectUnderTest.childFile('Pods/Manifest.lock')
..createSync(recursive: true)
..writeAsString('Existing lock file.');
- await cocoaPodsUnderTest.processPods(
+ final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosEngineDir: 'engine/path',
- flutterPodChanged: true,
+ dependenciesChanged: true,
);
+ expect(didInstall, isTrue);
verify(mockProcessManager.run(
<String>['pod', 'install', '--verbose'],
workingDirectory: 'project/ios',
@@ -327,11 +332,12 @@
projectUnderTest.childFile('Pods/Manifest.lock')
..createSync(recursive: true)
..writeAsString('Existing lock file.');
- await cocoaPodsUnderTest.processPods(
+ final bool didInstall = await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosEngineDir: 'engine/path',
- flutterPodChanged: false,
+ dependenciesChanged: false,
);
+ expect(didInstall, isFalse);
verifyNever(mockProcessManager.run(
typed<List<String>>(any),
workingDirectory: any,
@@ -370,7 +376,7 @@
await cocoaPodsUnderTest.processPods(
appIosDirectory: projectUnderTest,
iosEngineDir: 'engine/path',
- flutterPodChanged: true,
+ dependenciesChanged: true,
);
fail('Tool throw expected when pod install fails');
} on ToolExit {
@@ -406,4 +412,4 @@
0, // exitCode
'', // stdout
'', // stderr
-);
\ No newline at end of file
+);