Skip to content

Commit

Permalink
[Build] iOS simulator build intermittently performs a clean build (wh…
Browse files Browse the repository at this point in the history
…en it should be incremental)

rdar://127636159
https://bugs.webkit.org/show_bug.cgi?id=274184

Reviewed by Alexey Proskuryakov.

Incremental builds break when the targeted simulator changes. Xcode sets
build settings like TARGET_DEVICE_MODEL and TARGET_DEVICE_IDENTIFIER
based on destination, so when the selected simulator changes, all our
build script executions are invalidated and the next build rebuilds
nearly everything.

webkitdirs was picking the first eligible simulator as reported by
`simctl list devices`. This was causing incremental rebuilds when
engineers add and remove simulator devices (relatively common during
testing workflows). Additionally, it meant that switching between the
Xcode IDE and command line for building was likely to be non-incremental
-- you'd have to know which simulator was the special one and select it
in the UI.

Fix by teaching webkitdirs to look at previous build requests and find
the UDID of the simulator that was used. If it's still available and
eligible, build with it. If it isn't, pick an eligible simulator -- the
next request will re-use it. Instead of picking simulators based on the
order simctl reports them, sort lexicographically like Xcode does, so
that the default matches the first list item in the UI.

* Tools/Scripts/webkitdirs.pm:
(determineXcodeDestination):

Canonical link: https://commits.webkit.org/278893@main
  • Loading branch information
emw-apple committed May 16, 2024
1 parent 85bd8da commit 3a0f6f6
Showing 1 changed file with 51 additions and 9 deletions.
60 changes: 51 additions & 9 deletions Tools/Scripts/webkitdirs.pm
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use File::Path qw(make_path mkpath rmtree);
use File::Spec;
use File::Temp qw(tempdir);
use File::stat;
use List::Util;
use List::Util qw(first);
use POSIX;
use Time::HiRes qw(usleep);
use Text::ParseWords;
Expand Down Expand Up @@ -580,6 +580,17 @@ sub determineArchitecture
$architecture = 'arm64' if $architecture =~ /aarch64/i;
}

sub xcodeBuildRequestsInRecencyOrder
{
determineBaseProductDir();
my @buildRequests = sort { -M $a <=> -M $b } <"$baseProductDir/XCBuildData/*.xcbuilddata/build-request.json">;

return map {
open(my $fh, $_) or warn "Can't open previous build request: $!";
return decode_json(join '', <$fh>) if $fh;
} @buildRequests;
}

sub determineXcodeDestination
{
return if defined $destination;
Expand Down Expand Up @@ -617,16 +628,47 @@ sub determineXcodeDestination
}

if (!$generic && $xcodeSDKPlatformName =~ /simulator$/) {
# Two goals:
# 1. Find a simulator device to build for, to avoid building multiple architectures.
# 2. Try to pick a simulator that's been used before (either by command-line or IDE builds) to avoid
# unnecessary recompilation.
#
# Changing the targeted simulator between builds breaks incremental building, because it changes
# build settings like TARGET_DEVICE_IDENTIFIER.
my $prevBuildRequest = first {
$_->{parameters}->{activeRunDestination}->{platform} eq $xcodeSDKPlatformName
} xcodeBuildRequestsInRecencyOrder();
my $prevUDID = $prevBuildRequest->{parameters}->{overrides}->{synthesized}->{table}->{TARGET_DEVICE_IDENTIFIER} if $prevBuildRequest;
if ($prevBuildRequest && !$prevUDID) {
warn "Can't find UDID in previous $xcodeSDKPlatformName build request, builds may not be incremental.\n";
}

# Sort the list of devices to match the ordering in Xcode's UI.
my @devices = sort { $a->{name} cmp $b->{name} } iOSSimulatorDevices();
my $prevDevice = first { $prevUDID && $_->{UDID} eq $prevUDID } @devices;
if ($prevUDID && !$prevDevice) {
warn "Simulator with UDID '$prevUDID' not found, falling back to another available simulator. " .
"This build may not be incremental.\n";
}

# If we found the previous device, check that the runtime being built has not changed (e.g. due to a
# major SDK update). If it has changed, or if no previous device is available, fall back to the first
# eligible device in the list.
my $runtime = simulatorRuntime($portName);
for my $device (iOSSimulatorDevices()) {
if ($device->{runtime} eq $runtime) {
$destination .= ',id=' . $device->{UDID};
return;
}
my $device;
if ($prevDevice && $prevDevice->{runtime} eq $runtime) {
$device = $prevDevice;
} else {
$device = first { $_->{runtime} eq $runtime } @devices;
}

if ($device) {
$destination .= ',id=' . $device->{UDID};
} else {
warn "Unable to find a simulator target for $xcodeSDKPlatformName. " .
"Building for a generic device, which may build unwanted additional architectures";
$generic = 1;
}
warn "Unable to find a simulator target for $xcodeSDKPlatformName. " .
"Building for a generic device, which may build unwanted additional architectures";
$generic = 1;
}

$destination = 'generic/' . $destination if $generic;
Expand Down

0 comments on commit 3a0f6f6

Please sign in to comment.