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,
     );
 
diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
index 468af2a..6b63f74 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
@@ -11,6 +11,7 @@
 import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/net.dart';
 import 'package:flutter_tools/src/base/platform.dart';
+import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/commands/create.dart';
 import 'package:flutter_tools/src/dart/pub.dart';
@@ -996,8 +997,20 @@
     await runner.run(<String>['create', '--template=app', '--no-pub', '--org', 'com.example', tmpProjectDir]);
     FlutterProject project = FlutterProject.fromDirectory(globals.fs.directory(tmpProjectDir));
     expect(
-        await project.ios.productBundleIdentifier,
-        'com.example.helloFlutter',
+      await project.ios.productBundleIdentifier(BuildInfo.debug),
+      'com.example.helloFlutter',
+    );
+    expect(
+      await project.ios.productBundleIdentifier(BuildInfo.profile),
+      'com.example.helloFlutter',
+    );
+    expect(
+      await project.ios.productBundleIdentifier(BuildInfo.release),
+      'com.example.helloFlutter',
+    );
+    expect(
+      await project.ios.productBundleIdentifier(null),
+      'com.example.helloFlutter',
     );
     expect(
         project.android.applicationId,
@@ -1008,7 +1021,7 @@
     await runner.run(<String>['create', '--template=app', '--no-pub', '--org', 'abc^*.1#@', tmpProjectDir]);
     project = FlutterProject.fromDirectory(globals.fs.directory(tmpProjectDir));
     expect(
-        await project.ios.productBundleIdentifier,
+        await project.ios.productBundleIdentifier(BuildInfo.debug),
         'abc.1.testAbc',
     );
     expect(
@@ -1020,7 +1033,7 @@
     await runner.run(<String>['create', '--template=app', '--no-pub', '--org', '#+^%', tmpProjectDir]);
     project = FlutterProject.fromDirectory(globals.fs.directory(tmpProjectDir));
     expect(
-        await project.ios.productBundleIdentifier,
+        await project.ios.productBundleIdentifier(BuildInfo.debug),
         'flutterProject.untitled',
     );
     expect(
@@ -1154,7 +1167,7 @@
     await _createProject(projectDir, <String>[], <String>[]);
     final FlutterProject project = FlutterProject.fromDirectory(projectDir);
     expect(
-      await project.ios.productBundleIdentifier,
+      await project.ios.productBundleIdentifier(BuildInfo.debug),
       'com.bar.foo.flutterProject',
     );
   }, overrides: <Type, Generator>{
@@ -1203,7 +1216,7 @@
     await _createProject(projectDir, <String>['--no-pub'], <String>[]);
     final FlutterProject project = FlutterProject.fromDirectory(projectDir);
     expect(
-      await project.ios.productBundleIdentifier,
+      await project.ios.productBundleIdentifier(BuildInfo.debug),
       'com.bar.foo.flutterProject',
     );
   });
@@ -1236,7 +1249,7 @@
     );
     final FlutterProject project = FlutterProject.fromDirectory(projectDir);
     expect(
-      await project.example.ios.productBundleIdentifier,
+      await project.example.ios.productBundleIdentifier(BuildInfo.debug),
       'com.bar.foo.flutterProjectExample',
     );
   });
diff --git a/packages/flutter_tools/test/general.shard/application_package_test.dart b/packages/flutter_tools/test/general.shard/application_package_test.dart
index f7fd187..6407dfd 100644
--- a/packages/flutter_tools/test/general.shard/application_package_test.dart
+++ b/packages/flutter_tools/test/general.shard/application_package_test.dart
@@ -91,6 +91,7 @@
 
       final ApplicationPackage applicationPackage = await ApplicationPackageFactory.instance.getPackageForPlatform(
         TargetPlatform.android_arm,
+        buildInfo: null,
         applicationBinary: apkFile,
       );
       expect(applicationPackage.name, 'app.apk');
