Remove Runner target check, prefer schemes (#56620)

diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index 0b00d6b..3315e46 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -31,6 +31,7 @@
 
   Future<ApplicationPackage> getPackageForPlatform(
     TargetPlatform platform, {
+    BuildInfo buildInfo,
     File applicationBinary,
   }) async {
     switch (platform) {
@@ -47,7 +48,7 @@
             : AndroidApk.fromApk(applicationBinary);
       case TargetPlatform.ios:
         return applicationBinary == null
-            ? await IOSApp.fromIosProject(FlutterProject.current().ios)
+            ? await IOSApp.fromIosProject(FlutterProject.current().ios, buildInfo)
             : IOSApp.fromPrebuiltApp(applicationBinary);
       case TargetPlatform.tester:
         return FlutterTesterApp.fromCurrentDirectory();
@@ -327,7 +328,7 @@
     );
   }
 
-  static Future<IOSApp> fromIosProject(IosProject project) {
+  static Future<IOSApp> fromIosProject(IosProject project, BuildInfo buildInfo) {
     if (getCurrentHostPlatform() != HostPlatform.darwin_x64) {
       return null;
     }
@@ -344,7 +345,7 @@
       globals.printError('Expected ios/Runner.xcodeproj/project.pbxproj but this file is missing.');
       return null;
     }
-    return BuildableIOSApp.fromProject(project);
+    return BuildableIOSApp.fromProject(project, buildInfo);
   }
 
   @override
@@ -360,9 +361,9 @@
     : _hostAppBundleName = hostAppBundleName,
       super(projectBundleId: projectBundleId);
 
-  static Future<BuildableIOSApp> fromProject(IosProject project) async {
-    final String projectBundleId = await project.productBundleIdentifier;
-    final String hostAppBundleName = await project.hostAppBundleName;
+  static Future<BuildableIOSApp> fromProject(IosProject project, BuildInfo buildInfo) async {
+    final String projectBundleId = await project.productBundleIdentifier(buildInfo);
+    final String hostAppBundleName = await project.hostAppBundleName(buildInfo);
     return BuildableIOSApp(project, projectBundleId, hostAppBundleName);
   }
 
@@ -416,7 +417,10 @@
   MacOSApp macOS;
   WindowsApp windows;
 
-  Future<ApplicationPackage> getPackageForPlatform(TargetPlatform platform) async {
+  Future<ApplicationPackage> getPackageForPlatform(
+    TargetPlatform platform,
+    BuildInfo buildInfo,
+  ) async {
     switch (platform) {
       case TargetPlatform.android:
       case TargetPlatform.android_arm:
@@ -426,7 +430,7 @@
         android ??= await AndroidApk.fromAndroidProject(FlutterProject.current().android);
         return android;
       case TargetPlatform.ios:
-        iOS ??= await IOSApp.fromIosProject(FlutterProject.current().ios);
+        iOS ??= await IOSApp.fromIosProject(FlutterProject.current().ios, buildInfo);
         return iOS;
       case TargetPlatform.fuchsia_arm64:
       case TargetPlatform.fuchsia_x64:
diff --git a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
index b3cb71d..b445ea1 100644
--- a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
@@ -370,6 +370,7 @@
     firstBuildTime = DateTime.now();
     final ApplicationPackage package = await ApplicationPackageFactory.instance.getPackageForPlatform(
       TargetPlatform.web_javascript,
+      buildInfo: debuggingOptions.buildInfo,
       applicationBinary: null,
     );
     if (package == null) {
diff --git a/packages/flutter_tools/lib/src/commands/build_ios.dart b/packages/flutter_tools/lib/src/commands/build_ios.dart
index fb7a10b..5f5dee1 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios.dart
@@ -64,7 +64,11 @@
       throwToolExit('Building for iOS is only supported on the Mac.');
     }
 
-    final BuildableIOSApp app = await applicationPackages.getPackageForPlatform(TargetPlatform.ios) as BuildableIOSApp;
+    final BuildInfo buildInfo = getBuildInfo();
+    final BuildableIOSApp app = await applicationPackages.getPackageForPlatform(
+      TargetPlatform.ios,
+      buildInfo,
+    ) as BuildableIOSApp;
 
     if (app == null) {
       throwToolExit('Application not configured for iOS');
@@ -76,7 +80,6 @@
       globals.printStatus('Warning: Building for device with codesigning disabled. You will '
         'have to manually codesign before deploying to device.');
     }
-    final BuildInfo buildInfo = getBuildInfo();
     if (forSimulator && !buildInfo.supportsSimulator) {
       throwToolExit('${toTitleCase(buildInfo.friendlyModeName)} mode is not supported for simulators.');
     }
diff --git a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
index 479119b..5fa5942 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
@@ -181,8 +181,8 @@
 
     final Directory outputDirectory = globals.fs.directory(globals.fs.path.absolute(globals.fs.path.normalize(outputArgument)));
 
-    final String productBundleIdentifier = await _project.ios.productBundleIdentifier;
     for (final BuildInfo buildInfo in buildInfos) {
+      final String productBundleIdentifier = await _project.ios.productBundleIdentifier(buildInfo);
       globals.printStatus('Building frameworks for $productBundleIdentifier in ${getNameForBuildMode(buildInfo.mode)} mode...');
       final String xcodeBuildConfiguration = toTitleCase(getNameForBuildMode(buildInfo.mode));
       final Directory modeDirectory = outputDirectory.childDirectory(xcodeBuildConfiguration);
diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart
index b6bfdf2..9055483 100644
--- a/packages/flutter_tools/lib/src/commands/drive.dart
+++ b/packages/flutter_tools/lib/src/commands/drive.dart
@@ -404,7 +404,7 @@
   await appStopper(command);
 
   final ApplicationPackage package = await command.applicationPackages
-      .getPackageForPlatform(await command.device.targetPlatform);
+      .getPackageForPlatform(await command.device.targetPlatform, command.getBuildInfo());
 
   if (command.shouldBuild) {
     globals.printTrace('Installing application package.');
@@ -497,7 +497,10 @@
 
 Future<bool> _stopApp(DriveCommand command) async {
   globals.printTrace('Stopping application.');
-  final ApplicationPackage package = await command.applicationPackages.getPackageForPlatform(await command.device.targetPlatform);
+  final ApplicationPackage package = await command.applicationPackages.getPackageForPlatform(
+    await command.device.targetPlatform,
+    command.getBuildInfo(),
+  );
   final bool stopped = await command.device.stopApp(package);
   await command._deviceLogSubscription?.cancel();
   return stopped;
diff --git a/packages/flutter_tools/lib/src/commands/install.dart b/packages/flutter_tools/lib/src/commands/install.dart
index 2d2b69a..320552a 100644
--- a/packages/flutter_tools/lib/src/commands/install.dart
+++ b/packages/flutter_tools/lib/src/commands/install.dart
@@ -43,7 +43,10 @@
 
   @override
   Future<FlutterCommandResult> runCommand() async {
-    final ApplicationPackage package = await applicationPackages.getPackageForPlatform(await device.targetPlatform);
+    final ApplicationPackage package = await applicationPackages.getPackageForPlatform(
+      await device.targetPlatform,
+      null, // Build info isn't relevant for install, will use whatever bundle was built last.
+    );
 
     Cache.releaseLockEarly();
 
diff --git a/packages/flutter_tools/lib/src/ios/code_signing.dart b/packages/flutter_tools/lib/src/ios/code_signing.dart
index cce0ae4..ec99325 100644
--- a/packages/flutter_tools/lib/src/ios/code_signing.dart
+++ b/packages/flutter_tools/lib/src/ios/code_signing.dart
@@ -13,6 +13,7 @@
 import '../base/io.dart';
 import '../base/logger.dart';
 import '../base/process.dart';
+import '../build_info.dart';
 import '../convert.dart' show utf8;
 import '../globals.dart' as globals;
 
@@ -101,8 +102,9 @@
   @required BuildableIOSApp iosApp,
   @required ProcessManager processManager,
   @required Logger logger,
+  @required BuildInfo buildInfo,
 }) async {
-  final Map<String, String> buildSettings = await iosApp.project.buildSettings;
+  final Map<String, String> buildSettings = await iosApp.project.buildSettingsForBuildInfo(buildInfo);
   if (buildSettings == null) {
     return null;
   }
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index d3d6e92..f2f088d 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -111,12 +111,6 @@
   await removeFinderExtendedAttributes(app.project.hostAppRoot, processUtils, globals.logger);
 
   final XcodeProjectInfo projectInfo = await globals.xcodeProjectInterpreter.getInfo(app.project.hostAppRoot.path);
-  if (!projectInfo.targets.contains('Runner')) {
-    globals.printError('The Xcode project does not define target "Runner" which is needed by Flutter tooling.');
-    globals.printError('Open Xcode to fix the problem:');
-    globals.printError('  open ios/Runner.xcworkspace');
-    return XcodeBuildResult(success: false);
-  }
   final String scheme = projectInfo.schemeFor(buildInfo);
   if (scheme == null) {
     globals.printError('');
@@ -180,7 +174,8 @@
     autoSigningConfigs = await getCodeSigningIdentityDevelopmentTeam(
       iosApp: app,
       processManager: globals.processManager,
-      logger: globals.logger
+      logger: globals.logger,
+      buildInfo: buildInfo,
     );
   }
 
@@ -229,7 +224,10 @@
   }
 
   // Check if the project contains a watchOS companion app.
-  final bool hasWatchCompanion = await app.project.containsWatchCompanion(projectInfo.targets);
+  final bool hasWatchCompanion = await app.project.containsWatchCompanion(
+    projectInfo.targets,
+    buildInfo,
+  );
   if (hasWatchCompanion) {
     // The -sdk argument has to be omitted if a watchOS companion app exists.
     // Otherwise the build will fail as WatchKit dependencies cannot be build using the iOS SDK.
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
index 1786508..b2fab77 100644
--- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -309,9 +309,12 @@
 
   /// Asynchronously retrieve xcode build settings. This one is preferred for
   /// new call-sites.
+  ///
+  /// If [scheme] is null, xcodebuild will return build settings for the first discovered
+  /// target (by default this is Runner).
   Future<Map<String, String>> getBuildSettings(
-    String projectPath,
-    String target, {
+    String projectPath, {
+    String scheme,
     Duration timeout = const Duration(minutes: 1),
   }) async {
     final Status status = Status.withSpinner(
@@ -324,8 +327,8 @@
       _executable,
       '-project',
       _fileSystem.path.absolute(projectPath),
-      '-target',
-      target,
+      if (scheme != null)
+        ...<String>['-scheme', scheme],
       '-showBuildSettings',
       ...environmentVariablesAsXcodeBuildSettings(_platform)
     ];
@@ -465,7 +468,6 @@
   final List<String> buildConfigurations;
   final List<String> schemes;
 
-  bool get definesCustomTargets => !(targets.contains('Runner') && targets.length == 1);
   bool get definesCustomSchemes => !(schemes.contains('Runner') && schemes.length == 1);
   bool get definesCustomBuildConfigurations {
     return !(buildConfigurations.contains('Debug') &&
@@ -475,7 +477,7 @@
 
   /// The expected scheme for [buildInfo].
   static String expectedSchemeFor(BuildInfo buildInfo) {
-    return toTitleCase(buildInfo.flavor ?? 'runner');
+    return toTitleCase(buildInfo?.flavor ?? 'runner');
   }
 
   /// The expected build configuration for [buildInfo] and [scheme].
diff --git a/packages/flutter_tools/lib/src/macos/cocoapods.dart b/packages/flutter_tools/lib/src/macos/cocoapods.dart
index c46a096..2960019 100644
--- a/packages/flutter_tools/lib/src/macos/cocoapods.dart
+++ b/packages/flutter_tools/lib/src/macos/cocoapods.dart
@@ -267,7 +267,6 @@
     } else {
       final bool isSwift = (await _xcodeProjectInterpreter.getBuildSettings(
         runnerProject.path,
-        'Runner',
       )).containsKey('SWIFT_VERSION');
       podfileTemplateName = isSwift ? 'Podfile-ios-swift' : 'Podfile-ios-objc';
     }
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index 77cd014..8ef4656 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -102,11 +102,14 @@
   /// Gradle group ID.
   Future<Set<String>> get organizationNames async {
     final List<String> candidates = <String>[
-      await ios.productBundleIdentifier,
+      // Don't require iOS build info, this method is only
+      // used during create as best-effort, use the
+      // default target bundle identifier.
+      await ios.productBundleIdentifier(null),
       android.applicationId,
       android.group,
       example.android.applicationId,
-      await example.ios.productBundleIdentifier,
+      await example.ios.productBundleIdentifier(null),
     ];
     return Set<String>.of(candidates
         .map<String>(_organizationNameFromPackageName)
@@ -411,11 +414,11 @@
 
   /// The product bundle identifier of the host app, or null if not set or if
   /// iOS tooling needed to read it is not installed.
-  Future<String> get productBundleIdentifier async =>
-    _productBundleIdentifier ??= await _parseProductBundleIdentifier();
+  Future<String> productBundleIdentifier(BuildInfo buildInfo) async =>
+    _productBundleIdentifier ??= await _parseProductBundleIdentifier(buildInfo);
   String _productBundleIdentifier;
 
-  Future<String> _parseProductBundleIdentifier() async {
+  Future<String> _parseProductBundleIdentifier(BuildInfo buildInfo) async {
     String fromPlist;
     final File defaultInfoPlist = defaultHostInfoPlist;
     // Users can change the location of the Info.plist.
@@ -434,7 +437,7 @@
         return fromPlist;
       }
     }
-    final Map<String, String> allBuildSettings = await buildSettings;
+    final Map<String, String> allBuildSettings = await buildSettingsForBuildInfo(buildInfo);
     if (allBuildSettings != null) {
       if (fromPlist != null) {
         // Perform variable substitution using build settings.
@@ -458,18 +461,18 @@
   }
 
   /// The bundle name of the host app, `My App.app`.
-  Future<String> get hostAppBundleName async =>
-    _hostAppBundleName ??= await _parseHostAppBundleName();
+  Future<String> hostAppBundleName(BuildInfo buildInfo) async =>
+    _hostAppBundleName ??= await _parseHostAppBundleName(buildInfo);
   String _hostAppBundleName;
 
-  Future<String> _parseHostAppBundleName() async {
+  Future<String> _parseHostAppBundleName(BuildInfo buildInfo) async {
     // The product name and bundle name are derived from the display name, which the user
     // is instructed to change in Xcode as part of deploying to the App Store.
     // https://flutter.dev/docs/deployment/ios#review-xcode-project-settings
     // The only source of truth for the name is Xcode's interpretation of the build settings.
     String productName;
     if (globals.xcodeProjectInterpreter.isInstalled) {
-      final Map<String, String> xcodeBuildSettings = await buildSettings;
+      final Map<String, String> xcodeBuildSettings = await buildSettingsForBuildInfo(buildInfo);
       if (xcodeBuildSettings != null) {
         productName = xcodeBuildSettings['FULL_PRODUCT_NAME'];
       }
@@ -483,17 +486,20 @@
   /// The build settings for the host app of this project, as a detached map.
   ///
   /// Returns null, if iOS tooling is unavailable.
-  Future<Map<String, String>> get buildSettings async =>
-    _buildSettings ??= await _xcodeProjectBuildSettings();
-  Map<String, String> _buildSettings;
+  Future<Map<String, String>> buildSettingsForBuildInfo(BuildInfo buildInfo) async {
+    _buildSettingsByScheme ??= <String, Map<String, String>>{};
+    final String scheme = xcode.XcodeProjectInfo.expectedSchemeFor(buildInfo);
+    return _buildSettingsByScheme[scheme] ??= await _xcodeProjectBuildSettings(scheme);
+  }
+  Map<String, Map<String, String>> _buildSettingsByScheme;
 
-  Future<Map<String, String>> _xcodeProjectBuildSettings() async {
+  Future<Map<String, String>> _xcodeProjectBuildSettings(String scheme) async {
     if (!globals.xcodeProjectInterpreter.isInstalled) {
       return null;
     }
     final Map<String, String> buildSettings = await globals.xcodeProjectInterpreter.getBuildSettings(
       xcodeProject.path,
-      _hostAppProjectName,
+      scheme: scheme,
     );
     if (buildSettings != null && buildSettings.isNotEmpty) {
       // No timeouts, flakes, or errors.
@@ -511,8 +517,8 @@
   }
 
   /// Check if one the [targets] of the project is a watchOS companion app target.
-  Future<bool> containsWatchCompanion(List<String> targets) async {
-    final String bundleIdentifier = await productBundleIdentifier;
+  Future<bool> containsWatchCompanion(List<String> targets, BuildInfo buildInfo) async {
+    final String bundleIdentifier = await productBundleIdentifier(buildInfo);
     // A bundle identifier is required for a companion app.
     if (bundleIdentifier == null) {
       return false;
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index d9d044d..40d6311 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -469,6 +469,7 @@
     final TargetPlatform targetPlatform = await device.targetPlatform;
     package = await ApplicationPackageFactory.instance.getPackageForPlatform(
       targetPlatform,
+      buildInfo: hotRunner.debuggingOptions.buildInfo,
       applicationBinary: hotRunner.applicationBinary,
     );
 
@@ -524,6 +525,7 @@
     final TargetPlatform targetPlatform = await device.targetPlatform;
     package = await ApplicationPackageFactory.instance.getPackageForPlatform(
       targetPlatform,
+      buildInfo: coldRunner.debuggingOptions.buildInfo,
       applicationBinary: coldRunner.applicationBinary,
     );