Add more instructions and handling for first time iOS run (#10521)

* Before tests

* Add the part to trust the cert on the device

* flip the error checks since some are more specific and are more actionable

* add tests

* review
diff --git a/packages/flutter_tools/lib/src/commands/build_ios.dart b/packages/flutter_tools/lib/src/commands/build_ios.dart
index 93792ec..eb72d4a 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios.dart
@@ -45,7 +45,7 @@
     if (getCurrentHostPlatform() != HostPlatform.darwin_x64)
       throwToolExit('Building for iOS is only supported on the Mac.');
 
-    final IOSApp app = applicationPackages.getPackageForPlatform(TargetPlatform.ios);
+    final BuildableIOSApp app = applicationPackages.getPackageForPlatform(TargetPlatform.ios);
 
     if (app == null)
       throwToolExit('Application not configured for iOS');
@@ -73,7 +73,7 @@
     );
 
     if (!result.success) {
-      await diagnoseXcodeBuildFailure(result);
+      await diagnoseXcodeBuildFailure(result, app);
       throwToolExit('Encountered error while building for $logTarget.');
     }
 
diff --git a/packages/flutter_tools/lib/src/ios/code_signing.dart b/packages/flutter_tools/lib/src/ios/code_signing.dart
index 5a917db..169332d 100644
--- a/packages/flutter_tools/lib/src/ios/code_signing.dart
+++ b/packages/flutter_tools/lib/src/ios/code_signing.dart
@@ -14,34 +14,60 @@
 import '../base/terminal.dart';
 import '../globals.dart';
 
+/// User message when no development certificates are found in the keychain.
+///
+/// The user likely never did any iOS development.
 const String noCertificatesInstruction = '''
 ═══════════════════════════════════════════════════════════════════════════════════
 No valid code signing certificates were found
-Please ensure that you have a valid Development Team with valid iOS Development Certificates
-associated with your Apple ID by:
-  1- Opening the Xcode application
-  2- Go to Xcode->Preferences->Accounts
-  3- Make sure that you're signed in with your Apple ID via the '+' button on the bottom left
-  4- Make sure that you have development certificates available by signing up to Apple
-     Developer Program and/or downloading available profiles as needed.
+You can connect to your Apple Developer account by signing in with your Apple ID in Xcode
+and create an iOS Development Certificate as well as a Provisioning Profile for your project by:
+$fixWithDevelopmentTeamInstruction
+  5- Trust your newly created Development Certificate on your iOS device
+     via Settings > General > Device Management > [your new certificate] > Trust
+
 For more information, please visit:
   https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html
 
 Or run on an iOS simulator without code signing
 ═══════════════════════════════════════════════════════════════════════════════════''';