@@ -117,6 +118,7 @@
 
       await ApplicationPackageFactory.instance.getPackageForPlatform(
         TargetPlatform.android_arm,
+        buildInfo: null,
         applicationBinary: globals.fs.file('app.apk'),
       );
       verify(
@@ -134,6 +136,7 @@
 
       await ApplicationPackageFactory.instance.getPackageForPlatform(
         TargetPlatform.android_arm,
+        buildInfo: null,
       );
       verifyNever(
         mockProcessManager.run(
@@ -328,7 +331,7 @@
       globals.fs.file('pubspec.yaml').createSync();
       globals.fs.file('.packages').createSync();
       final BuildableIOSApp iosApp = await IOSApp.fromIosProject(
-        FlutterProject.fromDirectory(globals.fs.currentDirectory).ios) as BuildableIOSApp;
+        FlutterProject.fromDirectory(globals.fs.currentDirectory).ios, null) as BuildableIOSApp;
 
       expect(iosApp, null);
     }, overrides: overrides);
@@ -338,7 +341,7 @@
       globals.fs.file('.packages').createSync();
       globals.fs.file('ios/FooBar.xcodeproj').createSync(recursive: true);
       final BuildableIOSApp iosApp = await IOSApp.fromIosProject(
-        FlutterProject.fromDirectory(globals.fs.currentDirectory).ios) as BuildableIOSApp;
+        FlutterProject.fromDirectory(globals.fs.currentDirectory).ios, null) as BuildableIOSApp;
 
       expect(iosApp, null);
     }, overrides: overrides);
@@ -348,7 +351,7 @@
       globals.fs.file('.packages').createSync();
       globals.fs.file('ios/Runner.xcodeproj').createSync(recursive: true);
       final BuildableIOSApp iosApp = await IOSApp.fromIosProject(
-        FlutterProject.fromDirectory(globals.fs.currentDirectory).ios) as BuildableIOSApp;
+        FlutterProject.fromDirectory(globals.fs.currentDirectory).ios, null) as BuildableIOSApp;
 
       expect(iosApp, null);
     }, overrides: overrides);
diff --git a/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart b/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart
index fc78ff5..37ee6b3 100644
--- a/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart
@@ -6,6 +6,7 @@
 import 'dart:convert';
 
 import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/project.dart';
 import 'package:mockito/mockito.dart';
 import 'package:flutter_tools/src/application_package.dart';
@@ -36,28 +37,32 @@
       when(mockProcessManager.canRun(any)).thenReturn(true);
       mockConfig = MockConfig();
       mockIosProject = MockIosProject();
