Title: [179130] trunk/Tools
Revision
179130
Author
[email protected]
Date
2015-01-26 11:45:48 -0800 (Mon, 26 Jan 2015)

Log Message

Teach run-webkit-app --simulator how to install custom built app
https://bugs.webkit.org/show_bug.cgi?id=140691

Reviewed by David Kilzer.

We should teach run-webkit-app --simulator how to install a custom built app.

Currently run-webkit-app --simulator can only run a system app or an app that
was installed using the simctl command line tool. For convenience we should
teach run-webkit-app --simulator how to install a custom built app.

As a side effect of this change run-safari --simulator will install and run
a custom built of MobileSafari (if it exists).

* Scripts/webkitdirs.pm:
(iOSSimulatorDevices): Added FIXME comment to decouple device representation in
Perl from the structure of the device.plist file.
(plistPathFromBundle): Fix if-statement condition so that we actually perform a
file system check to determine the plist path for a Mac app bundle.
(appIdentifierFromBundle): Pass absolute files system path to defaults(1). Otherwise,
it will complain that it cannot find the file: "Domain ... does not exist".
(appDisplayNameFromBundle): Ditto.
(waitUntilIOSSimulatorDeviceIsInState): Added; helper function that does not return
until a simulator device is the specified state.
(relaunchIOSSimulator): Renamed; formerly named openIOSSimulator. Quits iOS Simulator
(if it's open) before opening it again so as to ensure that the iOS Simulator boots
the specified device.
(quitIOSSimulator): Added optional parameter, $waitForShutdownOfSimulatedDeviceUDID.
As implied by its name, this function will not return until the specified simulator
device UDID is in the shutdown state.
(iosSimulatorDeviceByUDID): Added; returns the device dictionary object for the simulator
device with the specified UDID.
(isIOSSimulatorSystemInstalledApp): Resolve symbolic links in iosSimulatorApplicationsPath()
before using it as part of a prefix match to avoid a mismatch. In the public iOS 8.1 SDK the
return value of iosSimulatorApplicationsPath(), which returns a result analogous to the shell
_expression_ echo `xcrun --sdk iphonesimulator --show-sdk-path`/Applications/, contains a
symbolic link. Specifically, echo `xcrun --sdk iphonesimulator --show-sdk-path` returns
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.1.sdk,
which is a symbolic link to /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk.
(hasUserInstalledAppInSimulatorDevice): Added.
(isSimulatorDeviceBooted): Added; returns whether the specified simulator device is in the
"Booted" state.
(runIOSWebKitAppInSimulator): Modified to install a custom built app (if needed). For now,
switching between a system installed app and a custom built version of the same app (e.g.
custom build of MobileSafari) will erase all contents and settings in the simulator device.
(eraseIOSSimulatorDevice): Deleted; inline implementation into runIOSWebKitAppInSimulator()
as that was the only caller of this function.
(bootedIOSSimulatorDevice): Deleted.
(openIOSSimulator): Deleted.

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (179129 => 179130)


--- trunk/Tools/ChangeLog	2015-01-26 19:44:09 UTC (rev 179129)
+++ trunk/Tools/ChangeLog	2015-01-26 19:45:48 UTC (rev 179130)
@@ -1,3 +1,55 @@
+2015-01-26  Daniel Bates  <[email protected]>
+
+        Teach run-webkit-app --simulator how to install custom built app
+        https://bugs.webkit.org/show_bug.cgi?id=140691
+
+        Reviewed by David Kilzer.
+
+        We should teach run-webkit-app --simulator how to install a custom built app.
+
+        Currently run-webkit-app --simulator can only run a system app or an app that
+        was installed using the simctl command line tool. For convenience we should
+        teach run-webkit-app --simulator how to install a custom built app.
+
+        As a side effect of this change run-safari --simulator will install and run
+        a custom built of MobileSafari (if it exists).
+
+        * Scripts/webkitdirs.pm:
+        (iOSSimulatorDevices): Added FIXME comment to decouple device representation in
+        Perl from the structure of the device.plist file.
+        (plistPathFromBundle): Fix if-statement condition so that we actually perform a
+        file system check to determine the plist path for a Mac app bundle.
+        (appIdentifierFromBundle): Pass absolute files system path to defaults(1). Otherwise,
+        it will complain that it cannot find the file: "Domain ... does not exist".
+        (appDisplayNameFromBundle): Ditto.
+        (waitUntilIOSSimulatorDeviceIsInState): Added; helper function that does not return
+        until a simulator device is the specified state.
+        (relaunchIOSSimulator): Renamed; formerly named openIOSSimulator. Quits iOS Simulator
+        (if it's open) before opening it again so as to ensure that the iOS Simulator boots
+        the specified device.
+        (quitIOSSimulator): Added optional parameter, $waitForShutdownOfSimulatedDeviceUDID.
+        As implied by its name, this function will not return until the specified simulator
+        device UDID is in the shutdown state.
+        (iosSimulatorDeviceByUDID): Added; returns the device dictionary object for the simulator
+        device with the specified UDID.
+        (isIOSSimulatorSystemInstalledApp): Resolve symbolic links in iosSimulatorApplicationsPath()
+        before using it as part of a prefix match to avoid a mismatch. In the public iOS 8.1 SDK the
+        return value of iosSimulatorApplicationsPath(), which returns a result analogous to the shell
+        _expression_ echo `xcrun --sdk iphonesimulator --show-sdk-path`/Applications/, contains a
+        symbolic link. Specifically, echo `xcrun --sdk iphonesimulator --show-sdk-path` returns
+        /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.1.sdk,
+        which is a symbolic link to /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk.
+        (hasUserInstalledAppInSimulatorDevice): Added.
+        (isSimulatorDeviceBooted): Added; returns whether the specified simulator device is in the
+        "Booted" state.
+        (runIOSWebKitAppInSimulator): Modified to install a custom built app (if needed). For now,
+        switching between a system installed app and a custom built version of the same app (e.g.
+        custom build of MobileSafari) will erase all contents and settings in the simulator device.
+        (eraseIOSSimulatorDevice): Deleted; inline implementation into runIOSWebKitAppInSimulator()
+        as that was the only caller of this function.
+        (bootedIOSSimulatorDevice): Deleted.
+        (openIOSSimulator): Deleted.
+
 2015-01-26  Alexey Proskuryakov  <[email protected]>
 
         Update bot assignments.

Modified: trunk/Tools/Scripts/webkitdirs.pm (179129 => 179130)


--- trunk/Tools/Scripts/webkitdirs.pm	2015-01-26 19:44:09 UTC (rev 179129)
+++ trunk/Tools/Scripts/webkitdirs.pm	2015-01-26 19:45:48 UTC (rev 179130)
@@ -37,11 +37,13 @@
 use Digest::MD5 qw(md5_hex);
 use FindBin;
 use File::Basename;
+use File::Find;
 use File::Path qw(mkpath rmtree);
 use File::Spec;
 use File::stat;
 use List::Util;
 use POSIX;
+use Time::HiRes qw(usleep);
 use VCSUtils;
 
 BEGIN {
@@ -66,11 +68,11 @@
        &findOrCreateSimulatorForIOSDevice
        &iosSimulatorDeviceByName
        &nmPath
-       &openIOSSimulator
        &passedConfiguration
        &printHelpAndExitForRunAndDebugWebKitAppIfNeeded
        &productDir
        &quitIOSSimulator
+       &relaunchIOSSimulator
        &runIOSWebKitApp
        &runMacWebKitApp
        &safariPath
@@ -86,6 +88,7 @@
 
 use constant USE_OPEN_COMMAND => 1; # Used in runMacWebKitApp().
 use constant INCLUDE_OPTIONS_FOR_DEBUGGING => 1;
+use constant SIMULATOR_DEVICE_STATE_SHUTDOWN => "1";
 use constant SIMULATOR_DEVICE_STATE_BOOTED => "3";
 
 our @EXPORT_OK;
@@ -1150,6 +1153,9 @@
     } readdir(DEVICES);
     close(DEVICES);
 
+    # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
+    #        dictionary keys so as to decouple our representation of the plist from the actual structure
+    #        of the plist, which may change.
     my @devices = map {
         Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_("$devicesPath/$_/device.plist"));
     } @udids;
@@ -1180,23 +1186,6 @@
     die "Device $name $deviceTypeId $runtimeId wasn't found in " . iOSSimulatorDevicesPath();
 }
 
