Unverified Commit 1755819c authored by Chris Yang's avatar Chris Yang Committed by GitHub

Log XCResult before other build issues (#100787)

parent 74cdc422
...@@ -574,37 +574,14 @@ Future<void> diagnoseXcodeBuildFailure(XcodeBuildResult result, Usage flutterUsa ...@@ -574,37 +574,14 @@ Future<void> diagnoseXcodeBuildFailure(XcodeBuildResult result, Usage flutterUsa
).send(); ).send();
} }
// Building for iOS Simulator, but the linked and embedded framework 'App.framework' was built for iOS. // Handle errors.
// or final bool issueDetected = _handleIssues(result.xcResult, logger, xcodeBuildExecution);
// Building for iOS, but the linked and embedded framework 'App.framework' was built for iOS Simulator.
if ((result.stdout?.contains('Building for iOS') ?? false) if (!issueDetected && xcodeBuildExecution != null) {
&& (result.stdout?.contains('but the linked and embedded framework') ?? false) // Fallback to use stdout to detect and print issues.
&& (result.stdout?.contains('was built for iOS') ?? false)) { _parseIssueInStdout(xcodeBuildExecution, logger, result);
logger.printError('');
logger.printError('Your Xcode project requires migration. See https://flutter.dev/docs/development/ios-project-migration for details.');
logger.printError('');
logger.printError('You can temporarily work around this issue by running:');
logger.printError(' flutter clean');
return;
}
if (xcodeBuildExecution != null
&& xcodeBuildExecution.environmentType == EnvironmentType.physical
&& (result.stdout?.contains('BCEROR') ?? false)
// May need updating if Xcode changes its outputs.
&& (result.stdout?.contains("Xcode couldn't find a provisioning profile matching") ?? false)) {
logger.printError(noProvisioningProfileInstruction, emphasis: true);
return;
}
// Make sure the user has specified one of:
// * DEVELOPMENT_TEAM (automatic signing)
// * PROVISIONING_PROFILE (manual signing)
if (xcodeBuildExecution != null &&
xcodeBuildExecution.environmentType == EnvironmentType.physical &&
!<String>['DEVELOPMENT_TEAM', 'PROVISIONING_PROFILE'].any(
xcodeBuildExecution.buildSettings.containsKey)) {
logger.printError(noDevelopmentTeamInstruction, emphasis: true);
return;
} }
if (xcodeBuildExecution != null if (xcodeBuildExecution != null
&& xcodeBuildExecution.environmentType == EnvironmentType.physical && xcodeBuildExecution.environmentType == EnvironmentType.physical
&& (xcodeBuildExecution.buildSettings['PRODUCT_BUNDLE_IDENTIFIER']?.contains('com.example') ?? false)) { && (xcodeBuildExecution.buildSettings['PRODUCT_BUNDLE_IDENTIFIER']?.contains('com.example') ?? false)) {
...@@ -614,19 +591,6 @@ Future<void> diagnoseXcodeBuildFailure(XcodeBuildResult result, Usage flutterUsa ...@@ -614,19 +591,6 @@ Future<void> diagnoseXcodeBuildFailure(XcodeBuildResult result, Usage flutterUsa
logger.printError(' open ios/Runner.xcworkspace'); logger.printError(' open ios/Runner.xcworkspace');
return; return;
} }
// Handle xcresult errors.
final XCResult? xcResult = result.xcResult;
if (xcResult == null) {
return;
}
if (!xcResult.parseSuccess) {
globals.printTrace('XCResult parsing error: ${xcResult.parsingErrorMessage}');
return;
}
for (final XCResultIssue issue in xcResult.issues) {
_handleXCResultIssue(issue: issue, logger: logger);
}
} }
/// xcodebuild <buildaction> parameter (see man xcodebuild for details). /// xcodebuild <buildaction> parameter (see man xcodebuild for details).
...@@ -724,7 +688,7 @@ bool upgradePbxProjWithFlutterAssets(IosProject project, Logger logger) { ...@@ -724,7 +688,7 @@ bool upgradePbxProjWithFlutterAssets(IosProject project, Logger logger) {
return true; return true;
} }
void _handleXCResultIssue({required XCResultIssue issue, required Logger logger}) { _XCResultIssueHandlingResult _handleXCResultIssue({required XCResultIssue issue, required Logger logger}) {
// Issue summary from xcresult. // Issue summary from xcresult.
final StringBuffer issueSummaryBuffer = StringBuffer(); final StringBuffer issueSummaryBuffer = StringBuffer();
issueSummaryBuffer.write(issue.subType ?? 'Unknown'); issueSummaryBuffer.write(issue.subType ?? 'Unknown');
...@@ -744,17 +708,90 @@ void _handleXCResultIssue({required XCResultIssue issue, required Logger logger} ...@@ -744,17 +708,90 @@ void _handleXCResultIssue({required XCResultIssue issue, required Logger logger}
break; break;
} }
// Add more custom output for flutter users. final String? message = issue.message;
if (issue.message != null && issue.message!.toLowerCase().contains('provisioning profile')) { if (message == null) {
return _XCResultIssueHandlingResult(requiresProvisioningProfile: false, hasProvisioningProfileIssue: false);
}
// Add more error messages for flutter users for some special errors.
if (message.toLowerCase().contains('requires a provisioning profile.')) {
return _XCResultIssueHandlingResult(requiresProvisioningProfile: true, hasProvisioningProfileIssue: true);
} else if (message.toLowerCase().contains('provisioning profile')) {
return _XCResultIssueHandlingResult(requiresProvisioningProfile: false, hasProvisioningProfileIssue: true);
}
return _XCResultIssueHandlingResult(requiresProvisioningProfile: false, hasProvisioningProfileIssue: false);
}
// Returns `true` if at least one issue is detected.
bool _handleIssues(XCResult? xcResult, Logger logger, XcodeBuildExecution? xcodeBuildExecution) {
bool requiresProvisioningProfile = false;
bool hasProvisioningProfileIssue = false;
bool issueDetected = false;
if (xcResult != null && xcResult.parseSuccess) {
for (final XCResultIssue issue in xcResult.issues) {
final _XCResultIssueHandlingResult handlingResult = _handleXCResultIssue(issue: issue, logger: logger);
if (handlingResult.hasProvisioningProfileIssue) {
hasProvisioningProfileIssue = true;
}
if (handlingResult.requiresProvisioningProfile) {
requiresProvisioningProfile = true;
}
issueDetected = true;
}
} else if (xcResult != null) {
globals.printTrace('XCResult parsing error: ${xcResult.parsingErrorMessage}');
}
if (requiresProvisioningProfile) {
logger.printError(noProvisioningProfileInstruction, emphasis: true);
} else if (_missingDevelopmentTeam(xcodeBuildExecution)) {
issueDetected = true;
logger.printError(noDevelopmentTeamInstruction, emphasis: true);
} else if (hasProvisioningProfileIssue) {
logger.printError(''); logger.printError('');
logger.printError('It appears that there was a problem signing your application prior to installation on the device.'); logger.printError('It appears that there was a problem signing your application prior to installation on the device.');
logger.printError(''); logger.printError('');
logger.printError('Verify that the Bundle Identifier in your project is your signing id in Xcode'); logger.printError('Verify that the Bundle Identifier in your project is your signing id in Xcode');
logger.printError(' open ios/Runner.xcworkspace'); logger.printError(' open ios/Runner.xcworkspace');
logger.printError(''); logger.printError('');
logger.printError("Also try selecting 'Product > Build' to fix the problem:"); logger.printError("Also try selecting 'Product > Build' to fix the problem.");
}
return issueDetected;
}
// Return 'true' a missing development team issue is detected.
bool _missingDevelopmentTeam(XcodeBuildExecution? xcodeBuildExecution) {
// Make sure the user has specified one of:
// * DEVELOPMENT_TEAM (automatic signing)
// * PROVISIONING_PROFILE (manual signing)
return xcodeBuildExecution != null && xcodeBuildExecution.environmentType == EnvironmentType.physical &&
!<String>['DEVELOPMENT_TEAM', 'PROVISIONING_PROFILE'].any(
xcodeBuildExecution.buildSettings.containsKey);
}
// Detects and handles errors from stdout.
//
// As detecting issues in stdout is not usually accurate, this should be used as a fallback when other issue detecting methods failed.
void _parseIssueInStdout(XcodeBuildExecution xcodeBuildExecution, Logger logger, XcodeBuildResult result) {
if (xcodeBuildExecution.environmentType == EnvironmentType.physical
// May need updating if Xcode changes its outputs.
&& (result.stdout?.contains('requires a provisioning profile. Select a provisioning profile in the Signing & Capabilities editor') ?? false)) {
logger.printError(noProvisioningProfileInstruction, emphasis: true);
return;
} }
} }
// The result of [_handleXCResultIssue].
class _XCResultIssueHandlingResult {
_XCResultIssueHandlingResult({required this.requiresProvisioningProfile, required this.hasProvisioningProfileIssue});
// An issue indicates that user didn't provide the provisioning profile.
final bool requiresProvisioningProfile;
// An issue indicates that there is a provisioning profile issue.
final bool hasProvisioningProfileIssue;
}
const String _kResultBundlePath = 'temporary_xcresult_bundle'; const String _kResultBundlePath = 'temporary_xcresult_bundle';
const String _kResultBundleVersion = '3'; const String _kResultBundleVersion = '3';
...@@ -721,7 +721,7 @@ void main() { ...@@ -721,7 +721,7 @@ void main() {
expect(testLogger.errorText, contains('It appears that there was a problem signing your application prior to installation on the device.')); expect(testLogger.errorText, contains('It appears that there was a problem signing your application prior to installation on the device.'));
expect(testLogger.errorText, contains('Verify that the Bundle Identifier in your project is your signing id in Xcode')); expect(testLogger.errorText, contains('Verify that the Bundle Identifier in your project is your signing id in Xcode'));
expect(testLogger.errorText, contains('open ios/Runner.xcworkspace')); expect(testLogger.errorText, contains('open ios/Runner.xcworkspace'));
expect(testLogger.errorText, contains("Also try selecting 'Product > Build' to fix the problem:")); expect(testLogger.errorText, contains("Also try selecting 'Product > Build' to fix the problem."));
expect(fakeProcessManager, hasNoRemainingExpectations); expect(fakeProcessManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
......
...@@ -10,6 +10,7 @@ import 'package:flutter_tools/src/base/logger.dart'; ...@@ -10,6 +10,7 @@ import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/process.dart'; import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/ios/code_signing.dart';
import 'package:flutter_tools/src/ios/iproxy.dart'; import 'package:flutter_tools/src/ios/iproxy.dart';
import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
...@@ -166,7 +167,11 @@ void main() { ...@@ -166,7 +167,11 @@ void main() {
)); ));
}); });
testWithoutContext('No provisioning profile shows message', () async { testWithoutContext('fallback to stdout: No provisioning profile shows message', () async {
final Map<String, String> buildSettingsWithDevTeam = <String, String>{
'PRODUCT_BUNDLE_IDENTIFIER': 'test.app',
'DEVELOPMENT_TEAM': 'a team',
};
final XcodeBuildResult buildResult = XcodeBuildResult( final XcodeBuildResult buildResult = XcodeBuildResult(
success: false, success: false,
stdout: ''' stdout: '''
...@@ -194,7 +199,7 @@ Xcode's output: ...@@ -194,7 +199,7 @@ Xcode's output:
=== CLEAN TARGET Runner OF PROJECT Runner WITH CONFIGURATION Release === === CLEAN TARGET Runner OF PROJECT Runner WITH CONFIGURATION Release ===
Check dependencies Check dependencies
[BCEROR]No profiles for 'com.example.test' were found: Xcode couldn't find a provisioning profile matching 'com.example.test'. [BCEROR]"Runner" requires a provisioning profile. Select a provisioning profile in the Signing & Capabilities 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' [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'
...@@ -228,14 +233,14 @@ Error launching application on iPhone.''', ...@@ -228,14 +233,14 @@ Error launching application on iPhone.''',
buildCommands: <String>['xcrun', 'xcodebuild', 'blah'], buildCommands: <String>['xcrun', 'xcodebuild', 'blah'],
appDirectory: '/blah/blah', appDirectory: '/blah/blah',
environmentType: EnvironmentType.physical, environmentType: EnvironmentType.physical,
buildSettings: buildSettings, buildSettings: buildSettingsWithDevTeam,
), ),
); );
await diagnoseXcodeBuildFailure(buildResult, testUsage, logger); await diagnoseXcodeBuildFailure(buildResult, testUsage, logger);
expect( expect(
logger.errorText, logger.errorText,
contains("No Provisioning Profile was found for your project's Bundle Identifier or your \ndevice."), contains(noProvisioningProfileInstruction),
); );
}); });
...@@ -319,80 +324,6 @@ Could not build the precompiled application for the device.''', ...@@ -319,80 +324,6 @@ Could not build the precompiled application for the device.''',
contains('Building a deployable iOS app requires a selected Development Team with a \nProvisioning Profile.'), contains('Building a deployable iOS app requires a selected Development Team with a \nProvisioning Profile.'),
); );
}); });
testWithoutContext('embedded and linked framework iOS mismatch shows message', () async {
final XcodeBuildResult buildResult = XcodeBuildResult(
success: false,
stdout: '''
Launching lib/main.dart on iPhone in debug mode...
Automatically signing iOS for device deployment using specified development team in Xcode project: blah
Xcode build done. 5.7s
Failed to build iOS app
Error output from Xcode build:
** BUILD FAILED **
Xcode's output:
note: Using new build system
note: Building targets in parallel
note: Planning build
note: Constructing build description
error: Building for iOS Simulator, but the linked and embedded framework 'App.framework' was built for iOS. (in target 'Runner' from project 'Runner')
Could not build the precompiled application for the device.
Error launching application on iPhone.
Exited (sigterm)''',
xcodeBuildExecution: XcodeBuildExecution(
buildCommands: <String>['xcrun', 'xcodebuild', 'blah'],
appDirectory: '/blah/blah',
environmentType: EnvironmentType.physical,
buildSettings: buildSettings,
),
);
await diagnoseXcodeBuildFailure(buildResult, testUsage, logger);
expect(
logger.errorText,
contains('Your Xcode project requires migration.'),
);
});
testWithoutContext('embedded and linked framework iOS simulator mismatch shows message', () async {
final XcodeBuildResult buildResult = XcodeBuildResult(
success: false,
stdout: '''
Launching lib/main.dart on iPhone in debug mode...
Automatically signing iOS for device deployment using specified development team in Xcode project: blah
Xcode build done. 5.7s
Failed to build iOS app
Error output from Xcode build:
** BUILD FAILED **
Xcode's output:
note: Using new build system
note: Building targets in parallel
note: Planning build
note: Constructing build description
error: Building for iOS, but the linked and embedded framework 'App.framework' was built for iOS Simulator. (in target 'Runner' from project 'Runner')
Could not build the precompiled application for the device.
Error launching application on iPhone.
Exited (sigterm)''',
xcodeBuildExecution: XcodeBuildExecution(
buildCommands: <String>['xcrun', 'xcodebuild', 'blah'],
appDirectory: '/blah/blah',
environmentType: EnvironmentType.physical,
buildSettings: buildSettings,
),
);
await diagnoseXcodeBuildFailure(buildResult, testUsage, logger);
expect(
logger.errorText,
contains('Your Xcode project requires migration.'),
);
});
}); });
group('Upgrades project.pbxproj for old asset usage', () { group('Upgrades project.pbxproj for old asset usage', () {
......
...@@ -181,6 +181,82 @@ const String kSampleResultJsonWithIssues = r''' ...@@ -181,6 +181,82 @@ const String kSampleResultJsonWithIssues = r'''
} }
'''; ''';
/// An example xcresult bundle json that contains some warning and some errors.
const String kSampleResultJsonWithNoProvisioningProfileIssue = r'''
{
"issues" : {
"_type" : {
"_name" : "ResultIssueSummaries"
},
"errorSummaries" : {
"_type" : {
"_name" : "Array"
},
"_values" : [
{
"_type" : {
"_name" : "IssueSummary"
},
"documentLocationInCreatingWorkspace" : {
"_type" : {
"_name" : "DocumentLocation"
},
"concreteTypeName" : {
"_type" : {
"_name" : "String"
},
"_value" : "DVTTextDocumentLocation"
},
"url" : {
"_type" : {
"_name" : "String"
},
"_value" : "file:\/\/\/Users\/m\/Projects\/test_create\/ios\/Runner\/AppDelegate.m#CharacterRangeLen=0&CharacterRangeLoc=263&EndingColumnNumber=56&EndingLineNumber=7&LocationEncoding=1&StartingColumnNumber=56&StartingLineNumber=7"
}
},
"issueType" : {
"_type" : {
"_name" : "String"
},
"_value" : "Error"
},
"message" : {
"_type" : {
"_name" : "String"
},
"_value" : "Runner requires a provisioning profile. Select a provisioning profile in the Signing & Capabilities editor"
}
}
]
},
"warningSummaries" : {
"_type" : {
"_name" : "Array"
},
"_values" : [
{
"_type" : {
"_name" : "IssueSummary"
},
"issueType" : {
"_type" : {
"_name" : "String"
},
"_value" : "Warning"
},
"message" : {
"_type" : {
"_name" : "String"
},
"_value" : "The iOS deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.0.99."
}
}
]
}
}
}
''';
/// An example xcresult bundle json that contains some warning and some errors. /// An example xcresult bundle json that contains some warning and some errors.
const String kSampleResultJsonWithIssuesAndInvalidUrl = r''' const String kSampleResultJsonWithIssuesAndInvalidUrl = r'''
{ {
......
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