-      when(mockIosProject.buildSettings).thenAnswer((_) {
+      when(mockIosProject.buildSettingsForBuildInfo(any)).thenAnswer((_) {
         return Future<Map<String, String>>.value(<String, String>{
           'For our purposes': 'a non-empty build settings map is valid',
         });
       });
       testTerminal = TestTerminal();
       testTerminal.usesTerminalUi = true;
-      app = await BuildableIOSApp.fromProject(mockIosProject);
+      app = await BuildableIOSApp.fromProject(mockIosProject, null);
     });
 
     testWithoutContext('No auto-sign if Xcode project settings are not available', () async {
-      when(mockIosProject.buildSettings).thenReturn(null);
+      const BuildInfo buildInfo = BuildInfo.debug;
+      when(mockIosProject.buildSettingsForBuildInfo(any)).thenReturn(null);
       final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: buildInfo,
       );
       expect(signingConfigs, isNull);
+      verify(mockIosProject.buildSettingsForBuildInfo(buildInfo));
     });
 
     testWithoutContext('No discovery if development team specified in Xcode project', () async {
-      when(mockIosProject.buildSettings).thenAnswer((_) {
+      const BuildInfo buildInfo = BuildInfo.debug;
+      when(mockIosProject.buildSettingsForBuildInfo(any)).thenAnswer((_) {
         return Future<Map<String, String>>.value(<String, String>{
           'DEVELOPMENT_TEAM': 'abc',
         });
@@ -66,11 +71,13 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: buildInfo,
       );
       expect(signingConfigs, isNull);
       expect(logger.statusText, equals(
         'Automatically signing iOS for device deployment using specified development team in Xcode project: abc\n'
       ));
+      verify(mockIosProject.buildSettingsForBuildInfo(buildInfo));
     });
 
     testWithoutContext('No auto-sign if security or openssl not available', () async {
@@ -80,12 +87,14 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: null,
       );
       expect(signingConfigs, isNull);
     });
 
     testUsingContext('No valid code signing certificates shows instructions', () async {
-      when(mockIosProject.buildSettings).thenAnswer((_) {
+      const BuildInfo buildInfo = BuildInfo.debug;
+      when(mockIosProject.buildSettingsForBuildInfo(any)).thenAnswer((_) {
         return Future<Map<String, String>>.value(<String, String>{});
       });
       when(mockProcessManager.run(
@@ -108,7 +117,9 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: buildInfo,
       ), throwsToolExit(message: 'No development certificates available to code sign app for device deployment'));
+      verify(mockIosProject.buildSettingsForBuildInfo(buildInfo));
     },
     overrides: <Type, Generator>{
       OutputPreferences: () => OutputPreferences(wrapText: false),
@@ -172,6 +183,7 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: null,
       );
 
       expect(logger.statusText, contains('iPhone Developer: Profile 1 (1111AAAA11)'));
@@ -240,6 +252,7 @@
           iosApp: app,
           processManager: mockProcessManager,
           logger: logger,
+          buildInfo: null,
         );
       } on Exception catch (e) {
         // This should not throw
@@ -314,6 +327,7 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: null,
       );
 
       expect(
@@ -399,6 +413,7 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: null,
       );
 
       expect(
@@ -476,6 +491,7 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: null,
       );
 
       expect(
@@ -559,6 +575,7 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: null,
       );
 
       expect(
@@ -600,6 +617,7 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: null,
       );
       expect(signingConfigs, isNull);
     });
@@ -643,6 +661,7 @@
         iosApp: app,
         processManager: mockProcessManager,
         logger: logger,
+        buildInfo: null,
       );
       expect(signingConfigs, isNull);
     },
diff --git a/packages/flutter_tools/test/general.shard/ios/mac_test.dart b/packages/flutter_tools/test/general.shard/ios/mac_test.dart
index 51406f2..bba41fb 100644
--- a/packages/flutter_tools/test/general.shard/ios/mac_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/mac_test.dart
@@ -395,7 +395,7 @@
       final MockFile pbxprojFile = MockFile();
 
       when(project.xcodeProjectInfoFile).thenReturn(pbxprojFile);
-      when(project.hostAppBundleName).thenAnswer((_) => Future<String>.value('UnitTestRunner.app'));
+      when(project.hostAppBundleName(any)).thenAnswer((_) => Future<String>.value('UnitTestRunner.app'));
       when(pbxprojFile.readAsLinesSync())
           .thenAnswer((_) => flutterAssetPbxProjLines);
       when(pbxprojFile.existsSync())
diff --git a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
index 7b6d3c7..caf0a32 100644
--- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
@@ -553,7 +553,7 @@
           xcode: mockXcode,
         );
         final DeviceLogReader logReader = device.getLogReader(
-          app: await BuildableIOSApp.fromProject(mockIosProject),
+          app: await BuildableIOSApp.fromProject(mockIosProject, null),
         );
 
         final List<String> lines = await logReader.logLines.toList();
@@ -587,7 +587,7 @@
           xcode: mockXcode,
         );
         final DeviceLogReader logReader = device.getLogReader(
-          app: await BuildableIOSApp.fromProject(mockIosProject),
+          app: await BuildableIOSApp.fromProject(mockIosProject, null),
         );
 
         final List<String> lines = await logReader.logLines.toList();
@@ -634,7 +634,7 @@
           xcode: mockXcode,
         );
         final DeviceLogReader logReader = device.getLogReader(
-          app: await BuildableIOSApp.fromProject(mockIosProject),
+          app: await BuildableIOSApp.fromProject(mockIosProject, null),
         );
 
         final List<String> lines = await logReader.logLines.toList();