-sub eraseIOSSimulatorDevice($)
-{
-    my $udid = shift;
-    return exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "erase", $udid)) == 0;
-}
-
-sub bootedIOSSimulatorDevice()
-{
-    my @devices = iOSSimulatorDevices();
-    for my $device (@devices) {
-        if ($device->{state} eq SIMULATOR_DEVICE_STATE_BOOTED) {
-            return $device;
-        }
-    }
-    return undef;
-}
-
 sub willUseIOSDeviceSDKWhenBuilding()
 {
     return xcodeSDKPlatformName() eq "iphoneos";
@@ -2063,14 +2052,14 @@
 {
     my ($appBundle) = @_;
     return "$appBundle/Info.plist" if -f "$appBundle/Info.plist"; # iOS app bundle
-    return "$appBundle/Contents/Info.plist" if "$appBundle/Contents/Info.plist"; # Mac app bundle
+    return "$appBundle/Contents/Info.plist" if -f "$appBundle/Contents/Info.plist"; # Mac app bundle
     return "";
 }
 
 sub appIdentifierFromBundle($)
 {
     my ($appBundle) = @_;
-    my $plistPath = plistPathFromBundle($appBundle);
+    my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
     chomp(my $bundleIdentifier = `defaults read '$plistPath' CFBundleIdentifier 2> /dev/null`);
     return $bundleIdentifier;
 }
@@ -2078,28 +2067,45 @@
 sub appDisplayNameFromBundle($)
 {
     my ($appBundle) = @_;
-    my $plistPath = plistPathFromBundle($appBundle);
+    my $plistPath = File::Spec->rel2abs(plistPathFromBundle($appBundle)); # defaults(1) will complain if the specified path is not absolute.
     chomp(my $bundleDisplayName = `defaults read '$plistPath' CFBundleDisplayName 2> /dev/null`);
     return $bundleDisplayName;
 }
 
-sub openIOSSimulator($)
+sub waitUntilIOSSimulatorDeviceIsInState($$)
 {
+    my ($deviceUDID, $waitUntilState) = @_;
+    my $device = iosSimulatorDeviceByUDID($deviceUDID);
+    while ($device->{state} ne $waitUntilState) {
+        usleep(500 * 1000); # Waiting 500ms between file system polls does not make script run-safari feel sluggish.
+        $device = iosSimulatorDeviceByUDID($deviceUDID);
+    }
+}
+
+sub relaunchIOSSimulator($)
+{
     my ($simulatedDevice) = @_;
+    quitIOSSimulator($simulatedDevice->{UDID});
+
     chomp(my $developerDirectory = $ENV{DEVELOPER_DIR} || `xcode-select --print-path`);
     my $iosSimulatorPath = File::Spec->catfile($developerDirectory, "Applications", "iOS Simulator.app");
 
     system("open", "-a", $iosSimulatorPath, "--args", "-CurrentDeviceUDID", $simulatedDevice->{UDID}) == 0 or die "Failed to open $iosSimulatorPath: $!";
-    my $device;
-    do {
-        $device = iosSimulatorDeviceByName($simulatedDevice->{name});
-        sleep(2);
-    } while ($device->{state} ne SIMULATOR_DEVICE_STATE_BOOTED);
+    waitUntilIOSSimulatorDeviceIsInState($simulatedDevice->{UDID}, SIMULATOR_DEVICE_STATE_BOOTED);
 }
 
-sub quitIOSSimulator()
+sub quitIOSSimulator(;$)
 {
-    return system {"osascript"} "osascript", "-e", 'tell application "iOS Simulator" to quit';
+    my ($waitForShutdownOfSimulatedDeviceUDID) = @_;
+    exitStatus(system {"osascript"} "osascript", "-e", 'tell application "iOS Simulator" to quit') == 0 or die "Failed to quit iOS Simulator: $!";
+    if (!defined($waitForShutdownOfSimulatedDeviceUDID)) {
+        return;
+    }
+    # FIXME: We assume that $waitForShutdownOfSimulatedDeviceUDID was not booted using the simctl command line tool.
+    #        Otherwise we will spin indefinitely since quiting the iOS Simulator will not shutdown this device. We
+    #        should add a maximum time limit to wait for a device to shutdown and either return an error or die()
+    #        on expiration of the time limit.
+    waitUntilIOSSimulatorDeviceIsInState($waitForShutdownOfSimulatedDeviceUDID, SIMULATOR_DEVICE_STATE_SHUTDOWN);
 }
 
 sub iosSimulatorDeviceByName($)
@@ -2115,6 +2121,20 @@
     return undef;
 }
 
