Revert pod install skip revert (#13774)

* Revert "Revert "Enable developers to run pod from terminal directly and skip pod install if possible. (#13374)" (#13770)"

This reverts commit 0759043e47395b692bff7d6aa045c1ecb06627eb.

* some nits on cocoapods code

* put back the FLUTTER_FRAMEWORK_DIR env variable
diff --git a/packages/flutter_tools/lib/src/commands/inject_plugins.dart b/packages/flutter_tools/lib/src/commands/inject_plugins.dart
index bf53cb5..f1df432 100644
--- a/packages/flutter_tools/lib/src/commands/inject_plugins.dart
+++ b/packages/flutter_tools/lib/src/commands/inject_plugins.dart
@@ -24,7 +24,7 @@
 
   @override
   Future<Null> runCommand() async {
-    final bool result = injectPlugins();
+    final bool result = injectPlugins().hasPlugin;
     if (result) {
       printStatus('GeneratedPluginRegistrants successfully written.');
     } else {
diff --git a/packages/flutter_tools/lib/src/ios/cocoapods.dart b/packages/flutter_tools/lib/src/ios/cocoapods.dart
index 78fd425..9ebe95a 100644
--- a/packages/flutter_tools/lib/src/ios/cocoapods.dart
+++ b/packages/flutter_tools/lib/src/ios/cocoapods.dart
@@ -57,18 +57,21 @@
 
   Future<Null> processPods({
     @required Directory appIosDir,
+    // For backward compatibility with previously created Podfile only.
     @required String iosEngineDir,
     bool isSwift: false,
+    bool pluginOrFlutterPodChanged: true,
   }) async {
     if (await _checkPodCondition()) {
       if (!fs.file(fs.path.join(appIosDir.path, 'Podfile')).existsSync()) {
         await _createPodfile(appIosDir, isSwift);
       } // TODO(xster): Add more logic for handling merge conflicts.
-
-      await _runPodInstall(appIosDir, iosEngineDir);
+      if (_shouldRunPodInstall(appIosDir.path, pluginOrFlutterPodChanged))
+        await _runPodInstall(appIosDir, iosEngineDir);
     }
   }
 
+  /// Make sure the CocoaPods tools are in the right states.
   Future<bool> _checkPodCondition() async {
     if (!await isCocoaPodsInstalledAndMeetsVersionCheck) {
       final String minimumVersion = cocoaPodsMinimumVersion;
@@ -108,12 +111,36 @@
     podfileTemplate.copySync(fs.path.join(bundle.path, 'Podfile'));
   }
 
+  // Check if you need to run pod install.
+  // The pod install will run if any of below is true.
+  // 1. Any plugins changed (add/update/delete)
+  // 2. The flutter.framework has changed (debug/release/profile)
+  // 3. The podfile.lock doesn't exists
+  // 4. The Pods/manifest.lock doesn't exists
+  // 5. The podfile.lock doesn't match Pods/manifest.lock.
+  bool _shouldRunPodInstall(String appDir, bool pluginOrFlutterPodChanged) {
+    if (pluginOrFlutterPodChanged)
+      return true;
+    // Check if podfile.lock and Pods/Manifest.lock exists and matches.
+    final File podfileLockFile = fs.file(fs.path.join(appDir, 'Podfile.lock'));
+    final File manifestLockFile =
+        fs.file(fs.path.join(appDir, 'Pods', 'Manifest.lock'));
+    if (!podfileLockFile.existsSync()
+        || !manifestLockFile.existsSync()
+        || podfileLockFile.readAsStringSync() !=
+            manifestLockFile.readAsStringSync()) {
+      return true;
+    }
+    return false;
+  }
+
   Future<Null> _runPodInstall(Directory bundle, String engineDirectory) async {
     final Status status = logger.startProgress('Running pod install...', expectSlowOperation: true);
     final ProcessResult result = await processManager.run(
       <String>['pod', 'install', '--verbose'],
       workingDirectory: bundle.path,
       environment: <String, String>{
+        // For backward compatibility with previously created Podfile only.
         'FLUTTER_FRAMEWORK_DIR': engineDirectory,
         // See https://github.com/flutter/flutter/issues/10873.
         // CocoaPods analytics adds a lot of latency.
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index e3ead3b..2ca8109 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -254,14 +254,9 @@
   // copied over to a location that is suitable for Xcodebuild to find them.
   final Directory appDirectory = fs.directory(app.appDirectory);
   await _addServicesToBundle(appDirectory);
-  final bool hasFlutterPlugins = injectPlugins();
-
-  if (hasFlutterPlugins)
-    await cocoaPods.processPods(
-      appIosDir: appDirectory,
-      iosEngineDir: flutterFrameworkDir(buildInfo.mode),
-      isSwift: app.isSwift,
-    );
+  final InjectPluginsResult injectPluginsResult = injectPlugins();
+  final bool hasFlutterPlugins = injectPluginsResult.hasPlugin;
+  final String previousGeneratedXcconfig = readGeneratedXcconfig(app.appDirectory);
 
   updateXcodeGeneratedProperties(
     projectPath: fs.currentDirectory.path,
@@ -271,6 +266,17 @@
     previewDart2: buildInfo.previewDart2,
   );
 
+  if (hasFlutterPlugins) {
+    final String currentGeneratedXcconfig = readGeneratedXcconfig(app.appDirectory);
+    await cocoaPods.processPods(
+        appIosDir: appDirectory,
+        iosEngineDir: flutterFrameworkDir(buildInfo.mode),
+        isSwift: app.isSwift,
+        pluginOrFlutterPodChanged: (injectPluginsResult.hasChanged
+            || previousGeneratedXcconfig != currentGeneratedXcconfig),
+    );
+  }
+
   final List<String> commands = <String>[
     '/usr/bin/env',
     'xcrun',
@@ -355,7 +361,17 @@
   }
 }
 
-Future<Null> diagnoseXcodeBuildFailure(XcodeBuildResult result, BuildableIOSApp app) async {
+String readGeneratedXcconfig(String appPath) {
+  final String generatedXcconfigPath =
+      fs.path.join(fs.currentDirectory.path, appPath, 'Flutter','Generated.xcconfig');
+  final File generatedXcconfigFile = fs.file(generatedXcconfigPath);
+  if (!generatedXcconfigFile.existsSync())
+    return null;
+  return generatedXcconfigFile.readAsStringSync();
+}
+
+Future<Null> diagnoseXcodeBuildFailure(
+    XcodeBuildResult result, BuildableIOSApp app) async {
   if (result.xcodeBuildExecution != null &&
       result.xcodeBuildExecution.buildForPhysicalDevice &&
       result.stdout?.contains('BCEROR') == true &&
@@ -369,7 +385,8 @@
       // Make sure the user has specified one of:
       // DEVELOPMENT_TEAM (automatic signing)
       // PROVISIONING_PROFILE (manual signing)
-      !(app.buildSettings?.containsKey('DEVELOPMENT_TEAM')) == true || app.buildSettings?.containsKey('PROVISIONING_PROFILE') == true) {
+      !(app.buildSettings?.containsKey('DEVELOPMENT_TEAM')) == true
+          || app.buildSettings?.containsKey('PROVISIONING_PROFILE') == true) {
     printError(noDevelopmentTeamInstruction, emphasis: true);
     return;
   }
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
index 19d7bc0..60f7c5c 100644
--- a/packages/flutter_tools/lib/src/plugins.dart
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:meta/meta.dart';
 import 'package:mustache/mustache.dart' as mustache;
 import 'package:yaml/yaml.dart';
 
@@ -77,10 +78,13 @@
   return plugins;
 }
 
-void _writeFlutterPluginsList(String directory, List<Plugin> plugins) {
+/// Returns true if .flutter-plugins has changed, otherwise returns false.
+bool _writeFlutterPluginsList(String directory, List<Plugin> plugins) {
   final File pluginsProperties = fs.file(fs.path.join(directory, '.flutter-plugins'));
+  final String previousFlutterPlugins =
+      (pluginsProperties.existsSync() ? pluginsProperties.readAsStringSync() : null);
   final String pluginManifest =
-    plugins.map((Plugin p) => '${p.name}=${escapePath(p.path)}').join('\n');
+      plugins.map((Plugin p) => '${p.name}=${escapePath(p.path)}').join('\n');
   if (pluginManifest.isNotEmpty) {
     pluginsProperties.writeAsStringSync('$pluginManifest\n');
   } else {
@@ -88,6 +92,9 @@
       pluginsProperties.deleteSync();
     }
   }
+  final String currentFlutterPlugins =
+      (pluginsProperties.existsSync() ? pluginsProperties.readAsStringSync() : null);
+  return currentFlutterPlugins != previousFlutterPlugins;
 }
 
 const String _androidPluginRegistryTemplate = '''package io.flutter.plugins;
@@ -206,17 +213,28 @@
 
 }
 
+class InjectPluginsResult{
+  InjectPluginsResult({
+    @required this.hasPlugin,
+    @required this.hasChanged,
+  });
+  /// True if any flutter plugin exists, otherwise false.
+  final bool hasPlugin;
+  /// True if plugins have changed since last build.
+  final bool hasChanged;
+}
+
 /// Finds Flutter plugins in the pubspec.yaml, creates platform injection
 /// registries classes and add them to the build dependencies.
 ///
-/// Returns whether any Flutter plugins are added.
-bool injectPlugins({String directory}) {
+/// Returns whether any Flutter plugins are added and whether they changed.
+InjectPluginsResult injectPlugins({String directory}) {
   directory ??= fs.currentDirectory.path;
   final List<Plugin> plugins = _findPlugins(directory);
-  _writeFlutterPluginsList(directory, plugins);
+  final bool hasPluginsChanged = _writeFlutterPluginsList(directory, plugins);
   if (fs.isDirectorySync(fs.path.join(directory, 'android')))
     _writeAndroidPluginRegistry(directory, plugins);
   if (fs.isDirectorySync(fs.path.join(directory, 'ios')))
     _writeIOSPluginRegistry(directory, plugins);
-  return plugins.isNotEmpty;
+  return new InjectPluginsResult(hasPlugin: plugins.isNotEmpty, hasChanged: hasPluginsChanged);
 }