@@ -699,7 +699,7 @@
           xcode: mockXcode,
         );
         final DeviceLogReader logReader = device.getLogReader(
-          app: await BuildableIOSApp.fromProject(mockIosProject),
+          app: await BuildableIOSApp.fromProject(mockIosProject, null),
         );
 
         final List<String> lines = await logReader.logLines.toList();
diff --git a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
index bbc018f..262482f 100644
--- a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
@@ -68,7 +68,7 @@
       platform.environment = const <String, String>{};
 
       expect(await xcodeProjectInterpreter.getBuildSettings(
-        '', '', timeout: delay),
+        '', scheme: 'Runner', timeout: delay),
         const <String, String>{});
       // build settings times out and is killed once, then succeeds.
       verify(processManager.killPid(any)).called(1);
@@ -246,14 +246,31 @@
         '/usr/bin/xcodebuild',
         '-project',
         '/',
-        '-target',
-        'Runner',
+        '-scheme',
+        'Free',
         '-showBuildSettings'
       ],
       exitCode: 1,
     ));
 
-    expect(await xcodeProjectInterpreter.getBuildSettings('', 'Runner'), const <String, String>{});
+    expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{});
+    expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+  });
+
+  testWithoutContext('build settings accepts an empty scheme', () async {
+    platform.environment = const <String, String>{};
+
+    fakeProcessManager.addCommand(const FakeCommand(
+      command: <String>[
+        '/usr/bin/xcodebuild',
+        '-project',
+        '/',
+        '-showBuildSettings'
+      ],
+      exitCode: 1,
+    ));
+
+    expect(await xcodeProjectInterpreter.getBuildSettings(''), const <String, String>{});
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
@@ -267,14 +284,14 @@
         xcodebuild,
         '-project',
         fileSystem.path.separator,
-        '-target',
-        'Runner',
+        '-scheme',
+        'Free',
         '-showBuildSettings',
         'CODE_SIGN_STYLE=Manual',
         'ARCHS=arm64'
       ],
     ));
-    expect(await xcodeProjectInterpreter.getBuildSettings('', 'Runner'), const <String, String>{});
+    expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{});
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
@@ -290,7 +307,7 @@
         '-workspace',
         'workspace_path',
         '-scheme',
-        'Runner',
+        'Free',
         '-quiet',
         'clean',
         'CODE_SIGN_STYLE=Manual',
@@ -298,7 +315,7 @@
       ],
     ));
 
-    await xcodeProjectInterpreter.cleanWorkspace('workspace_path', 'Runner');
+    await xcodeProjectInterpreter.cleanWorkspace('workspace_path', 'Free');
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
diff --git a/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart b/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart
index c994189..f191b59 100644
--- a/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart
+++ b/packages/flutter_tools/test/general.shard/macos/cocoapods_test.dart
@@ -208,7 +208,7 @@
     });
 
     testWithoutContext('creates objective-c Podfile when not present', () async {
-      when(mockXcodeProjectInterpreter.getBuildSettings(any, any))
+      when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: null, timeout: anyNamed('timeout')))
         .thenAnswer((_) async => <String, String>{});
       await cocoaPodsUnderTest.setupPodfile(projectUnderTest.ios);
 
@@ -216,7 +216,7 @@
     });
 
     testUsingContext('creates swift Podfile if swift', () async {
-      when(mockXcodeProjectInterpreter.getBuildSettings(any, any))
+      when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: null, timeout: anyNamed('timeout')))
         .thenAnswer((_) async => <String, String>{
           'SWIFT_VERSION': '5.0',
         });
