Commit c2b0a30c authored by xster's avatar xster Committed by GitHub

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
parent 96b9d647
...@@ -45,7 +45,7 @@ class BuildIOSCommand extends BuildSubCommand { ...@@ -45,7 +45,7 @@ class BuildIOSCommand extends BuildSubCommand {
if (getCurrentHostPlatform() != HostPlatform.darwin_x64) if (getCurrentHostPlatform() != HostPlatform.darwin_x64)
throwToolExit('Building for iOS is only supported on the Mac.'); 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) if (app == null)
throwToolExit('Application not configured for iOS'); throwToolExit('Application not configured for iOS');
...@@ -73,7 +73,7 @@ class BuildIOSCommand extends BuildSubCommand { ...@@ -73,7 +73,7 @@ class BuildIOSCommand extends BuildSubCommand {
); );
if (!result.success) { if (!result.success) {
await diagnoseXcodeBuildFailure(result); await diagnoseXcodeBuildFailure(result, app);
throwToolExit('Encountered error while building for $logTarget.'); throwToolExit('Encountered error while building for $logTarget.');
} }
......
...@@ -14,34 +14,60 @@ import '../base/process.dart'; ...@@ -14,34 +14,60 @@ import '../base/process.dart';
import '../base/terminal.dart'; import '../base/terminal.dart';
import '../globals.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 = ''' const String noCertificatesInstruction = '''
═══════════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════════
No valid code signing certificates were found No valid code signing certificates were found
Please ensure that you have a valid Development Team with valid iOS Development Certificates You can connect to your Apple Developer account by signing in with your Apple ID in Xcode
associated with your Apple ID by: and create an iOS Development Certificate as well as a Provisioning Profile for your project by:
1- Opening the Xcode application $fixWithDevelopmentTeamInstruction
2- Go to Xcode->Preferences->Accounts 5- Trust your newly created Development Certificate on your iOS device
3- Make sure that you're signed in with your Apple ID via the '+' button on the bottom left via Settings > General > Device Management > [your new certificate] > Trust
4- Make sure that you have development certificates available by signing up to Apple
Developer Program and/or downloading available profiles as needed.
For more information, please visit: For more information, please visit:
https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html
Or run on an iOS simulator without code signing 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 = ''' const String noDevelopmentTeamInstruction = '''
═══════════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════════
Building a deployable iOS app requires a selected Development Team with a Provisioning Profile Building a deployable iOS app requires a selected Development Team with a Provisioning Profile
Please ensure that a Development Team is selected by: 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 open ios/Runner.xcworkspace
2- Select the 'Runner' project in the navigator then the 'Runner' target 2- Select the 'Runner' project in the navigator then the 'Runner' target
in the project settings in the project settings
3- In the 'General' tab, make sure a 'Development Team' is selected\n 3- In the 'General' tab, make sure a 'Development Team' is selected. You may need to add
For more information, please visit: your Apple ID first.
https://flutter.io/setup/#deploy-to-ios-devices\n 4- Build or run your project again''';
Or run on an iOS simulator
═══════════════════════════════════════════════════════════════════════════════════''';
final RegExp _securityFindIdentityDeveloperIdentityExtractionPattern = final RegExp _securityFindIdentityDeveloperIdentityExtractionPattern =
new RegExp(r'^\s*\d+\).+"(.+Developer.+)"$'); new RegExp(r'^\s*\d+\).+"(.+Developer.+)"$');
......
...@@ -208,7 +208,7 @@ class IOSDevice extends Device { ...@@ -208,7 +208,7 @@ class IOSDevice extends Device {
final XcodeBuildResult buildResult = await buildXcodeProject(app: app, mode: mode, target: mainPath, buildForDevice: true); final XcodeBuildResult buildResult = await buildXcodeProject(app: app, mode: mode, target: mainPath, buildForDevice: true);
if (!buildResult.success) { if (!buildResult.success) {
printError('Could not build the precompiled application for the device.'); printError('Could not build the precompiled application for the device.');
await diagnoseXcodeBuildFailure(buildResult); await diagnoseXcodeBuildFailure(buildResult, app);
printError(''); printError('');
return new LaunchResult.failed(); return new LaunchResult.failed();
} }
...@@ -242,7 +242,7 @@ class IOSDevice extends Device { ...@@ -242,7 +242,7 @@ class IOSDevice extends Device {
// the port picked and scrape that later. // the port picked and scrape that later.
} }
if (debuggingOptions.enableSoftwareRendering) if (debuggingOptions.enableSoftwareRendering)
launchArguments.add('--enable-software-rendering'); launchArguments.add('--enable-software-rendering');
if (platformArgs['trace-startup'] ?? false) if (platformArgs['trace-startup'] ?? false)
......
...@@ -147,7 +147,7 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -147,7 +147,7 @@ Future<XcodeBuildResult> buildXcodeProject({
} }
String developmentTeam; String developmentTeam;
if (codesign && mode != BuildMode.release && buildForDevice) if (codesign && buildForDevice)
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app); developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
// Before the build, all service definitions must be updated and the dylibs // Before the build, all service definitions must be updated and the dylibs
...@@ -246,51 +246,40 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -246,51 +246,40 @@ Future<XcodeBuildResult> buildXcodeProject({
} }
} }
Future<Null> diagnoseXcodeBuildFailure(XcodeBuildResult result) async { Future<Null> diagnoseXcodeBuildFailure(XcodeBuildResult result, BuildableIOSApp app) async {
final File plistFile = fs.file('ios/Runner/Info.plist'); if (result.xcodeBuildExecution != null &&
if (plistFile.existsSync()) { result.xcodeBuildExecution.buildForPhysicalDevice &&
final String plistContent = plistFile.readAsStringSync(); result.stdout?.contains('BCEROR') == true &&
if (plistContent.contains('com.yourcompany')) { // May need updating if Xcode changes its outputs.
printError(''); result.stdout?.contains('Xcode couldn\'t find a provisioning profile matching') == true) {
printError('It appears that your application still contains the default signing identifier.'); printError(noProvisioningProfileInstruction, emphasis: true);
printError("Try replacing 'com.yourcompany' with your signing id"); return;
printError('in ${plistFile.absolute.path}'); }
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) { if (result.stdout?.contains('Code Sign error') == true) {
printError(''); printError('');
printError('It appears that there was a problem signing your application prior to installation on the device.'); printError('It appears that there was a problem signing your application prior to installation on the device.');
printError(''); printError('');
if (plistFile.existsSync()) { printError('Verify that the Bundle Identifier in your project is your signing id in Xcode');
printError('Verify that the CFBundleIdentifier in the Info.plist file is your signing id'); printError(' open ios/Runner.xcworkspace');
printError(' ${plistFile.absolute.path}'); printError('');
printError(''); printError("Also try selecting 'Product > Build' to fix the problem:");
}
printError("Try launching Xcode and selecting 'Product > Build' to fix the problem:");
printError(" open ios/Runner.xcworkspace");
return; 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 { class XcodeBuildResult {
......
// 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'),
);
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment