Re-reland Xcode backend refactor (#23762)

* Use Xcode build configurations to drive Flutter build mode

* Proper check wrt local_engine, print error if profile mode misisng

* Remove unused code, update tests, fix template problem, update warning

* fix up warning

* add explanatory dev comment

* fix whitespace

* missing words, change lambda arrow to function body

* error indentation

* Test early exits for xcode_backend.sh

* only on macOS, use right test

* Update error messages

* case insensitive compare for build config

* Update gallery podfile

* update projects to add profile configuration

* make compatible with flavors

* add missing plist files

* add FLUTTER_FRAMEWORK_DIR back, set swift version for profile, tell Podfile about profile
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
index 2bb2a20..e8e07fc 100644
--- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -49,9 +49,6 @@
   if (targetOverride != null)
     localsBuffer.writeln('FLUTTER_TARGET=$targetOverride');
 
-  // The runtime mode for the current build.
-  localsBuffer.writeln('FLUTTER_BUILD_MODE=${buildInfo.modeName}');
-
   // The build outputs directory, relative to FLUTTER_APPLICATION_PATH.
   localsBuffer.writeln('FLUTTER_BUILD_DIR=${getBuildDirectory()}');
 
@@ -61,6 +58,7 @@
     // For module projects we do not want to write the FLUTTER_FRAMEWORK_DIR
     // explicitly. Rather we rely on the xcode backend script and the Podfile
     // logic to derive it from FLUTTER_ROOT and FLUTTER_BUILD_MODE.
+    // However, this is necessary for regular projects using Cocoapods.
     localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}');
   }
 
@@ -247,6 +245,17 @@
       return baseConfiguration + '-$scheme';
   }
 
+  /// Checks whether the [buildConfigurations] contains the specified string, without
+  /// regard to case.
+  bool hasBuildConfiguratinForBuildMode(String buildMode) {
+    buildMode = buildMode.toLowerCase();
+    for (String name in buildConfigurations) {
+      if (name.toLowerCase() == buildMode) {
+        return true;
+      }
+    }
+    return false;
+  }
   /// Returns unique scheme matching [buildInfo], or null, if there is no unique
   /// best match.
   String schemeFor(BuildInfo buildInfo) {
@@ -262,7 +271,7 @@
   /// null, if there is no unique best match.
   String buildConfigurationFor(BuildInfo buildInfo, String scheme) {
     final String expectedConfiguration = expectedBuildConfigurationFor(buildInfo, scheme);
-    if (buildConfigurations.contains(expectedConfiguration))
+    if (hasBuildConfiguratinForBuildMode(expectedConfiguration))
       return expectedConfiguration;
     final String baseConfiguration = _baseConfigurationFor(buildInfo);
     return _uniqueMatch(buildConfigurations, (String candidate) {
@@ -274,7 +283,13 @@
     });
   }
 
-  static String _baseConfigurationFor(BuildInfo buildInfo) => buildInfo.isDebug ? 'Debug' : 'Release';
+  static String _baseConfigurationFor(BuildInfo buildInfo) {
+    if (buildInfo.isDebug)
+      return 'Debug';
+    if (buildInfo.isProfile)
+      return 'Profile';
+    return 'Release';
+  }
 
   static String _uniqueMatch(Iterable<String> strings, bool matches(String s)) {
     final List<String> options = strings.where(matches).toList();