diff --git a/packages/flutter_tools/test/general.shard/project_test.dart b/packages/flutter_tools/test/general.shard/project_test.dart
index e8c68a9..f671f42 100644
--- a/packages/flutter_tools/test/general.shard/project_test.dart
+++ b/packages/flutter_tools/test/general.shard/project_test.dart
@@ -386,19 +386,19 @@
 
       testWithMocks('null, if no build settings or plist entries', () async {
         final FlutterProject project = await someProject();
-        expect(await project.ios.productBundleIdentifier, isNull);
+        expect(await project.ios.productBundleIdentifier(null), isNull);
       });
 
       testWithMocks('from build settings, if no plist', () async {
         final FlutterProject project = await someProject();
-        when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
+        when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
                 (_) {
               return Future<Map<String,String>>.value(<String, String>{
                 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
               });
             }
         );
-        expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
+        expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
       });
 
       testWithMocks('from project file, if no plist or build settings', () async {
@@ -406,19 +406,19 @@
         addIosProjectFile(project.directory, projectFileContent: () {
           return projectFileWithBundleId('io.flutter.someProject');
         });
-        expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
+        expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
       });
 
       testWithMocks('from plist, if no variables', () async {
         final FlutterProject project = await someProject();
         project.ios.defaultHostInfoPlist.createSync(recursive: true);
         when(mockPlistUtils.getValueFromFile(any, any)).thenReturn('io.flutter.someProject');
-        expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
+        expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
       });
 
       testWithMocks('from build settings and plist, if default variable', () async {
         final FlutterProject project = await someProject();
-        when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
+        when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
                 (_) {
               return Future<Map<String,String>>.value(<String, String>{
                 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
@@ -426,13 +426,13 @@
             }
         );
         when(mockPlistUtils.getValueFromFile(any, any)).thenReturn(r'$(PRODUCT_BUNDLE_IDENTIFIER)');
-        expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
+        expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
       });
 
       testWithMocks('from build settings and plist, by substitution', () async {
         final FlutterProject project = await someProject();
         project.ios.defaultHostInfoPlist.createSync(recursive: true);
-        when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
+        when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
           (_) {
             return Future<Map<String,String>>.value(<String, String>{
               'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
@@ -441,28 +441,28 @@
           }
         );
         when(mockPlistUtils.getValueFromFile(any, any)).thenReturn(r'$(PRODUCT_BUNDLE_IDENTIFIER).$(SUFFIX)');
-        expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject.suffix');
+        expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject.suffix');
       });
       testWithMocks('empty surrounded by quotes', () async {
         final FlutterProject project = await someProject();
         addIosProjectFile(project.directory, projectFileContent: () {
           return projectFileWithBundleId('', qualifier: '"');
         });
-        expect(await project.ios.productBundleIdentifier, '');
+        expect(await project.ios.productBundleIdentifier(null), '');
       });
       testWithMocks('surrounded by double quotes', () async {
         final FlutterProject project = await someProject();
         addIosProjectFile(project.directory, projectFileContent: () {
           return projectFileWithBundleId('io.flutter.someProject', qualifier: '"');
         });
-        expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
+        expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
       });
       testWithMocks('surrounded by single quotes', () async {
         final FlutterProject project = await someProject();
         addIosProjectFile(project.directory, projectFileContent: () {
           return projectFileWithBundleId('io.flutter.someProject', qualifier: "'");
         });
-        expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
+        expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
       });
     });
 
