* Revert "Revert "Reduce xcodebuild noise #2" (#14641)"
This reverts commit 2d47481f1e.
* Stop scrapping xcodebuild output, get the right build settings
* clone the command params first
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
# found in the LICENSE file.
|
||||
|
||||
RunCommand() {
|
||||
echo "♦ $*"
|
||||
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
|
||||
echo "♦ $*"
|
||||
fi
|
||||
"$@"
|
||||
return $?
|
||||
}
|
||||
|
||||
@@ -210,7 +210,11 @@ class BuildableIOSApp extends IOSApp {
|
||||
|
||||
final String appDirectory;
|
||||
|
||||
/// Build settings of the app's XCode project.
|
||||
/// Build settings of the app's Xcode project.
|
||||
///
|
||||
/// These are the build settings as specified in the Xcode project files.
|
||||
///
|
||||
/// Build settings may change depending on the parameters passed while building.
|
||||
final Map<String, String> buildSettings;
|
||||
|
||||
@override
|
||||
|
||||
@@ -76,7 +76,7 @@ class BuildIOSCommand extends BuildSubCommand {
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
await diagnoseXcodeBuildFailure(result, app);
|
||||
await diagnoseXcodeBuildFailure(result);
|
||||
throwToolExit('Encountered error while building for $logTarget.');
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ class IOSDevice extends Device {
|
||||
);
|
||||
if (!buildResult.success) {
|
||||
printError('Could not build the precompiled application for the device.');
|
||||
await diagnoseXcodeBuildFailure(buildResult, app);
|
||||
await diagnoseXcodeBuildFailure(buildResult);
|
||||
printError('');
|
||||
return new LaunchResult.failed();
|
||||
}
|
||||
|
||||
@@ -277,26 +277,51 @@ Future<XcodeBuildResult> buildXcodeProject({
|
||||
);
|
||||
}
|
||||
|
||||
final List<String> commands = <String>[
|
||||
final Status cleanStatus =
|
||||
logger.startProgress('Running Xcode clean...', expectSlowOperation: true);
|
||||
final RunResult cleanResult = await runAsync(
|
||||
<String>[
|
||||
'/usr/bin/env',
|
||||
'xcrun',
|
||||
'xcodebuild',
|
||||
'clean',
|
||||
'-configuration', configuration,
|
||||
],
|
||||
workingDirectory: app.appDirectory,
|
||||
);
|
||||
cleanStatus.stop();
|
||||
if (cleanResult.exitCode != 0) {
|
||||
throwToolExit('Xcode failed to clean\n${cleanResult.stderr}');
|
||||
}
|
||||
|
||||
final List<String> buildCommands = <String>[
|
||||
'/usr/bin/env',
|
||||
'xcrun',
|
||||
'xcodebuild',
|
||||
'clean',
|
||||
'build',
|
||||
'-configuration', configuration,
|
||||
'ONLY_ACTIVE_ARCH=YES',
|
||||
];
|
||||
|
||||
if (logger.isVerbose) {
|
||||
// An environment variable to be passed to xcode_backend.sh determining
|
||||
// whether to echo back executed commands.
|
||||
buildCommands.add('VERBOSE_SCRIPT_LOGGING=YES');
|
||||
} else {
|
||||
// This will print warnings and errors only.
|
||||
buildCommands.add('-quiet');
|
||||
}
|
||||
|
||||
if (developmentTeam != null) {
|
||||
commands.add('DEVELOPMENT_TEAM=$developmentTeam');
|
||||
commands.add('-allowProvisioningUpdates');
|
||||
commands.add('-allowProvisioningDeviceRegistration');
|
||||
buildCommands.add('DEVELOPMENT_TEAM=$developmentTeam');
|
||||
buildCommands.add('-allowProvisioningUpdates');
|
||||
buildCommands.add('-allowProvisioningDeviceRegistration');
|
||||
}
|
||||
|
||||
final List<FileSystemEntity> contents = fs.directory(app.appDirectory).listSync();
|
||||
for (FileSystemEntity entity in contents) {
|
||||
if (fs.path.extension(entity.path) == '.xcworkspace') {
|
||||
commands.addAll(<String>[
|
||||
buildCommands.addAll(<String>[
|
||||
'-workspace', fs.path.basename(entity.path),
|
||||
'-scheme', scheme,
|
||||
'BUILD_DIR=${fs.path.absolute(getIosBuildDirectory())}',
|
||||
@@ -306,13 +331,13 @@ Future<XcodeBuildResult> buildXcodeProject({
|
||||
}
|
||||
|
||||
if (buildForDevice) {
|
||||
commands.addAll(<String>['-sdk', 'iphoneos', '-arch', 'arm64']);
|
||||
buildCommands.addAll(<String>['-sdk', 'iphoneos', '-arch', 'arm64']);
|
||||
} else {
|
||||
commands.addAll(<String>['-sdk', 'iphonesimulator', '-arch', 'x86_64']);
|
||||
buildCommands.addAll(<String>['-sdk', 'iphonesimulator', '-arch', 'x86_64']);
|
||||
}
|
||||
|
||||
if (!codesign) {
|
||||
commands.addAll(
|
||||
buildCommands.addAll(
|
||||
<String>[
|
||||
'CODE_SIGNING_ALLOWED=NO',
|
||||
'CODE_SIGNING_REQUIRED=NO',
|
||||
@@ -321,49 +346,61 @@ Future<XcodeBuildResult> buildXcodeProject({
|
||||
);
|
||||
}
|
||||
|
||||
final Status status = logger.startProgress('Running Xcode build...', expectSlowOperation: true);
|
||||
final RunResult result = await runAsync(
|
||||
commands,
|
||||
final Status buildStatus =
|
||||
logger.startProgress('Running Xcode build...', expectSlowOperation: true);
|
||||
final RunResult buildResult = await runAsync(
|
||||
buildCommands,
|
||||
workingDirectory: app.appDirectory,
|
||||
allowReentrantFlutter: true
|
||||
);
|
||||
status.stop();
|
||||
if (result.exitCode != 0) {
|
||||
buildStatus.stop();
|
||||
|
||||
// Run -showBuildSettings again but with the exact same parameters as the build.
|
||||
final Map<String, String> buildSettings = parseXcodeBuildSettings(runCheckedSync(
|
||||
new List<String>.from(buildCommands)..add('-showBuildSettings'),
|
||||
workingDirectory: app.appDirectory,
|
||||
));
|
||||
|
||||
if (buildResult.exitCode != 0) {
|
||||
printStatus('Failed to build iOS app');
|
||||
if (result.stderr.isNotEmpty) {
|
||||
if (buildResult.stderr.isNotEmpty) {
|
||||
printStatus('Error output from Xcode build:\n↳');
|
||||
printStatus(result.stderr, indent: 4);
|
||||
printStatus(buildResult.stderr, indent: 4);
|
||||
}
|
||||
if (result.stdout.isNotEmpty) {
|
||||
if (buildResult.stdout.isNotEmpty) {
|
||||
printStatus('Xcode\'s output:\n↳');
|
||||
printStatus(result.stdout, indent: 4);
|
||||
printStatus(buildResult.stdout, indent: 4);
|
||||
}
|
||||
return new XcodeBuildResult(
|
||||
success: false,
|
||||
stdout: result.stdout,
|
||||
stderr: result.stderr,
|
||||
stdout: buildResult.stdout,
|
||||
stderr: buildResult.stderr,
|
||||
xcodeBuildExecution: new XcodeBuildExecution(
|
||||
commands,
|
||||
app.appDirectory,
|
||||
buildCommands: buildCommands,
|
||||
appDirectory: app.appDirectory,
|
||||
buildForPhysicalDevice: buildForDevice,
|
||||
buildSettings: buildSettings,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Look for 'clean build/<configuration>-<sdk>/Runner.app'.
|
||||
final RegExp regexp = new RegExp(r' clean (.*\.app)$', multiLine: true);
|
||||
final Match match = regexp.firstMatch(result.stdout);
|
||||
final String expectedOutputDirectory = fs.path.join(
|
||||
buildSettings['TARGET_BUILD_DIR'],
|
||||
buildSettings['WRAPPER_NAME'],
|
||||
);
|
||||
|
||||
String outputDir;
|
||||
if (match != null) {
|
||||
final String actualOutputDir = match.group(1).replaceAll('\\ ', ' ');
|
||||
if (fs.isDirectorySync(expectedOutputDirectory)) {
|
||||
// Copy app folder to a place where other tools can find it without knowing
|
||||
// the BuildInfo.
|
||||
outputDir = actualOutputDir.replaceFirst('/$configuration-', '/');
|
||||
outputDir = expectedOutputDirectory.replaceFirst('/$configuration-', '/');
|
||||
if (fs.isDirectorySync(outputDir)) {
|
||||
// Previous output directory might have incompatible artifacts
|
||||
// (for example, kernel binary files produced from previous `--preview-dart-2` run).
|
||||
fs.directory(outputDir).deleteSync(recursive: true);
|
||||
}
|
||||
copyDirectorySync(fs.directory(actualOutputDir), fs.directory(outputDir));
|
||||
copyDirectorySync(fs.directory(expectedOutputDirectory), fs.directory(outputDir));
|
||||
} else {
|
||||
printError('Build succeeded but the expected app at $expectedOutputDirectory not found');
|
||||
}
|
||||
return new XcodeBuildResult(success: true, output: outputDir);
|
||||
}
|
||||
@@ -378,8 +415,7 @@ String readGeneratedXcconfig(String appPath) {
|
||||
return generatedXcconfigFile.readAsStringSync();
|
||||
}
|
||||
|
||||
Future<Null> diagnoseXcodeBuildFailure(
|
||||
XcodeBuildResult result, BuildableIOSApp app) async {
|
||||
Future<Null> diagnoseXcodeBuildFailure(XcodeBuildResult result) async {
|
||||
if (result.xcodeBuildExecution != null &&
|
||||
result.xcodeBuildExecution.buildForPhysicalDevice &&
|
||||
result.stdout?.contains('BCEROR') == true &&
|
||||
@@ -393,14 +429,15 @@ Future<Null> diagnoseXcodeBuildFailure(
|
||||
// * PROVISIONING_PROFILE (manual signing)
|
||||
if (result.xcodeBuildExecution != null &&
|
||||
result.xcodeBuildExecution.buildForPhysicalDevice &&
|
||||
app.buildSettings != null &&
|
||||
!<String>['DEVELOPMENT_TEAM', 'PROVISIONING_PROFILE'].any(app.buildSettings.containsKey)) {
|
||||
!<String>['DEVELOPMENT_TEAM', 'PROVISIONING_PROFILE'].any(
|
||||
result.xcodeBuildExecution.buildSettings.containsKey)
|
||||
) {
|
||||
printError(noDevelopmentTeamInstruction, emphasis: true);
|
||||
return;
|
||||
}
|
||||
if (result.xcodeBuildExecution != null &&
|
||||
result.xcodeBuildExecution.buildForPhysicalDevice &&
|
||||
app.id.contains('com.example')) {
|
||||
result.xcodeBuildExecution.buildSettings['PRODUCT_BUNDLE_IDENTIFIER'].contains('com.example')) {
|
||||
printError('');
|
||||
printError('It appears that your application still contains the default signing identifier.');
|
||||
printError("Try replacing 'com.example' with your signing id in Xcode:");
|
||||
@@ -441,10 +478,11 @@ class XcodeBuildResult {
|
||||
/// Describes an invocation of a Xcode build command.
|
||||
class XcodeBuildExecution {
|
||||
XcodeBuildExecution(
|
||||
this.buildCommands,
|
||||
this.appDirectory,
|
||||
{
|
||||
@required this.buildCommands,
|
||||
@required this.appDirectory,
|
||||
@required this.buildForPhysicalDevice,
|
||||
@required this.buildSettings,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -452,6 +490,8 @@ class XcodeBuildExecution {
|
||||
final List<String> buildCommands;
|
||||
final String appDirectory;
|
||||
final bool buildForPhysicalDevice;
|
||||
/// The build settings corresponding to the [buildCommands] invocation.
|
||||
final Map<String, String> buildSettings;
|
||||
}
|
||||
|
||||
final RegExp _xcodeVersionRegExp = new RegExp(r'Xcode (\d+)\..*');
|
||||
|
||||
@@ -72,8 +72,12 @@ Map<String, String> getXcodeBuildSettings(String xcodeProjPath, String target) {
|
||||
final String out = runCheckedSync(<String>[
|
||||
'/usr/bin/xcodebuild', '-project', absProjPath, '-target', target, '-showBuildSettings'
|
||||
]);
|
||||
return parseXcodeBuildSettings(out);
|
||||
}
|
||||
|
||||
Map<String, String> parseXcodeBuildSettings(String showBuildSettingsOutput) {
|
||||
final Map<String, String> settings = <String, String>{};
|
||||
for (String line in out.split('\n').where(_settingExpr.hasMatch)) {
|
||||
for (String line in showBuildSettingsOutput.split('\n').where(_settingExpr.hasMatch)) {
|
||||
final Match match = _settingExpr.firstMatch(line);
|
||||
settings[match[1]] = match[2];
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:flutter_tools/src/application_package.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult;
|
||||
import 'package:flutter_tools/src/ios/mac.dart';
|
||||
@@ -243,15 +242,12 @@ void main() {
|
||||
});
|
||||
|
||||
group('Diagnose Xcode build failure', () {
|
||||
BuildableIOSApp app;
|
||||
Map<String, String> buildSettings;
|
||||
|
||||
setUp(() {
|
||||
app = new BuildableIOSApp(
|
||||
projectBundleId: 'test.app',
|
||||
buildSettings: <String, String>{
|
||||
'For our purposes': 'a non-empty build settings map is valid',
|
||||
},
|
||||
);
|
||||
buildSettings = <String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'test.app',
|
||||
};
|
||||
});
|
||||
|
||||
testUsingContext('No provisioning profile shows message', () async {
|
||||
@@ -313,13 +309,14 @@ 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
|
||||
buildCommands: <String>['xcrun', 'xcodebuild', 'blah'],
|
||||
appDirectory: '/blah/blah',
|
||||
buildForPhysicalDevice: true,
|
||||
buildSettings: buildSettings,
|
||||
),
|
||||
);
|
||||
|
||||
await diagnoseXcodeBuildFailure(buildResult, app);
|
||||
await diagnoseXcodeBuildFailure(buildResult);
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
contains('No Provisioning Profile was found for your project\'s Bundle Identifier or your device.'),
|
||||
@@ -393,13 +390,14 @@ Xcode's output:
|
||||
|
||||
Could not build the precompiled application for the device.''',
|
||||
xcodeBuildExecution: new XcodeBuildExecution(
|
||||
<String>['xcrun', 'xcodebuild', 'blah'],
|
||||
'/blah/blah',
|
||||
buildForPhysicalDevice: true
|
||||
buildCommands: <String>['xcrun', 'xcodebuild', 'blah'],
|
||||
appDirectory: '/blah/blah',
|
||||
buildForPhysicalDevice: true,
|
||||
buildSettings: buildSettings,
|
||||
),
|
||||
);
|
||||
|
||||
await diagnoseXcodeBuildFailure(buildResult, app);
|
||||
await diagnoseXcodeBuildFailure(buildResult);
|
||||
expect(
|
||||
testLogger.errorText,
|
||||
contains('Building a deployable iOS app requires a selected Development Team with a Provisioning Profile'),
|
||||
|
||||
Reference in New Issue
Block a user