Unverified Commit f059dd40 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Show Xcode compilation errors at end of build, suppress stdout and stderr from Xcode (#113302)

parent 3034a4ef
......@@ -548,8 +548,7 @@ end
);
if (!xcodebuildOutput.contains('flutter --verbose --local-engine-src-path=bogus assemble') || // Verbose output
!xcodebuildOutput.contains('Unable to detect a Flutter engine build directory in bogus') ||
!xcodebuildOutput.contains('Command PhaseScriptExecution failed with a nonzero exit code')) {
!xcodebuildOutput.contains('Unable to detect a Flutter engine build directory in bogus')) {
return TaskResult.failure('Host Objective-C app build succeeded though flutter script failed');
}
......
......@@ -97,11 +97,17 @@ class Context {
if (verbose) {
print((result.stdout as String).trim());
}
if ((result.stderr as String).isNotEmpty) {
echoError((result.stderr as String).trim());
final String resultStderr = result.stderr.toString().trim();
if (resultStderr.isNotEmpty) {
final StringBuffer errorOutput = StringBuffer();
if (result.exitCode != 0) {
// "error:" prefix makes this show up as an Xcode compilation error.
errorOutput.write('error: ');
}
errorOutput.write(resultStderr);
echoError(errorOutput.toString());
}
if (!allowFail && result.exitCode != 0) {
stderr.write('${result.stderr}\n');
throw Exception(
'Command "$bin ${args.join(' ')}" exited with code ${result.exitCode}',
);
......
......@@ -871,13 +871,11 @@ class _BuildInstance {
ErrorHandlingFileSystem.deleteIfExists(previousFile);
}
} on Exception catch (exception, stackTrace) {
// TODO(zanderso): throw specific exception for expected errors to mark
// as non-fatal. All others should be fatal.
node.target.clearStamp(environment);
succeeded = false;
skipped = false;
exceptionMeasurements[node.target.name] = ExceptionMeasurement(
node.target.name, exception, stackTrace);
node.target.name, exception, stackTrace, fatal: true);
} finally {
resource.release();
stopwatch.stop();
......
......@@ -347,7 +347,7 @@ class AssembleCommand extends FlutterCommand {
for (final ExceptionMeasurement measurement in result.exceptions.values) {
if (measurement.fatal || globals.logger.isVerbose) {
globals.printError('Target ${measurement.target} failed: ${measurement.exception}',
stackTrace: measurement.stackTrace
stackTrace: globals.logger.isVerbose ? measurement.stackTrace : null,
);
}
}
......
......@@ -414,14 +414,6 @@ Future<XcodeBuildResult> buildXcodeProject({
}
if (buildResult != null && buildResult.exitCode != 0) {
globals.printStatus('Failed to build iOS app');
if (buildResult.stderr.isNotEmpty) {
globals.printStatus('Error output from Xcode build:\n↳');
globals.printStatus(buildResult.stderr, indent: 4);
}
if (buildResult.stdout.isNotEmpty) {
globals.printStatus("Xcode's output:\n↳");
globals.printStatus(buildResult.stdout, indent: 4);
}
return XcodeBuildResult(
success: false,
stdout: buildResult.stdout,
......@@ -738,7 +730,7 @@ bool _handleIssues(XCResult? xcResult, Logger logger, XcodeBuildExecution? xcode
if (requiresProvisioningProfile) {
logger.printError(noProvisioningProfileInstruction, emphasis: true);
} else if (_missingDevelopmentTeam(xcodeBuildExecution)) {
} else if ((!issueDetected || hasProvisioningProfileIssue) && _missingDevelopmentTeam(xcodeBuildExecution)) {
issueDetected = true;
logger.printError(noDevelopmentTeamInstruction, emphasis: true);
} else if (hasProvisioningProfileIssue) {
......@@ -782,11 +774,21 @@ bool _needUpdateSigningIdentifier(XcodeBuildExecution? xcodeBuildExecution) {
//
// 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) {
final String? stderr = result.stderr;
if (stderr != null && stderr.isNotEmpty) {
logger.printStatus('Error output from Xcode build:\n↳');
logger.printStatus(stderr, indent: 4);
}
final String? stdout = result.stdout;
if (stdout != null && stdout.isNotEmpty) {
logger.printStatus("Xcode's output:\n↳");
logger.printStatus(stdout, indent: 4);
}
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;
}
}
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:meta/meta.dart';
import '../../src/base/process.dart';
import '../../src/convert.dart' show json;
import '../../src/macos/xcode.dart';
......@@ -112,6 +114,20 @@ class XCResult {
);
}
/// Create a [XCResult] with constructed [XCResultIssue]s for testing.
@visibleForTesting
factory XCResult.test({
List<XCResultIssue>? issues,
bool? parseSuccess,
String? parsingErrorMessage,
}) {
return XCResult._(
issues: issues ?? const <XCResultIssue>[],
parseSuccess: parseSuccess ?? true,
parsingErrorMessage: parsingErrorMessage,
);
}
XCResult._({
this.issues = const <XCResultIssue>[],
this.parseSuccess = true,
......@@ -189,6 +205,24 @@ class XCResultIssue {
);
}
/// Create a [XCResultIssue] without JSON parsing for testing.
@visibleForTesting
factory XCResultIssue.test({
XCResultIssueType type = XCResultIssueType.error,
String? subType,
String? message,
String? location,
List<String> warnings = const <String>[],
}) {
return XCResultIssue._(
type: type,
subType: subType,
message: message,
location: location,
warnings: warnings,
);
}
XCResultIssue._({
required this.type,
required this.subType,
......
......@@ -135,13 +135,13 @@ void main() {
testUsingContext('flutter assemble does not log stack traces during build failure', () async {
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: false, exceptions: <String, ExceptionMeasurement>{
'hello': ExceptionMeasurement('hello', 'bar', stackTrace),
'hello': ExceptionMeasurement('hello', 'bar', stackTrace, fatal: true),
}))
));
await expectLater(commandRunner.run(<String>['assemble', '-o Output', 'debug_macos_bundle_flutter_assets']),
throwsToolExit());
expect(testLogger.errorText, isNot(contains('bar')));
expect(testLogger.errorText, contains('Target hello failed: bar'));
expect(testLogger.errorText, isNot(contains(stackTrace.toString())));
}, overrides: <Type, Generator>{
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
......
......@@ -372,7 +372,7 @@ void main() {
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
});
testUsingContext('Display xcresult issues on console if parsed.', () async {
testUsingContext('Display xcresult issues on console if parsed, suppress Xcode output', () async {
final BuildCommand command = BuildCommand();
createMinimalMockProjectFiles();
......@@ -384,13 +384,16 @@ void main() {
expect(testLogger.errorText, contains("Use of undeclared identifier 'asdas'"));
expect(testLogger.errorText, contains('/Users/m/Projects/test_create/ios/Runner/AppDelegate.m:7:56'));
expect(testLogger.statusText, isNot(contains("Xcode's output")));
expect(testLogger.statusText, isNot(contains('Lots of spew from Xcode')));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand,
setUpFakeXcodeBuildHandler(exitCode: 1, onRun: () {
fileSystem.systemTempDirectory.childDirectory(_xcBundleFilePath).createSync();
}),
}, stdout: 'Lots of spew from Xcode',
),
setUpXCResultCommand(stdout: kSampleResultJsonWithIssues),
setUpRsyncCommand(),
]),
......@@ -621,7 +624,6 @@ Runner requires a provisioning profile. Select a provisioning profile in the Sig
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(developmentTeam: null),
});
testUsingContext('xcresult did not detect issue but detected by stdout.', () async {
final BuildCommand command = BuildCommand();
......
......@@ -13,6 +13,7 @@ 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/mac.dart';
import 'package:flutter_tools/src/ios/xcresult.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:test/fake.dart';
......@@ -265,49 +266,48 @@ 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 ===
Could not build the precompiled application for the device.''',
xcodeBuildExecution: XcodeBuildExecution(
buildCommands: <String>['xcrun', 'xcodebuild', 'blah'],
appDirectory: '/blah/blah',
environmentType: EnvironmentType.physical,
buildSettings: buildSettings,
),
);
Check dependencies
await diagnoseXcodeBuildFailure(buildResult, testUsage, logger);
expect(
logger.errorText,
contains('Building a deployable iOS app requires a selected Development Team with a \nProvisioning Profile.'),
);
});
blah
testWithoutContext('does not show no development team message when other Xcode issues detected', () async {
final XcodeBuildResult buildResult = XcodeBuildResult(
success: false,
stdout: '''
Running "flutter pub 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 **
=== BUILD TARGET Pods-Runner OF PROJECT Pods WITH CONFIGURATION Release ===
The following build commands failed:
Check dependencies
(1 failure)
Xcode's output:
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'
[BCEROR]Signing for "Runner" requires a development team. Select a development team in the project editor.
Could not build the precompiled application for the device.''',
xcodeBuildExecution: XcodeBuildExecution(
......@@ -316,13 +316,14 @@ Could not build the precompiled application for the device.''',
environmentType: EnvironmentType.physical,
buildSettings: buildSettings,
),
xcResult: XCResult.test(issues: <XCResultIssue>[
XCResultIssue.test(message: 'Target aot_assembly_release failed', subType: 'Error'),
])
);
await diagnoseXcodeBuildFailure(buildResult, testUsage, logger);
expect(
logger.errorText,
contains('Building a deployable iOS app requires a selected Development Team with a \nProvisioning Profile.'),
);
expect(logger.errorText, contains('Error (Xcode): Target aot_assembly_release failed'));
expect(logger.errorText, isNot(contains('Building a deployable iOS app requires a selected Development Team')));
});
});
......
......@@ -64,8 +64,7 @@ int x = 'String';
], workingDirectory: projectRoot.path);
expect(
// iOS shows this as stdout.
targetPlatform == 'ios' ? result.stdout : result.stderr,
result.stderr,
contains("A value of type 'String' can't be assigned to a variable of type 'int'."),
);
expect(result.exitCode, 1);
......
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