@@ -277,26 +277,51 @@ Future<XcodeBuildResult> buildXcodeProject({
277277 );
278278 }
279279
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 > [
281298 '/usr/bin/env' ,
282299 'xcrun' ,
283300 'xcodebuild' ,
284- 'clean' ,
285301 'build' ,
286302 '-configuration' , configuration,
287303 'ONLY_ACTIVE_ARCH=YES' ,
288304 ];
289305
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+
290315 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' );
294319 }
295320
296321 final List <FileSystemEntity > contents = fs.directory (app.appDirectory).listSync ();
297322 for (FileSystemEntity entity in contents) {
298323 if (fs.path.extension (entity.path) == '.xcworkspace' ) {
299- commands .addAll (< String > [
324+ buildCommands .addAll (< String > [
300325 '-workspace' , fs.path.basename (entity.path),
301326 '-scheme' , scheme,
302327 'BUILD_DIR=${fs .path .absolute (getIosBuildDirectory ())}' ,
@@ -306,13 +331,13 @@ Future<XcodeBuildResult> buildXcodeProject({
306331 }
307332
308333 if (buildForDevice) {
309- commands .addAll (< String > ['-sdk' , 'iphoneos' , '-arch' , 'arm64' ]);
334+ buildCommands .addAll (< String > ['-sdk' , 'iphoneos' , '-arch' , 'arm64' ]);
310335 } else {
311- commands .addAll (< String > ['-sdk' , 'iphonesimulator' , '-arch' , 'x86_64' ]);
336+ buildCommands .addAll (< String > ['-sdk' , 'iphonesimulator' , '-arch' , 'x86_64' ]);
312337 }
313338
314339 if (! codesign) {
315- commands .addAll (
340+ buildCommands .addAll (
316341 < String > [
317342 'CODE_SIGNING_ALLOWED=NO' ,
318343 'CODE_SIGNING_REQUIRED=NO' ,
@@ -321,49 +346,61 @@ Future<XcodeBuildResult> buildXcodeProject({
321346 );
322347 }
323348
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,
327353 workingDirectory: app.appDirectory,
328354 allowReentrantFlutter: true
329355 );
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 ) {
332365 printStatus ('Failed to build iOS app' );
333- if (result .stderr.isNotEmpty) {
366+ if (buildResult .stderr.isNotEmpty) {
334367 printStatus ('Error output from Xcode build:\n ↳' );
335- printStatus (result .stderr, indent: 4 );
368+ printStatus (buildResult .stderr, indent: 4 );
336369 }
337- if (result .stdout.isNotEmpty) {
370+ if (buildResult .stdout.isNotEmpty) {
338371 printStatus ('Xcode\' s output:\n ↳' );
339- printStatus (result .stdout, indent: 4 );
372+ printStatus (buildResult .stdout, indent: 4 );
340373 }
341374 return new XcodeBuildResult (
342375 success: false ,
343- stdout: result .stdout,
344- stderr: result .stderr,
376+ stdout: buildResult .stdout,
377+ stderr: buildResult .stderr,
345378 xcodeBuildExecution: new XcodeBuildExecution (
346- commands ,
347- app.appDirectory,
379+ buildCommands : buildCommands ,
380+ appDirectory : app.appDirectory,
348381 buildForPhysicalDevice: buildForDevice,
382+ buildSettings: buildSettings,
349383 ),
350384 );
351385 } 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+
355391 String outputDir;
356- if (match != null ) {
357- final String actualOutputDir = match.group (1 ).replaceAll ('\\ ' , ' ' );
392+ if (fs.isDirectorySync (expectedOutputDirectory)) {
358393 // Copy app folder to a place where other tools can find it without knowing
359394 // the BuildInfo.
360- outputDir = actualOutputDir .replaceFirst ('/$configuration -' , '/' );
395+ outputDir = expectedOutputDirectory .replaceFirst ('/$configuration -' , '/' );
361396 if (fs.isDirectorySync (outputDir)) {
362397 // Previous output directory might have incompatible artifacts
363398 // (for example, kernel binary files produced from previous `--preview-dart-2` run).
364399 fs.directory (outputDir).deleteSync (recursive: true );
365400 }
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' );
367404 }
368405 return new XcodeBuildResult (success: true , output: outputDir);
369406 }
@@ -378,8 +415,7 @@ String readGeneratedXcconfig(String appPath) {
378415 return generatedXcconfigFile.readAsStringSync ();
379416}
380417
381- Future <Null > diagnoseXcodeBuildFailure (
382- XcodeBuildResult result, BuildableIOSApp app) async {
418+ Future <Null > diagnoseXcodeBuildFailure (XcodeBuildResult result) async {
383419 if (result.xcodeBuildExecution != null &&
384420 result.xcodeBuildExecution.buildForPhysicalDevice &&
385421 result.stdout? .contains ('BCEROR' ) == true &&
@@ -393,14 +429,15 @@ Future<Null> diagnoseXcodeBuildFailure(
393429 // * PROVISIONING_PROFILE (manual signing)
394430 if (result.xcodeBuildExecution != null &&
395431 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+ ) {
398435 printError (noDevelopmentTeamInstruction, emphasis: true );
399436 return ;
400437 }
401438 if (result.xcodeBuildExecution != null &&
402439 result.xcodeBuildExecution.buildForPhysicalDevice &&
403- app.id .contains ('com.example' )) {
440+ result.xcodeBuildExecution.buildSettings[ 'PRODUCT_BUNDLE_IDENTIFIER' ] .contains ('com.example' )) {
404441 printError ('' );
405442 printError ('It appears that your application still contains the default signing identifier.' );
406443 printError ("Try replacing 'com.example' with your signing id in Xcode:" );
@@ -441,17 +478,20 @@ class XcodeBuildResult {
441478/// Describes an invocation of a Xcode build command.
442479class XcodeBuildExecution {
443480 XcodeBuildExecution (
444- this .buildCommands,
445- this .appDirectory,
446481 {
482+ @required this .buildCommands,
483+ @required this .appDirectory,
447484 @required this .buildForPhysicalDevice,
485+ @required this .buildSettings,
448486 }
449487 );
450488
451489 /// The original list of Xcode build commands used to produce this build result.
452490 final List <String > buildCommands;
453491 final String appDirectory;
454492 final bool buildForPhysicalDevice;
493+ /// The build settings corresponding to the [buildCommands] invocation.
494+ final Map <String , String > buildSettings;
455495}
456496
457497final RegExp _xcodeVersionRegExp = new RegExp (r'Xcode (\d+)\..*' );
0 commit comments