@@ -476,7 +476,7 @@
 
       testUsingContext('app product name defaults to Runner.app', () async {
         final FlutterProject project = await someProject();
-        expect(await project.ios.hostAppBundleName, 'Runner.app');
+        expect(await project.ios.hostAppBundleName(null), 'Runner.app');
       }, overrides: <Type, Generator>{
         FileSystem: () => fs,
         ProcessManager: () => FakeProcessManager.any(),
@@ -485,13 +485,13 @@
 
       testUsingContext('app product name xcodebuild settings', () async {
         final FlutterProject project = await someProject();
-        when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer((_) {
+        when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer((_) {
           return Future<Map<String,String>>.value(<String, String>{
             'FULL_PRODUCT_NAME': 'My App.app'
           });
         });
 
-        expect(await project.ios.hostAppBundleName, 'My App.app');
+        expect(await project.ios.hostAppBundleName(null), 'My App.app');
       }, overrides: <Type, Generator>{
         FileSystem: () => fs,
         ProcessManager: () => FakeProcessManager.any(),
@@ -630,7 +630,7 @@
 
     testUsingContext('cannot find bundle identifier', () async {
       final FlutterProject project = await someProject();
-      expect(await project.ios.containsWatchCompanion(<String>['WatchTarget']), isFalse);
+      expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isFalse);
     }, overrides: <Type, Generator>{
       FileSystem: () => fs,
       ProcessManager: () => FakeProcessManager.any(),
@@ -641,7 +641,7 @@
 
     group('with bundle identifier', () {
       setUp(() {
-        when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
+        when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
             (_) {
             return Future<Map<String,String>>.value(<String, String>{
               'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
@@ -652,7 +652,7 @@
 
       testUsingContext('no Info.plist in target', () async {
         final FlutterProject project = await someProject();
-        expect(await project.ios.containsWatchCompanion(<String>['WatchTarget']), isFalse);
+        expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isFalse);
       }, overrides: <Type, Generator>{
         FileSystem: () => fs,
         ProcessManager: () => FakeProcessManager.any(),
@@ -665,7 +665,7 @@
         final FlutterProject project = await someProject();
         project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true);
 
-        expect(await project.ios.containsWatchCompanion(<String>['WatchTarget']), isFalse);
+        expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isFalse);
       }, overrides: <Type, Generator>{
         FileSystem: () => fs,
         ProcessManager: () => FakeProcessManager.any(),
@@ -679,7 +679,7 @@
         project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true);
 
         when(mockPlistUtils.getValueFromFile(any, 'WKCompanionAppBundleIdentifier')).thenReturn('io.flutter.someOTHERproject');
-        expect(await project.ios.containsWatchCompanion(<String>['WatchTarget']), isFalse);
+        expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isFalse);
       }, overrides: <Type, Generator>{
         FileSystem: () => fs,
         ProcessManager: () => FakeProcessManager.any(),
@@ -693,7 +693,7 @@
         project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true);
         when(mockPlistUtils.getValueFromFile(any, 'WKCompanionAppBundleIdentifier')).thenReturn('io.flutter.someProject');
 
-        expect(await project.ios.containsWatchCompanion(<String>['WatchTarget']), isTrue);
+        expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isTrue);
       }, overrides: <Type, Generator>{
         FileSystem: () => fs,
         ProcessManager: () => FakeProcessManager.any(),
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index 49b20ac..f54f90e 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -397,8 +397,8 @@
 
   @override
   Future<Map<String, String>> getBuildSettings(
-    String projectPath,
-    String target, {
+    String projectPath, {
+    String scheme,
     Duration timeout = const Duration(minutes: 1),
   }) async {
     return <String, String>{};
diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart
index c5eff89..3ababad 100644
--- a/packages/flutter_tools/test/src/mocks.dart
+++ b/packages/flutter_tools/test/src/mocks.dart
@@ -54,9 +54,10 @@
   @override
   Future<ApplicationPackage> getPackageForPlatform(
     TargetPlatform platform, {
+    BuildInfo buildInfo,
     File applicationBinary,
   }) async {
-    return _store.getPackageForPlatform(platform);
+    return _store.getPackageForPlatform(platform, buildInfo);
   }
 }
 
@@ -531,10 +532,10 @@
   static const String appBundleName = 'My Super Awesome App.app';
 
   @override
-  Future<String> get productBundleIdentifier async => bundleId;
+  Future<String> productBundleIdentifier(BuildInfo buildInfo) async => bundleId;
 
   @override
-  Future<String> get hostAppBundleName async => appBundleName;
+  Future<String> hostAppBundleName(BuildInfo buildInfo) async => appBundleName;
 }
 
 class MockAndroidDevice extends Mock implements AndroidDevice {