@@ -277,26 +277,51 @@ Future<XcodeBuildResult> buildXcodeProject({
277
277
);
278
278
}
279
279
280
- final List <String > commands = < String > [
280
+ final Status cleanStatus =
281
+ logger.startProgress ('Running Xcode clean...' , expectSlowOperation: true );
282
+ final RunResult cleanResult = await runAsync (
283
+ < String > [
284
+ '/usr/bin/env' ,
285
+ 'xcrun' ,
286
+ 'xcodebuild' ,
287
+ 'clean' ,
288
+ '-configuration' , configuration,
289
+ ],
290
+ workingDirectory: app.appDirectory,
291
+ );
292
+ cleanStatus.stop ();
293
+ if (cleanResult.exitCode != 0 ) {
294
+ throwToolExit ('Xcode failed to clean\n ${cleanResult .stderr }' );
295
+ }
296
+
297
+ final List <String > buildCommands = < String > [
281
298
'/usr/bin/env' ,
282
299
'xcrun' ,
283
300
'xcodebuild' ,
284
- 'clean' ,
285
301
'build' ,
286
302
'-configuration' , configuration,
287
303
'ONLY_ACTIVE_ARCH=YES' ,
288
304
];
289
305
306
+ if (logger.isVerbose) {
307
+ // An environment variable to be passed to xcode_backend.sh determining
308
+ // whether to echo back executed commands.
309
+ buildCommands.add ('VERBOSE_SCRIPT_LOGGING=YES' );
310
+ } else {
311
+ // This will print warnings and errors only.
312
+ buildCommands.add ('-quiet' );
313
+ }
314
+
290
315
if (developmentTeam != null ) {
291
- commands .add ('DEVELOPMENT_TEAM=$developmentTeam ' );
292
- commands .add ('-allowProvisioningUpdates' );
293
- commands .add ('-allowProvisioningDeviceRegistration' );
316
+ buildCommands .add ('DEVELOPMENT_TEAM=$developmentTeam ' );
317
+ buildCommands .add ('-allowProvisioningUpdates' );
318
+ buildCommands .add ('-allowProvisioningDeviceRegistration' );
294
319
}
295
320
296
321
final List <FileSystemEntity > contents = fs.directory (app.appDirectory).listSync ();
297
322
for (FileSystemEntity entity in contents) {
298
323
if (fs.path.extension (entity.path) == '.xcworkspace' ) {
299
- commands .addAll (< String > [
324
+ buildCommands .addAll (< String > [
300
325
'-workspace' , fs.path.basename (entity.path),
301
326
'-scheme' , scheme,
302
327
'BUILD_DIR=${fs .path .absolute (getIosBuildDirectory ())}' ,
@@ -306,13 +331,13 @@ Future<XcodeBuildResult> buildXcodeProject({
306
331
}
307
332
308
333
if (buildForDevice) {
309
- commands .addAll (< String > ['-sdk' , 'iphoneos' , '-arch' , 'arm64' ]);
334
+ buildCommands .addAll (< String > ['-sdk' , 'iphoneos' , '-arch' , 'arm64' ]);
310
335
} else {
311
- commands .addAll (< String > ['-sdk' , 'iphonesimulator' , '-arch' , 'x86_64' ]);
336
+ buildCommands .addAll (< String > ['-sdk' , 'iphonesimulator' , '-arch' , 'x86_64' ]);
312
337
}
313
338
314
339
if (! codesign) {
315
- commands .addAll (
340
+ buildCommands .addAll (
316
341
< String > [
317
342
'CODE_SIGNING_ALLOWED=NO' ,
318
343
'CODE_SIGNING_REQUIRED=NO' ,
@@ -321,49 +346,61 @@ Future<XcodeBuildResult> buildXcodeProject({
321
346
);
322
347
}
323
348
324
- final Status status = logger.startProgress ('Running Xcode build...' , expectSlowOperation: true );
325
- final RunResult result = await runAsync (
326
- commands,
349
+ final Status buildStatus =
350
+ logger.startProgress ('Running Xcode build...' , expectSlowOperation: true );
351
+ final RunResult buildResult = await runAsync (
352
+ buildCommands,
327
353
workingDirectory: app.appDirectory,
328
354
allowReentrantFlutter: true
329
355
);
330
- status.stop ();
331
- if (result.exitCode != 0 ) {
356
+ buildStatus.stop ();
357
+
358
+ // Run -showBuildSettings again but with the exact same parameters as the build.
359
+ final Map <String , String > buildSettings = parseXcodeBuildSettings (runCheckedSync (
360
+ new List <String >.from (buildCommands)..add ('-showBuildSettings' ),
361
+ workingDirectory: app.appDirectory,
362
+ ));
363
+
364
+ if (buildResult.exitCode != 0 ) {
332
365
printStatus ('Failed to build iOS app' );
333
- if (result .stderr.isNotEmpty) {
366
+ if (buildResult .stderr.isNotEmpty) {
334
367
printStatus ('Error output from Xcode build:\n ↳' );
335
- printStatus (result .stderr, indent: 4 );
368
+ printStatus (buildResult .stderr, indent: 4 );
336
369
}
337
- if (result .stdout.isNotEmpty) {
370
+ if (buildResult .stdout.isNotEmpty) {
338
371
printStatus ('Xcode\' s output:\n ↳' );
339
- printStatus (result .stdout, indent: 4 );
372
+ printStatus (buildResult .stdout, indent: 4 );
340
373
}
341
374
return new XcodeBuildResult (
342
375
success: false ,
343
- stdout: result .stdout,
344
- stderr: result .stderr,
376
+ stdout: buildResult .stdout,
377
+ stderr: buildResult .stderr,
345
378
xcodeBuildExecution: new XcodeBuildExecution (
346
- commands ,
347
- app.appDirectory,
379
+ buildCommands : buildCommands ,
380
+ appDirectory : app.appDirectory,
348
381
buildForPhysicalDevice: buildForDevice,
382
+ buildSettings: buildSettings,
349
383
),
350
384
);
351
385
} else {
352
- // Look for 'clean build/<configuration>-<sdk>/Runner.app'.
353
- final RegExp regexp = new RegExp (r' clean (.*\.app)$' , multiLine: true );
354
- final Match match = regexp.firstMatch (result.stdout);
386
+ final String expectedOutputDirectory = fs.path.join (
387
+ buildSettings['TARGET_BUILD_DIR' ],
388
+ buildSettings['WRAPPER_NAME' ],
389
+ );
390
+
355
391
String outputDir;
356
- if (match != null ) {
357
- final String actualOutputDir = match.group (1 ).replaceAll ('\\ ' , ' ' );
392
+ if (fs.isDirectorySync (expectedOutputDirectory)) {
358
393
// Copy app folder to a place where other tools can find it without knowing
359
394
// the BuildInfo.
360
- outputDir = actualOutputDir .replaceFirst ('/$configuration -' , '/' );
395
+ outputDir = expectedOutputDirectory .replaceFirst ('/$configuration -' , '/' );
361
396
if (fs.isDirectorySync (outputDir)) {
362
397
// Previous output directory might have incompatible artifacts
363
398
// (for example, kernel binary files produced from previous `--preview-dart-2` run).
364
399
fs.directory (outputDir).deleteSync (recursive: true );
365
400
}
366
- copyDirectorySync (fs.directory (actualOutputDir), fs.directory (outputDir));
401
+ copyDirectorySync (fs.directory (expectedOutputDirectory), fs.directory (outputDir));
402
+ } else {
403
+ printError ('Build succeeded but the expected app at $expectedOutputDirectory not found' );
367
404
}
368
405
return new XcodeBuildResult (success: true , output: outputDir);
369
406
}
@@ -378,8 +415,7 @@ String readGeneratedXcconfig(String appPath) {
378
415
return generatedXcconfigFile.readAsStringSync ();
379
416
}
380
417
381
- Future <Null > diagnoseXcodeBuildFailure (
382
- XcodeBuildResult result, BuildableIOSApp app) async {
418
+ Future <Null > diagnoseXcodeBuildFailure (XcodeBuildResult result) async {
383
419
if (result.xcodeBuildExecution != null &&
384
420
result.xcodeBuildExecution.buildForPhysicalDevice &&
385
421
result.stdout? .contains ('BCEROR' ) == true &&
@@ -393,14 +429,15 @@ Future<Null> diagnoseXcodeBuildFailure(
393
429
// * PROVISIONING_PROFILE (manual signing)
394
430
if (result.xcodeBuildExecution != null &&
395
431
result.xcodeBuildExecution.buildForPhysicalDevice &&
396
- app.buildSettings != null &&
397
- ! < String > ['DEVELOPMENT_TEAM' , 'PROVISIONING_PROFILE' ].any (app.buildSettings.containsKey)) {
432
+ ! < String > ['DEVELOPMENT_TEAM' , 'PROVISIONING_PROFILE' ].any (
433
+ result.xcodeBuildExecution.buildSettings.containsKey)
434
+ ) {
398
435
printError (noDevelopmentTeamInstruction, emphasis: true );
399
436
return ;
400
437
}
401
438
if (result.xcodeBuildExecution != null &&
402
439
result.xcodeBuildExecution.buildForPhysicalDevice &&
403
- app.id .contains ('com.example' )) {
440
+ result.xcodeBuildExecution.buildSettings[ 'PRODUCT_BUNDLE_IDENTIFIER' ] .contains ('com.example' )) {
404
441
printError ('' );
405
442
printError ('It appears that your application still contains the default signing identifier.' );
406
443
printError ("Try replacing 'com.example' with your signing id in Xcode:" );
@@ -441,17 +478,20 @@ class XcodeBuildResult {
441
478
/// Describes an invocation of a Xcode build command.
442
479
class XcodeBuildExecution {
443
480
XcodeBuildExecution (
444
- this .buildCommands,
445
- this .appDirectory,
446
481
{
482
+ @required this .buildCommands,
483
+ @required this .appDirectory,
447
484
@required this .buildForPhysicalDevice,
485
+ @required this .buildSettings,
448
486
}
449
487
);
450
488
451
489
/// The original list of Xcode build commands used to produce this build result.
452
490
final List <String > buildCommands;
453
491
final String appDirectory;
454
492
final bool buildForPhysicalDevice;
493
+ /// The build settings corresponding to the [buildCommands] invocation.
494
+ final Map <String , String > buildSettings;
455
495
}
456
496
457
497
final RegExp _xcodeVersionRegExp = new RegExp (r'Xcode (\d+)\..*' );
0 commit comments