+sub iosSimulatorDeviceByUDID($)
+{
+    my ($simulatedDeviceUDID) = @_;
+    my $devicePlistPath = File::Spec->catfile(iOSSimulatorDevicesPath(), $simulatedDeviceUDID, "device.plist");
+    if (!-f $devicePlistPath) {
+        return;
+    }
+    # FIXME: We should parse the device.plist file ourself and map the dictionary keys in it to known
+    #        dictionary keys so as to decouple our representation of the plist from the actual structure
+    #        of the plist, which may change.
+    eval "require Foundation";
+    return Foundation::perlRefFromObjectRef(NSDictionary->dictionaryWithContentsOfFile_($devicePlistPath));
+}
+
 sub iosSimulatorRuntime()
 {
     my $xcodeSDKVersion = xcodeSDKVersion();
@@ -2142,21 +2162,85 @@
 sub isIOSSimulatorSystemInstalledApp($)
 {
     my ($appBundle) = @_;
-    my $simulatorApplicationsPath = iosSimulatorApplicationsPath();
+    my $simulatorApplicationsPath = realpath(iosSimulatorApplicationsPath());
     return substr(realpath($appBundle), 0, length($simulatorApplicationsPath)) eq $simulatorApplicationsPath;
 }
 
+sub hasUserInstalledAppInSimulatorDevice($$)
+{
+    my ($appIdentifier, $simulatedDeviceUDID) = @_;
+    my $userInstalledAppPath = File::Spec->catfile($ENV{HOME}, "Library", "Developer", "CoreSimulator", "Devices", $simulatedDeviceUDID, "data", "Containers", "Bundle", "Application");
+    if (!$userInstalledAppPath) {
+        return 0; # No user installed apps.
+    }
+    local @::userInstalledAppBundles;
+    sub wanted {
+        my $file = $_;
+
+        # Ignore hidden files and directories.
+        if ($file =~ /^\../) {
+            $File::Find::prune = 1;
+            return;
+        }
+
+        return if !-d $file || $file !~ /\.app$/;
+        push @::userInstalledAppBundles, $File::Find::name;
+        $File::Find::prune = 1; # Do not traverse contents of app bundle.
+    }
+    find(\&wanted, $userInstalledAppPath);
+    for my $userInstalledAppBundle (@::userInstalledAppBundles) {
+        if (appIdentifierFromBundle($userInstalledAppBundle) eq $appIdentifier) {
+            return 1; # Has user installed app.
+        }
+    }
+    return 0; # Does not have user installed app.
+}
+
+sub isSimulatorDeviceBooted($)
+{
+    my ($simulatedDeviceUDID) = @_;
+    my $device = iosSimulatorDeviceByUDID($simulatedDeviceUDID);
+    return $device && $device->{state} eq SIMULATOR_DEVICE_STATE_BOOTED;
+}
+
 sub runIOSWebKitAppInSimulator($;$)
 {
     my ($appBundle, $simulatorOptions) = @_;
     my $productDir = productDir();
-    my $appDisplayName = appDisplayNameFromBundle($appBundle);;
+    my $appDisplayName = appDisplayNameFromBundle($appBundle);
+    my $appIdentifier = appIdentifierFromBundle($appBundle);
     my $simulatedDevice = findOrCreateSimulatorForIOSDevice("For WebKit Development");
+    my $simulatedDeviceUDID = $simulatedDevice->{UDID};
 
-    my $bootedSimulatorDevice = bootedIOSSimulatorDevice();
-    if (!$bootedSimulatorDevice || $bootedSimulatorDevice->{UDID} ne $simulatedDevice->{UDID}) {
-        quitIOSSimulator();
-        openIOSSimulator($simulatedDevice);
+    my $willUseSystemInstalledApp = isIOSSimulatorSystemInstalledApp($appBundle);
+    if ($willUseSystemInstalledApp) {
+        if (hasUserInstalledAppInSimulatorDevice($appIdentifier, $simulatedDeviceUDID)) {
+            # Restore the system-installed app in the simulator device corresponding to $appBundle as it
+            # was previously overwritten with a custom built version of the app.
+            # FIXME: Only restore the system-installed version of the app instead of erasing all contents and settings.
+            print "Quitting iOS Simulator...\n";
+            quitIOSSimulator($simulatedDeviceUDID);
+            print "Erasing contents and settings for simulator device \"$simulatedDevice->{name}\".\n";
+            exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "erase", $simulatedDeviceUDID)) == 0 or die;
+        }
+        # FIXME: We assume that if $simulatedDeviceUDID is not booted then iOS Simulator is not open. However
+        #        $simulatedDeviceUDID may have been booted using the simctl command line tool. If $simulatedDeviceUDID
+        #        was booted using simctl then we should shutdown the device and launch iOS Simulator to boot it again.
+        if (!isSimulatorDeviceBooted($simulatedDeviceUDID)) {
+            print "Launching iOS Simulator...\n";
+            relaunchIOSSimulator($simulatedDevice);
+        }
+    } else {
+        # FIXME: We should killall(1) any running instances of $appBundle before installing it to ensure
+        #        that simctl launch opens the latest installed version of the app. For now we quit and
+        #        launch the iOS Simulator again to ensure there are no running instances of $appBundle.
+        print "Quitting and launching iOS Simulator...\n";
+        relaunchIOSSimulator($simulatedDevice);
+
+        print "Installing $appBundle.\n";
+        # Install custom built app, overwriting an app with the same app identifier if one exists.
+        exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "install", $simulatedDeviceUDID, $appBundle)) == 0 or die;
+
     }
 
     $simulatorOptions = {} unless $simulatorOptions;
@@ -2176,15 +2260,8 @@
         $ENV{"SIMCTL_CHILD_$key"} = $simulatorENV{$key};
     }
 
-    # FIXME: We should also erase the iOS Simulator device when alternating between running a
-    # custom-built MobileSafari and the system-installed MobileSafari.
-    if (!isIOSSimulatorSystemInstalledApp($appBundle)) {
-        eraseIOSSimulatorDevice($simulatedDevice->{UDID}) or die;
-    }
-
-    my $appIdentifier = appIdentifierFromBundle($appBundle);
     print "Starting $appDisplayName with DYLD_FRAMEWORK_PATH set to point to built WebKit in $productDir.\n";
-    return exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "launch", $simulatedDevice->{UDID}, $appIdentifier, @$applicationArguments));
+    return exitStatus(system("xcrun", "--sdk", "iphonesimulator", "simctl", "launch", $simulatedDeviceUDID, $appIdentifier, @$applicationArguments));
 }
 
 sub runIOSWebKitApp($)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to