Unverified Commit cdbdafa8 authored by Chris Bracken's avatar Chris Bracken Committed by GitHub

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.
parent b2c98f9a
......@@ -58,21 +58,23 @@ class CocoaPods {
/// 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 @@ class CocoaPods {
// 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');
......
......@@ -11,6 +11,7 @@ import '../application_package.dart';
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 @@ Future<XcodeBuildResult> buildXcodeProject({
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 @@ Future<XcodeBuildResult> buildXcodeProject({
// 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 @@ Future<XcodeBuildResult> buildXcodeProject({
);
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 @@ Future<Null> _copyServiceFrameworks(List<Map<String, String>> services, Director
}
}
/// 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 @@ void _copyServiceDefinitionsManifest(List<Map<String, String>> services, File ma
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 @@ Future<bool> upgradePbxProjWithFlutterAssets(String app) async {
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');
......
......@@ -128,7 +128,7 @@ void main() {
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 @@ void main() {
));
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 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
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 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
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 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
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 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
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 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
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 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
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 @@ final ProcessResult exitsHappy = new ProcessResult(
0, // exitCode
'', // stdout
'', // stderr
);
\ No newline at end of file
);
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