+/// User message when there are no provisioning profile for the current app bundle identifier.
+///
+/// The user did iOS development but never on this project and/or device.
+const String noProvisioningProfileInstruction = '''
+═══════════════════════════════════════════════════════════════════════════════════
+No Provisioning Profile was found for your project's Bundle Identifier or your device.
+You can create a new Provisioning Profile for your project in Xcode for your
+team by:
+$fixWithDevelopmentTeamInstruction
+
+For more information, please visit:
+  https://flutter.io/setup/#deploy-to-ios-devices
+
+Or run on an iOS simulator without code signing
+═══════════════════════════════════════════════════════════════════════════════════''';
+/// Fallback error message for signing issues.
+///
+/// Couldn't auto sign the app but can likely solved by retracing the signing flow in Xcode.
 const String noDevelopmentTeamInstruction = '''
 ═══════════════════════════════════════════════════════════════════════════════════
 Building a deployable iOS app requires a selected Development Team with a Provisioning Profile
 Please ensure that a Development Team is selected by:
-  1- Opening the Flutter project's Xcode target with
+$fixWithDevelopmentTeamInstruction
+
+For more information, please visit:
+  https://flutter.io/setup/#deploy-to-ios-devices
+
+Or run on an iOS simulator without code signing
+═══════════════════════════════════════════════════════════════════════════════════''';
+const String fixWithDevelopmentTeamInstruction = '''
+  1- Open the Flutter project's Xcode target with
        open ios/Runner.xcworkspace
   2- Select the 'Runner' project in the navigator then the 'Runner' target
      in the project settings
-  3- In the 'General' tab, make sure a 'Development Team' is selected\n
-For more information, please visit:
-  https://flutter.io/setup/#deploy-to-ios-devices\n
-Or run on an iOS simulator
-═══════════════════════════════════════════════════════════════════════════════════''';
+  3- In the 'General' tab, make sure a 'Development Team' is selected. You may need to add
+     your Apple ID first.
+  4- Build or run your project again''';
 
 final RegExp _securityFindIdentityDeveloperIdentityExtractionPattern =
     new RegExp(r'^\s*\d+\).+"(.+Developer.+)"$');
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 0b4d0be..a1cb175 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -208,7 +208,7 @@
       final XcodeBuildResult buildResult = await buildXcodeProject(app: app, mode: mode, target: mainPath, buildForDevice: true);
       if (!buildResult.success) {
         printError('Could not build the precompiled application for the device.');
-        await diagnoseXcodeBuildFailure(buildResult);
+        await diagnoseXcodeBuildFailure(buildResult, app);
         printError('');
         return new LaunchResult.failed();
       }
@@ -242,7 +242,7 @@
       // the port picked and scrape that later.
     }
 
-    if (debuggingOptions.enableSoftwareRendering) 
+    if (debuggingOptions.enableSoftwareRendering)
       launchArguments.add('--enable-software-rendering');
 
     if (platformArgs['trace-startup'] ?? false)
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 84f6aab..16430aa 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -147,7 +147,7 @@
   }
 
   String developmentTeam;
-  if (codesign && mode != BuildMode.release && buildForDevice)
+  if (codesign && buildForDevice)
     developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
 
   // Before the build, all service definitions must be updated and the dylibs
@@ -246,51 +246,40 @@
   }
 }
 
-Future<Null> diagnoseXcodeBuildFailure(XcodeBuildResult result) async {
-  final File plistFile = fs.file('ios/Runner/Info.plist');
-  if (plistFile.existsSync()) {
-    final String plistContent = plistFile.readAsStringSync();
-    if (plistContent.contains('com.yourcompany')) {
-      printError('');
-      printError('It appears that your application still contains the default signing identifier.');
-      printError("Try replacing 'com.yourcompany' with your signing id");
-      printError('in ${plistFile.absolute.path}');
-      return;
-    }
+Future<Null> diagnoseXcodeBuildFailure(XcodeBuildResult result, BuildableIOSApp app) async {
+  if (result.xcodeBuildExecution != null &&
+      result.xcodeBuildExecution.buildForPhysicalDevice &&
+      result.stdout?.contains('BCEROR') == true &&
+      // May need updating if Xcode changes its outputs.
+      result.stdout?.contains('Xcode couldn\'t find a provisioning profile matching') == true) {
+    printError(noProvisioningProfileInstruction, emphasis: true);
+    return;
+  }
+  if (result.xcodeBuildExecution != null &&
+      result.xcodeBuildExecution.buildForPhysicalDevice &&
+      // Make sure the user has specified at least the DEVELOPMENT_TEAM (for automatic Xcode 8)
+      // signing or the PROVISIONING_PROFILE (for manual signing or Xcode 7).
+      !(app.buildSettings?.containsKey('DEVELOPMENT_TEAM')) == true || app.buildSettings?.containsKey('PROVISIONING_PROFILE') == true) {
+    printError(noDevelopmentTeamInstruction, emphasis: true);
+    return;
+  }
+  if (app.id?.contains('com.yourcompany') ?? false) {
+    printError('');
+    printError('It appears that your application still contains the default signing identifier.');
+    printError("Try replacing 'com.yourcompany' with your signing id in Xcode:");
+    printError('  open ios/Runner.xcworkspace');
+    return;
   }
   if (result.stdout?.contains('Code Sign error') == true) {
     printError('');
     printError('It appears that there was a problem signing your application prior to installation on the device.');
     printError('');
-    if (plistFile.existsSync()) {
-      printError('Verify that the CFBundleIdentifier in the Info.plist file is your signing id');
-      printError('  ${plistFile.absolute.path}');
-      printError('');
-    }
-    printError("Try launching Xcode and selecting 'Product > Build' to fix the problem:");
-    printError("  open ios/Runner.xcworkspace");
+    printError('Verify that the Bundle Identifier in your project is your signing id in Xcode');
+    printError('  open ios/Runner.xcworkspace');
+    printError('');
+    printError("Also try selecting 'Product > Build' to fix the problem:");
     return;
   }
-  if (result.xcodeBuildExecution != null) {
-    assert(result.xcodeBuildExecution.buildForPhysicalDevice != null);
-    assert(result.xcodeBuildExecution.buildCommands != null);
-    assert(result.xcodeBuildExecution.appDirectory != null);
-    if (result.xcodeBuildExecution.buildForPhysicalDevice &&
-        result.xcodeBuildExecution.buildCommands.contains('build')) {
-      final RunResult checkBuildSettings = await runAsync(
-        result.xcodeBuildExecution.buildCommands..add('-showBuildSettings'),
-        workingDirectory: result.xcodeBuildExecution.appDirectory,
-        allowReentrantFlutter: true
-      );
-      // Make sure the user has specified at least the DEVELOPMENT_TEAM (for automatic Xcode 8)
-      // signing or the PROVISIONING_PROFILE (for manual signing or Xcode 7).
-      if (checkBuildSettings.exitCode == 0 &&
-          !checkBuildSettings.stdout?.contains(new RegExp(r'\bDEVELOPMENT_TEAM\b')) == true &&
-          !checkBuildSettings.stdout?.contains(new RegExp(r'\bPROVISIONING_PROFILE\b')) == true) {
-        printError(noDevelopmentTeamInstruction, emphasis: true);
-      }
-    }
-  }
 }
 
 class XcodeBuildResult {
diff --git a/packages/flutter_tools/test/ios/mac_test.dart b/packages/flutter_tools/test/ios/mac_test.dart
new file mode 100644
index 0000000..e267b05
--- /dev/null
+++ b/packages/flutter_tools/test/ios/mac_test.dart
@@ -0,0 +1,176 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter_tools/src/application_package.dart';
+import 'package:flutter_tools/src/ios/mac.dart';
+import 'package:test/test.dart';
+
+import '../src/context.dart';
+
+void main() {
+  group('Diagnose Xcode build failure', () {
+    BuildableIOSApp app;
+
+    setUp(() {
+      app = new BuildableIOSApp(
+        projectBundleId: 'test.app',
+        buildSettings: <String, String>{
+          'For our purposes': 'a non-empty build settings map is valid',
+        },
+      );
+    });
+
+    testUsingContext('No provisioning profile shows message', () async {
+      final XcodeBuildResult buildResult = new XcodeBuildResult(
+        success: false,
+        stdout: '''
+Launching lib/main.dart on iPhone in debug mode...
+Signing iOS app for device deployment using developer identity: "iPhone Developer: test@flutter.io (1122334455)"
+Running Xcode build...                                1.3s
+Failed to build iOS app
+Error output from Xcode build:
+↳
+    ** BUILD FAILED **
+
+
+    The following build commands failed:
+    	Check dependencies
+    (1 failure)
+Xcode's output:
+↳
+    Build settings from command line:
+        ARCHS = arm64
+        BUILD_DIR = /Users/blah/blah
+        DEVELOPMENT_TEAM = AABBCCDDEE
+        ONLY_ACTIVE_ARCH = YES
+        SDKROOT = iphoneos10.3
+
+    === CLEAN TARGET Runner OF PROJECT Runner WITH CONFIGURATION Release ===
+
+    Check dependencies
+    [BCEROR]No profiles for 'com.yourcompany.test' were found:  Xcode couldn't find a provisioning profile matching 'com.yourcompany.test'.
+    [BCEROR]Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+    [BCEROR]Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+    [BCEROR]Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+
+    Create product structure
+    /bin/mkdir -p /Users/blah/Runner.app
+
+    Clean.Remove clean /Users/blah/Runner.app.dSYM
+        builtin-rm -rf /Users/blah/Runner.app.dSYM
+
+    Clean.Remove clean /Users/blah/Runner.app
+        builtin-rm -rf /Users/blah/Runner.app
+
+    Clean.Remove clean /Users/blah/Runner-dfvicjniknvzghgwsthwtgcjhtsk/Build/Intermediates/Runner.build/Release-iphoneos/Runner.build
+        builtin-rm -rf /Users/blah/Runner-dfvicjniknvzghgwsthwtgcjhtsk/Build/Intermediates/Runner.build/Release-iphoneos/Runner.build
+
+    ** CLEAN SUCCEEDED **
+
+    === BUILD TARGET Runner OF PROJECT Runner WITH CONFIGURATION Release ===
+
+    Check dependencies
+    No profiles for 'com.yourcompany.test' were found:  Xcode couldn't find a provisioning profile matching 'com.yourcompany.test'.
+    Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+    Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+    Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+
+Could not build the precompiled application for the device.
+
+Error launching application on iPhone.''',
+        xcodeBuildExecution: new XcodeBuildExecution(
+          <String>['xcrun', 'xcodebuild', 'blah'],
+          '/blah/blah',
+          buildForPhysicalDevice: true
+        ),
+      );
+
+      await diagnoseXcodeBuildFailure(buildResult, app);
+      expect(
+        testLogger.errorText,
+        contains('No Provisioning Profile was found for your project\'s Bundle Identifier or your device.'),
+      );
+    });
+
+    testUsingContext('No development team shows message', () async {
+      final XcodeBuildResult buildResult = new XcodeBuildResult(
+        success: false,
+        stdout: '''
+Running "flutter packages get" in flutter_gallery...  0.6s
+Launching lib/main.dart on x in release mode...
+Running pod install...                                1.2s
+Running Xcode build...                                1.4s
+Failed to build iOS app
+Error output from Xcode build:
+↳
+    ** BUILD FAILED **
+
+
+    The following build commands failed:
+    	Check dependencies
+    (1 failure)
+Xcode's output:
+↳
+    blah
+
+    === CLEAN TARGET url_launcher OF PROJECT Pods WITH CONFIGURATION Release ===
+
+    Check dependencies
+
+    blah
+
+    === CLEAN TARGET Pods-Runner OF PROJECT Pods WITH CONFIGURATION Release ===
+
+    Check dependencies
+
+    blah
+
+    === CLEAN TARGET Runner OF PROJECT Runner WITH CONFIGURATION Release ===
+
+    Check dependencies
+    [BCEROR]Signing for "Runner" requires a development team. Select a development team in the project editor.
+    [BCEROR]Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+    [BCEROR]Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+    [BCEROR]Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+
+    blah
+
+    ** CLEAN SUCCEEDED **
+
+    === BUILD TARGET url_launcher OF PROJECT Pods WITH CONFIGURATION Release ===
+
+    Check dependencies
+
+    blah
+
+    === BUILD TARGET Pods-Runner OF PROJECT Pods WITH CONFIGURATION Release ===
+
+    Check dependencies
+
+    blah
+
+    === BUILD TARGET Runner OF PROJECT Runner WITH CONFIGURATION Release ===
+
+    Check dependencies
+    Signing for "Runner" requires a development team. Select a development team in the project editor.
+    Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+    Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+    Code signing is required for product type 'Application' in SDK 'iOS 10.3'
+
+Could not build the precompiled application for the device.''',
+        xcodeBuildExecution: new XcodeBuildExecution(
+          <String>['xcrun', 'xcodebuild', 'blah'],
+          '/blah/blah',
+          buildForPhysicalDevice: true
+        ),
+      );
+
+      await diagnoseXcodeBuildFailure(buildResult, app);
+      expect(
+        testLogger.errorText,
+        contains('Building a deployable iOS app requires a selected Development Team with a Provisioning Profile'),
+      );
+    });
+  });
+}